mirror of
https://github.com/godotengine/godot.git
synced 2024-11-24 13:12:42 +00:00
OpenXR: Change timing of xrWaitFrame and add thread safety features to OpenXR
This commit is contained in:
parent
d282e4f0e6
commit
cbab7dc049
@ -1979,6 +1979,12 @@
|
|||||||
[b]Warning:[/b] This function is primarily intended for editor usage. For in-game use cases, prefer physics collision.
|
[b]Warning:[/b] This function is primarily intended for editor usage. For in-game use cases, prefer physics collision.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="is_on_render_thread">
|
||||||
|
<return type="bool" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if our code is currently executing on the rendering thread.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="light_directional_set_blend_splits">
|
<method name="light_directional_set_blend_splits">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="light" type="RID" />
|
<param index="0" name="light" type="RID" />
|
||||||
|
@ -37,8 +37,8 @@
|
|||||||
You should call this method after a few seconds have passed. For example, when the user requests a realignment of the display holding a designated button on a controller for a short period of time, or when implementing a teleport mechanism.
|
You should call this method after a few seconds have passed. For example, when the user requests a realignment of the display holding a designated button on a controller for a short period of time, or when implementing a teleport mechanism.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="clear_reference_frame" qualifiers="const">
|
<method name="clear_reference_frame">
|
||||||
<return type="Transform3D" />
|
<return type="void" />
|
||||||
<description>
|
<description>
|
||||||
Clears the reference frame that was set by previous calls to [method center_on_hmd].
|
Clears the reference frame that was set by previous calls to [method center_on_hmd].
|
||||||
</description>
|
</description>
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
<method name="get_next_frame_time">
|
<method name="get_next_frame_time">
|
||||||
<return type="int" />
|
<return type="int" />
|
||||||
<description>
|
<description>
|
||||||
Returns the timing for the next frame.
|
Returns the predicted display timing for the next frame.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_play_space">
|
<method name="get_play_space">
|
||||||
@ -63,6 +63,12 @@
|
|||||||
Returns the play space, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html]XrSpace[/url] cast to an integer.
|
Returns the play space, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html]XrSpace[/url] cast to an integer.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="get_predicted_display_time">
|
||||||
|
<return type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the predicted display timing for the current frame.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="get_session">
|
<method name="get_session">
|
||||||
<return type="int" />
|
<return type="int" />
|
||||||
<description>
|
<description>
|
||||||
|
@ -147,6 +147,11 @@
|
|||||||
</member>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
<signals>
|
<signals>
|
||||||
|
<signal name="instance_exiting">
|
||||||
|
<description>
|
||||||
|
Informs our OpenXR instance is exiting.
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
<signal name="pose_recentered">
|
<signal name="pose_recentered">
|
||||||
<description>
|
<description>
|
||||||
Informs the user queued a recenter of the player position.
|
Informs the user queued a recenter of the player position.
|
||||||
@ -169,6 +174,11 @@
|
|||||||
Informs our OpenXR session now has focus.
|
Informs our OpenXR session now has focus.
|
||||||
</description>
|
</description>
|
||||||
</signal>
|
</signal>
|
||||||
|
<signal name="session_loss_pending">
|
||||||
|
<description>
|
||||||
|
Informs our OpenXR session is in the process of being lost.
|
||||||
|
</description>
|
||||||
|
</signal>
|
||||||
<signal name="session_stopping">
|
<signal name="session_stopping">
|
||||||
<description>
|
<description>
|
||||||
Informs our OpenXR session is stopping.
|
Informs our OpenXR session is stopping.
|
||||||
|
@ -274,7 +274,7 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p
|
|||||||
if (swapchain_size == viewport_size && !p_static_image && !static_image) {
|
if (swapchain_size == viewport_size && !p_static_image && !static_image) {
|
||||||
// We're all good! Just acquire it.
|
// We're all good! Just acquire it.
|
||||||
// We can ignore should_render here, return will be false.
|
// We can ignore should_render here, return will be false.
|
||||||
XrBool32 should_render = true;
|
bool should_render = true;
|
||||||
return swapchain_info.acquire(should_render);
|
return swapchain_info.acquire(should_render);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p
|
|||||||
|
|
||||||
// Acquire our image so we can start rendering into it,
|
// Acquire our image so we can start rendering into it,
|
||||||
// we can ignore should_render here, ret will be false.
|
// we can ignore should_render here, ret will be false.
|
||||||
XrBool32 should_render = true;
|
bool should_render = true;
|
||||||
bool ret = swapchain_info.acquire(should_render);
|
bool ret = swapchain_info.acquire(should_render);
|
||||||
|
|
||||||
swapchain_size = viewport_size;
|
swapchain_size = viewport_size;
|
||||||
|
@ -128,7 +128,7 @@ void OpenXRHandTrackingExtension::on_process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process our hands
|
// process our hands
|
||||||
const XrTime time = OpenXRAPI::get_singleton()->get_next_frame_time(); // This data will be used for the next frame we render
|
const XrTime time = OpenXRAPI::get_singleton()->get_predicted_display_time();
|
||||||
if (time == 0) {
|
if (time == 0) {
|
||||||
// we don't have timing info yet, or we're skipping a frame...
|
// we don't have timing info yet, or we're skipping a frame...
|
||||||
return;
|
return;
|
||||||
|
@ -160,7 +160,7 @@ void OpenXRAPI::OpenXRSwapChainInfo::free() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::OpenXRSwapChainInfo::acquire(XrBool32 &p_should_render) {
|
bool OpenXRAPI::OpenXRSwapChainInfo::acquire(bool &p_should_render) {
|
||||||
ERR_FAIL_COND_V(image_acquired, true); // This was not released when it should be, error out and reuse...
|
ERR_FAIL_COND_V(image_acquired, true); // This was not released when it should be, error out and reuse...
|
||||||
|
|
||||||
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
||||||
@ -193,10 +193,18 @@ bool OpenXRAPI::OpenXRSwapChainInfo::acquire(XrBool32 &p_should_render) {
|
|||||||
XrSwapchainImageWaitInfo swapchain_image_wait_info = {
|
XrSwapchainImageWaitInfo swapchain_image_wait_info = {
|
||||||
XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, // type
|
XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, // type
|
||||||
nullptr, // next
|
nullptr, // next
|
||||||
17000000 // timeout in nanoseconds
|
1000000000 // 1s timeout in nanoseconds
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wait for a maximum of 10 seconds before calling it a critical failure...
|
||||||
|
for (int retry = 0; retry < 10; retry++) {
|
||||||
result = openxr_api->xrWaitSwapchainImage(swapchain, &swapchain_image_wait_info);
|
result = openxr_api->xrWaitSwapchainImage(swapchain, &swapchain_image_wait_info);
|
||||||
|
if (result != XR_TIMEOUT_EXPIRED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
WARN_PRINT("OpenXR: timed out waiting for swapchain image.");
|
||||||
|
}
|
||||||
|
|
||||||
if (!XR_UNQUALIFIED_SUCCESS(result)) {
|
if (!XR_UNQUALIFIED_SUCCESS(result)) {
|
||||||
// Make sure end_frame knows we need to submit an empty frame
|
// Make sure end_frame knows we need to submit an empty frame
|
||||||
p_should_render = false;
|
p_should_render = false;
|
||||||
@ -206,6 +214,8 @@ bool OpenXRAPI::OpenXRSwapChainInfo::acquire(XrBool32 &p_should_render) {
|
|||||||
print_line("OpenXR: failed to wait for swapchain image [", openxr_api->get_error_string(result), "]");
|
print_line("OpenXR: failed to wait for swapchain image [", openxr_api->get_error_string(result), "]");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
WARN_PRINT("OpenXR: couldn't to wait for swapchain but not a complete error [" + openxr_api->get_error_string(result) + "]");
|
||||||
|
|
||||||
// Make sure to skip trying to acquire the swapchain image in the next frame
|
// Make sure to skip trying to acquire the swapchain image in the next frame
|
||||||
skip_acquire_swapchain = true;
|
skip_acquire_swapchain = true;
|
||||||
return false;
|
return false;
|
||||||
@ -760,21 +770,6 @@ bool OpenXRAPI::load_supported_view_configuration_views(XrViewConfigurationType
|
|||||||
print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_views[i].recommendedSwapchainSampleCount));
|
print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_views[i].recommendedSwapchainSampleCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate buffers we'll be populating with view information.
|
|
||||||
views = (XrView *)memalloc(sizeof(XrView) * view_count);
|
|
||||||
ERR_FAIL_NULL_V_MSG(views, false, "OpenXR Couldn't allocate memory for views");
|
|
||||||
memset(views, 0, sizeof(XrView) * view_count);
|
|
||||||
|
|
||||||
projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * view_count);
|
|
||||||
ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views");
|
|
||||||
memset(projection_views, 0, sizeof(XrCompositionLayerProjectionView) * view_count);
|
|
||||||
|
|
||||||
if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
|
|
||||||
depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
|
|
||||||
ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views");
|
|
||||||
memset(depth_views, 0, sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,6 +922,9 @@ bool OpenXRAPI::setup_play_space() {
|
|||||||
|
|
||||||
// If we've previously created a play space, clean it up first.
|
// If we've previously created a play space, clean it up first.
|
||||||
if (play_space != XR_NULL_HANDLE) {
|
if (play_space != XR_NULL_HANDLE) {
|
||||||
|
// TODO Investigate if destroying our play space here is safe,
|
||||||
|
// it may still be used in the rendering thread.
|
||||||
|
|
||||||
xrDestroySpace(play_space);
|
xrDestroySpace(play_space);
|
||||||
}
|
}
|
||||||
play_space = new_play_space;
|
play_space = new_play_space;
|
||||||
@ -936,7 +934,11 @@ bool OpenXRAPI::setup_play_space() {
|
|||||||
if (emulating_local_floor) {
|
if (emulating_local_floor) {
|
||||||
// We'll use the STAGE space to get the floor height, but we can't do that until
|
// We'll use the STAGE space to get the floor height, but we can't do that until
|
||||||
// after xrWaitFrame(), so just set this flag for now.
|
// after xrWaitFrame(), so just set this flag for now.
|
||||||
|
// Render state will be updated then.
|
||||||
should_reset_emulated_floor_height = true;
|
should_reset_emulated_floor_height = true;
|
||||||
|
} else {
|
||||||
|
// Update render state so this play space is used rendering the upcoming frame.
|
||||||
|
set_render_play_space(play_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1016,7 +1018,7 @@ bool OpenXRAPI::reset_emulated_floor_height() {
|
|||||||
identityPose, // pose
|
identityPose, // pose
|
||||||
};
|
};
|
||||||
|
|
||||||
result = xrLocateSpace(stage_space, local_space, get_next_frame_time(), &stage_location);
|
result = xrLocateSpace(stage_space, local_space, get_predicted_display_time(), &stage_location);
|
||||||
|
|
||||||
xrDestroySpace(local_space);
|
xrDestroySpace(local_space);
|
||||||
xrDestroySpace(stage_space);
|
xrDestroySpace(stage_space);
|
||||||
@ -1042,6 +1044,9 @@ bool OpenXRAPI::reset_emulated_floor_height() {
|
|||||||
// report that as the reference space to the outside world.
|
// report that as the reference space to the outside world.
|
||||||
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
|
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
|
||||||
|
|
||||||
|
// Update render state so this play space is used rendering the upcoming frame.
|
||||||
|
set_render_play_space(play_space);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1136,6 +1141,7 @@ bool OpenXRAPI::obtain_swapchain_formats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
|
bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
|
||||||
|
ERR_NOT_ON_RENDER_THREAD_V(false);
|
||||||
ERR_FAIL_NULL_V(graphics_extension, false);
|
ERR_FAIL_NULL_V(graphics_extension, false);
|
||||||
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
|
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
|
||||||
|
|
||||||
@ -1154,12 +1160,12 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
|
|||||||
as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
|
as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
main_swapchain_size = p_size;
|
render_state.main_swapchain_size = p_size;
|
||||||
uint32_t sample_count = 1;
|
uint32_t sample_count = 1;
|
||||||
|
|
||||||
// We start with our color swapchain...
|
// We start with our color swapchain...
|
||||||
if (color_swapchain_format != 0) {
|
if (color_swapchain_format != 0) {
|
||||||
if (!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, main_swapchain_size.width, main_swapchain_size.height, sample_count, view_count)) {
|
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_count)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1169,7 +1175,7 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
|
|||||||
// - we support our depth layer extension
|
// - we support our depth layer extension
|
||||||
// - we have our spacewarp extension (not yet implemented)
|
// - we have our spacewarp extension (not yet implemented)
|
||||||
if (depth_swapchain_format != 0 && submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
|
if (depth_swapchain_format != 0 && submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
|
||||||
if (!main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, main_swapchain_size.width, main_swapchain_size.height, sample_count, view_count)) {
|
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_count)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1180,36 +1186,36 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
|
|||||||
// TBD
|
// TBD
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < view_count; i++) {
|
for (uint32_t i = 0; i < render_state.view_count; i++) {
|
||||||
views[i].type = XR_TYPE_VIEW;
|
render_state.views[i].type = XR_TYPE_VIEW;
|
||||||
views[i].next = nullptr;
|
render_state.views[i].next = nullptr;
|
||||||
|
|
||||||
projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
render_state.projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||||
projection_views[i].next = nullptr;
|
render_state.projection_views[i].next = nullptr;
|
||||||
projection_views[i].subImage.swapchain = main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
|
render_state.projection_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
|
||||||
projection_views[i].subImage.imageArrayIndex = i;
|
render_state.projection_views[i].subImage.imageArrayIndex = i;
|
||||||
projection_views[i].subImage.imageRect.offset.x = 0;
|
render_state.projection_views[i].subImage.imageRect.offset.x = 0;
|
||||||
projection_views[i].subImage.imageRect.offset.y = 0;
|
render_state.projection_views[i].subImage.imageRect.offset.y = 0;
|
||||||
projection_views[i].subImage.imageRect.extent.width = main_swapchain_size.width;
|
render_state.projection_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
|
||||||
projection_views[i].subImage.imageRect.extent.height = main_swapchain_size.height;
|
render_state.projection_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
|
||||||
|
|
||||||
if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) {
|
if (render_state.submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && render_state.depth_views) {
|
||||||
projection_views[i].next = &depth_views[i];
|
render_state.projection_views[i].next = &render_state.depth_views[i];
|
||||||
|
|
||||||
depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
|
render_state.depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
|
||||||
depth_views[i].next = nullptr;
|
render_state.depth_views[i].next = nullptr;
|
||||||
depth_views[i].subImage.swapchain = main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
|
render_state.depth_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
|
||||||
depth_views[i].subImage.imageArrayIndex = i;
|
render_state.depth_views[i].subImage.imageArrayIndex = i;
|
||||||
depth_views[i].subImage.imageRect.offset.x = 0;
|
render_state.depth_views[i].subImage.imageRect.offset.x = 0;
|
||||||
depth_views[i].subImage.imageRect.offset.y = 0;
|
render_state.depth_views[i].subImage.imageRect.offset.y = 0;
|
||||||
depth_views[i].subImage.imageRect.extent.width = main_swapchain_size.width;
|
render_state.depth_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
|
||||||
depth_views[i].subImage.imageRect.extent.height = main_swapchain_size.height;
|
render_state.depth_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
|
||||||
// OpenXR spec says that: minDepth < maxDepth.
|
// OpenXR spec says that: minDepth < maxDepth.
|
||||||
depth_views[i].minDepth = 0.0;
|
render_state.depth_views[i].minDepth = 0.0;
|
||||||
depth_views[i].maxDepth = 1.0;
|
render_state.depth_views[i].maxDepth = 1.0;
|
||||||
// But we can reverse near and far for reverse-Z.
|
// But we can reverse near and far for reverse-Z.
|
||||||
depth_views[i].nearZ = 100.0; // Near and far Z will be set to the correct values in fill_projection_matrix
|
render_state.depth_views[i].nearZ = 100.0; // Near and far Z will be set to the correct values in fill_projection_matrix
|
||||||
depth_views[i].farZ = 0.01;
|
render_state.depth_views[i].farZ = 0.01;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1217,23 +1223,33 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void OpenXRAPI::destroy_session() {
|
void OpenXRAPI::destroy_session() {
|
||||||
if (running && session != XR_NULL_HANDLE) {
|
// TODO need to figure out if we're still rendering our current frame
|
||||||
|
// in a separate rendering thread and if so,
|
||||||
|
// if we need to wait for completion.
|
||||||
|
// We could be pulling the rug from underneath rendering...
|
||||||
|
|
||||||
|
if (running) {
|
||||||
|
if (session != XR_NULL_HANDLE) {
|
||||||
xrEndSession(session);
|
xrEndSession(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (views != nullptr) {
|
running = false;
|
||||||
memfree(views);
|
render_state.running = false;
|
||||||
views = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projection_views != nullptr) {
|
if (render_state.views != nullptr) {
|
||||||
memfree(projection_views);
|
memfree(render_state.views);
|
||||||
projection_views = nullptr;
|
render_state.views = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth_views != nullptr) {
|
if (render_state.projection_views != nullptr) {
|
||||||
memfree(depth_views);
|
memfree(render_state.projection_views);
|
||||||
depth_views = nullptr;
|
render_state.projection_views = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (render_state.depth_views != nullptr) {
|
||||||
|
memfree(render_state.depth_views);
|
||||||
|
render_state.depth_views = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_main_swapchains();
|
free_main_swapchains();
|
||||||
@ -1248,6 +1264,7 @@ void OpenXRAPI::destroy_session() {
|
|||||||
if (play_space != XR_NULL_HANDLE) {
|
if (play_space != XR_NULL_HANDLE) {
|
||||||
xrDestroySpace(play_space);
|
xrDestroySpace(play_space);
|
||||||
play_space = XR_NULL_HANDLE;
|
play_space = XR_NULL_HANDLE;
|
||||||
|
render_state.play_space = XR_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
if (view_space != XR_NULL_HANDLE) {
|
if (view_space != XR_NULL_HANDLE) {
|
||||||
xrDestroySpace(view_space);
|
xrDestroySpace(view_space);
|
||||||
@ -1298,6 +1315,7 @@ bool OpenXRAPI::on_state_ready() {
|
|||||||
|
|
||||||
// we're running
|
// we're running
|
||||||
running = true;
|
running = true;
|
||||||
|
set_render_session_running(true);
|
||||||
|
|
||||||
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
||||||
wrapper->on_state_ready();
|
wrapper->on_state_ready();
|
||||||
@ -1374,34 +1392,37 @@ bool OpenXRAPI::on_state_stopping() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
running = false;
|
running = false;
|
||||||
|
set_render_session_running(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO further cleanup
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::on_state_loss_pending() {
|
bool OpenXRAPI::on_state_loss_pending() {
|
||||||
print_verbose("On state loss pending");
|
print_verbose("On state loss pending");
|
||||||
|
|
||||||
|
if (xr_interface) {
|
||||||
|
xr_interface->on_state_loss_pending();
|
||||||
|
}
|
||||||
|
|
||||||
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
||||||
wrapper->on_state_loss_pending();
|
wrapper->on_state_loss_pending();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO need to look into the correct action here, read up on the spec but we may need to signal Godot to exit (if it's not already exiting)
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::on_state_exiting() {
|
bool OpenXRAPI::on_state_exiting() {
|
||||||
print_verbose("On state existing");
|
print_verbose("On state existing");
|
||||||
|
|
||||||
|
if (xr_interface) {
|
||||||
|
xr_interface->on_state_exiting();
|
||||||
|
}
|
||||||
|
|
||||||
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
||||||
wrapper->on_state_exiting();
|
wrapper->on_state_exiting();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO need to look into the correct action here, read up on the spec but we may need to signal Godot to exit (if it's not already exiting)
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1419,10 +1440,7 @@ void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configurat
|
|||||||
|
|
||||||
bool OpenXRAPI::set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space) {
|
bool OpenXRAPI::set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space) {
|
||||||
requested_reference_space = p_requested_reference_space;
|
requested_reference_space = p_requested_reference_space;
|
||||||
|
play_space_is_dirty = true;
|
||||||
if (is_initialized()) {
|
|
||||||
return setup_play_space();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1625,11 +1643,6 @@ bool OpenXRAPI::initialize_session() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!setup_play_space()) {
|
|
||||||
destroy_session();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!setup_view_space()) {
|
if (!setup_view_space()) {
|
||||||
destroy_session();
|
destroy_session();
|
||||||
return false;
|
return false;
|
||||||
@ -1645,6 +1658,8 @@ bool OpenXRAPI::initialize_session() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allocate_view_buffers(view_count, submit_depth_buffer);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1696,12 +1711,18 @@ XrHandTrackerEXT OpenXRAPI::get_hand_tracker(int p_hand_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Size2 OpenXRAPI::get_recommended_target_size() {
|
Size2 OpenXRAPI::get_recommended_target_size() {
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
ERR_FAIL_NULL_V(view_configuration_views, Size2());
|
ERR_FAIL_NULL_V(view_configuration_views, Size2());
|
||||||
|
|
||||||
Size2 target_size;
|
Size2 target_size;
|
||||||
|
|
||||||
|
if (rendering_server && rendering_server->is_on_render_thread()) {
|
||||||
|
target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_state.render_target_size_multiplier;
|
||||||
|
target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_state.render_target_size_multiplier;
|
||||||
|
} else {
|
||||||
target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_target_size_multiplier;
|
target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_target_size_multiplier;
|
||||||
target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_target_size_multiplier;
|
target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_target_size_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
return target_size;
|
return target_size;
|
||||||
}
|
}
|
||||||
@ -1713,14 +1734,12 @@ XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform,
|
|||||||
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// xrWaitFrame not run yet
|
// Get display time
|
||||||
if (frame_state.predictedDisplayTime == 0) {
|
XrTime display_time = get_predicted_display_time();
|
||||||
|
if (display_time == 0) {
|
||||||
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get timing for the next frame, as that is the current frame we're processing
|
|
||||||
XrTime display_time = get_next_frame_time();
|
|
||||||
|
|
||||||
XrSpaceVelocity velocity = {
|
XrSpaceVelocity velocity = {
|
||||||
XR_TYPE_SPACE_VELOCITY, // type
|
XR_TYPE_SPACE_VELOCITY, // type
|
||||||
nullptr, // next
|
nullptr, // next
|
||||||
@ -1764,54 +1783,47 @@ XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
|
bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
|
||||||
if (!running) {
|
ERR_NOT_ON_RENDER_THREAD_V(false);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// xrWaitFrame not run yet
|
if (!render_state.running) {
|
||||||
if (frame_state.predictedDisplayTime == 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't have valid view info
|
// we don't have valid view info
|
||||||
if (views == nullptr || !view_pose_valid) {
|
if (render_state.views == nullptr || !render_state.view_pose_valid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note, the timing of this is set right before rendering, which is what we need here.
|
// Note, the timing of this is set right before rendering, which is what we need here.
|
||||||
r_transform = transform_from_pose(views[p_view].pose);
|
r_transform = transform_from_pose(render_state.views[p_view].pose);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
|
bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
|
||||||
|
ERR_NOT_ON_RENDER_THREAD_V(false);
|
||||||
ERR_FAIL_NULL_V(graphics_extension, false);
|
ERR_FAIL_NULL_V(graphics_extension, false);
|
||||||
|
|
||||||
if (!running) {
|
if (!render_state.running) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// xrWaitFrame not run yet
|
|
||||||
if (frame_state.predictedDisplayTime == 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't have valid view info
|
// we don't have valid view info
|
||||||
if (views == nullptr || !view_pose_valid) {
|
if (render_state.views == nullptr || !render_state.view_pose_valid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're using depth views, make sure we update our near and far there...
|
// if we're using depth views, make sure we update our near and far there...
|
||||||
if (depth_views != nullptr) {
|
if (render_state.depth_views != nullptr) {
|
||||||
for (uint32_t i = 0; i < view_count; i++) {
|
for (uint32_t i = 0; i < render_state.view_count; i++) {
|
||||||
// As we are using reverse-Z these need to be flipped.
|
// As we are using reverse-Z these need to be flipped.
|
||||||
depth_views[i].nearZ = p_z_far;
|
render_state.depth_views[i].nearZ = p_z_far;
|
||||||
depth_views[i].farZ = p_z_near;
|
render_state.depth_views[i].farZ = p_z_near;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now update our projection
|
// now update our projection
|
||||||
return graphics_extension->create_projection_fov(views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
|
return graphics_extension->create_projection_fov(render_state.views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::poll_events() {
|
bool OpenXRAPI::poll_events() {
|
||||||
@ -1934,6 +1946,69 @@ bool OpenXRAPI::poll_events() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenXRAPI::_allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
||||||
|
ERR_FAIL_NULL(openxr_api);
|
||||||
|
|
||||||
|
openxr_api->render_state.view_count = p_view_count;
|
||||||
|
openxr_api->render_state.submit_depth_buffer = p_submit_depth_buffer;
|
||||||
|
|
||||||
|
// Allocate buffers we'll be populating with view information.
|
||||||
|
openxr_api->render_state.views = (XrView *)memalloc(sizeof(XrView) * p_view_count);
|
||||||
|
ERR_FAIL_NULL_MSG(openxr_api->render_state.views, "OpenXR Couldn't allocate memory for views");
|
||||||
|
memset(openxr_api->render_state.views, 0, sizeof(XrView) * p_view_count);
|
||||||
|
|
||||||
|
openxr_api->render_state.projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * p_view_count);
|
||||||
|
ERR_FAIL_NULL_MSG(openxr_api->render_state.projection_views, "OpenXR Couldn't allocate memory for projection views");
|
||||||
|
memset(openxr_api->render_state.projection_views, 0, sizeof(XrCompositionLayerProjectionView) * p_view_count);
|
||||||
|
|
||||||
|
if (p_submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
|
||||||
|
openxr_api->render_state.depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * p_view_count);
|
||||||
|
ERR_FAIL_NULL_MSG(openxr_api->render_state.depth_views, "OpenXR Couldn't allocate memory for depth views");
|
||||||
|
memset(openxr_api->render_state.depth_views, 0, sizeof(XrCompositionLayerDepthInfoKHR) * p_view_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRAPI::_set_render_session_running(bool p_is_running) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
||||||
|
ERR_FAIL_NULL(openxr_api);
|
||||||
|
openxr_api->render_state.running = p_is_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRAPI::_set_render_display_info(XrTime p_predicted_display_time, bool p_should_render) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
||||||
|
ERR_FAIL_NULL(openxr_api);
|
||||||
|
openxr_api->render_state.predicted_display_time = p_predicted_display_time;
|
||||||
|
openxr_api->render_state.should_render = p_should_render;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRAPI::_set_render_play_space(uint64_t p_play_space) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
||||||
|
ERR_FAIL_NULL(openxr_api);
|
||||||
|
openxr_api->render_state.play_space = XrSpace(p_play_space);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRAPI::_set_render_state_multiplier(double p_render_target_size_multiplier) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
||||||
|
ERR_FAIL_NULL(openxr_api);
|
||||||
|
openxr_api->render_state.render_target_size_multiplier = p_render_target_size_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::process() {
|
bool OpenXRAPI::process() {
|
||||||
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
|
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
|
||||||
|
|
||||||
@ -1945,42 +2020,11 @@ bool OpenXRAPI::process() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
// We call xrWaitFrame as early as possible, this will allow OpenXR to get
|
||||||
wrapper->on_process();
|
// proper timing info between this point, and when we're ready to start rendering.
|
||||||
}
|
// As the name suggests, OpenXR can pause the thread to minimize the time between
|
||||||
|
// retrieving tracking data and using that tracking data to render.
|
||||||
return true;
|
// OpenXR thus works best if rendering is performed on a separate thread.
|
||||||
}
|
|
||||||
|
|
||||||
void OpenXRAPI::free_main_swapchains() {
|
|
||||||
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
|
||||||
main_swapchains[i].queue_free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenXRAPI::pre_render() {
|
|
||||||
ERR_FAIL_COND(instance == XR_NULL_HANDLE);
|
|
||||||
|
|
||||||
if (!running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process any swapchains that were queued to be freed
|
|
||||||
OpenXRSwapChainInfo::free_queued();
|
|
||||||
|
|
||||||
Size2i swapchain_size = get_recommended_target_size();
|
|
||||||
if (swapchain_size != main_swapchain_size) {
|
|
||||||
// Out with the old.
|
|
||||||
free_main_swapchains();
|
|
||||||
|
|
||||||
// In with the new.
|
|
||||||
create_main_swapchains(swapchain_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Waitframe does 2 important things in our process:
|
|
||||||
// 1) It provides us with predictive timing, telling us when OpenXR expects to display the frame we're about to commit
|
|
||||||
// 2) It will use the previous timing to pause our thread so that rendering starts as close to displaying as possible
|
|
||||||
// This must thus be called as close to when we start rendering as possible
|
|
||||||
XrFrameWaitInfo frame_wait_info = { XR_TYPE_FRAME_WAIT_INFO, nullptr };
|
XrFrameWaitInfo frame_wait_info = { XR_TYPE_FRAME_WAIT_INFO, nullptr };
|
||||||
frame_state.predictedDisplayTime = 0;
|
frame_state.predictedDisplayTime = 0;
|
||||||
frame_state.predictedDisplayPeriod = 0;
|
frame_state.predictedDisplayPeriod = 0;
|
||||||
@ -1995,7 +2039,9 @@ void OpenXRAPI::pre_render() {
|
|||||||
frame_state.predictedDisplayPeriod = 0;
|
frame_state.predictedDisplayPeriod = 0;
|
||||||
frame_state.shouldRender = false;
|
frame_state.shouldRender = false;
|
||||||
|
|
||||||
return;
|
set_render_display_info(0, false);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_state.predictedDisplayPeriod > 500000000) {
|
if (frame_state.predictedDisplayPeriod > 500000000) {
|
||||||
@ -2004,11 +2050,53 @@ void OpenXRAPI::pre_render() {
|
|||||||
frame_state.predictedDisplayPeriod = 0;
|
frame_state.predictedDisplayPeriod = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_render_display_info(frame_state.predictedDisplayTime, frame_state.shouldRender);
|
||||||
|
|
||||||
|
if (unlikely(play_space_is_dirty)) {
|
||||||
|
setup_play_space();
|
||||||
|
play_space_is_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (unlikely(should_reset_emulated_floor_height)) {
|
if (unlikely(should_reset_emulated_floor_height)) {
|
||||||
reset_emulated_floor_height();
|
reset_emulated_floor_height();
|
||||||
should_reset_emulated_floor_height = false;
|
should_reset_emulated_floor_height = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
||||||
|
wrapper->on_process();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRAPI::free_main_swapchains() {
|
||||||
|
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
||||||
|
render_state.main_swapchains[i].queue_free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRAPI::pre_render() {
|
||||||
|
ERR_FAIL_COND(session == XR_NULL_HANDLE);
|
||||||
|
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
if (!render_state.running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process any swapchains that were queued to be freed
|
||||||
|
OpenXRSwapChainInfo::free_queued();
|
||||||
|
|
||||||
|
Size2i swapchain_size = get_recommended_target_size();
|
||||||
|
if (swapchain_size != render_state.main_swapchain_size) {
|
||||||
|
// Out with the old.
|
||||||
|
free_main_swapchains();
|
||||||
|
|
||||||
|
// In with the new.
|
||||||
|
create_main_swapchains(swapchain_size);
|
||||||
|
}
|
||||||
|
|
||||||
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
||||||
wrapper->on_pre_render();
|
wrapper->on_pre_render();
|
||||||
}
|
}
|
||||||
@ -2028,8 +2116,8 @@ void OpenXRAPI::pre_render() {
|
|||||||
XR_TYPE_VIEW_LOCATE_INFO, // type
|
XR_TYPE_VIEW_LOCATE_INFO, // type
|
||||||
nullptr, // next
|
nullptr, // next
|
||||||
view_configuration, // viewConfigurationType
|
view_configuration, // viewConfigurationType
|
||||||
frame_state.predictedDisplayTime, // displayTime
|
render_state.predicted_display_time, // displayTime
|
||||||
play_space // space
|
render_state.play_space // space
|
||||||
};
|
};
|
||||||
XrViewState view_state = {
|
XrViewState view_state = {
|
||||||
XR_TYPE_VIEW_STATE, // type
|
XR_TYPE_VIEW_STATE, // type
|
||||||
@ -2037,7 +2125,7 @@ void OpenXRAPI::pre_render() {
|
|||||||
0 // viewStateFlags
|
0 // viewStateFlags
|
||||||
};
|
};
|
||||||
uint32_t view_count_output;
|
uint32_t view_count_output;
|
||||||
result = xrLocateViews(session, &view_locate_info, &view_state, view_count, &view_count_output, views);
|
XrResult result = xrLocateViews(session, &view_locate_info, &view_state, render_state.view_count, &view_count_output, render_state.views);
|
||||||
if (XR_FAILED(result)) {
|
if (XR_FAILED(result)) {
|
||||||
print_line("OpenXR: Couldn't locate views [", get_error_string(result), "]");
|
print_line("OpenXR: Couldn't locate views [", get_error_string(result), "]");
|
||||||
return;
|
return;
|
||||||
@ -2050,9 +2138,9 @@ void OpenXRAPI::pre_render() {
|
|||||||
pose_valid = false;
|
pose_valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (view_pose_valid != pose_valid) {
|
if (render_state.view_pose_valid != pose_valid) {
|
||||||
view_pose_valid = pose_valid;
|
render_state.view_pose_valid = pose_valid;
|
||||||
if (!view_pose_valid) {
|
if (!render_state.view_pose_valid) {
|
||||||
print_verbose("OpenXR View pose became invalid");
|
print_verbose("OpenXR View pose became invalid");
|
||||||
} else {
|
} else {
|
||||||
print_verbose("OpenXR View pose became valid");
|
print_verbose("OpenXR View pose became valid");
|
||||||
@ -2071,23 +2159,24 @@ void OpenXRAPI::pre_render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset this, we haven't found a viewport for output yet
|
// Reset this, we haven't found a viewport for output yet
|
||||||
has_xr_viewport = false;
|
render_state.has_xr_viewport = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
|
bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
|
||||||
// We found an XR viewport!
|
// Must be called from rendering thread!
|
||||||
has_xr_viewport = true;
|
ERR_NOT_ON_RENDER_THREAD_V(false);
|
||||||
|
|
||||||
if (!can_render()) {
|
// We found an XR viewport!
|
||||||
|
render_state.has_xr_viewport = true;
|
||||||
|
|
||||||
|
if (instance == XR_NULL_HANDLE || session == XR_NULL_HANDLE || !render_state.running || !render_state.view_pose_valid || !render_state.should_render) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: at some point in time we may support multiple viewports in which case we need to handle that...
|
|
||||||
|
|
||||||
// Acquire our images
|
// Acquire our images
|
||||||
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
||||||
if (!main_swapchains[i].is_image_acquired() && main_swapchains[i].get_swapchain() != XR_NULL_HANDLE) {
|
if (!render_state.main_swapchains[i].is_image_acquired() && render_state.main_swapchains[i].get_swapchain() != XR_NULL_HANDLE) {
|
||||||
if (!main_swapchains[i].acquire(frame_state.shouldRender)) {
|
if (!render_state.main_swapchains[i].acquire(render_state.should_render)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2101,24 +2190,33 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
XrSwapchain OpenXRAPI::get_color_swapchain() {
|
XrSwapchain OpenXRAPI::get_color_swapchain() {
|
||||||
return main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
|
ERR_NOT_ON_RENDER_THREAD_V(XR_NULL_HANDLE);
|
||||||
|
|
||||||
|
return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
|
||||||
}
|
}
|
||||||
|
|
||||||
RID OpenXRAPI::get_color_texture() {
|
RID OpenXRAPI::get_color_texture() {
|
||||||
return main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_image();
|
ERR_NOT_ON_RENDER_THREAD_V(RID());
|
||||||
|
|
||||||
|
return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_image();
|
||||||
}
|
}
|
||||||
|
|
||||||
RID OpenXRAPI::get_depth_texture() {
|
RID OpenXRAPI::get_depth_texture() {
|
||||||
|
ERR_NOT_ON_RENDER_THREAD_V(RID());
|
||||||
|
|
||||||
// Note, image will not be acquired if we didn't have a suitable swap chain format.
|
// Note, image will not be acquired if we didn't have a suitable swap chain format.
|
||||||
if (submit_depth_buffer) {
|
if (render_state.submit_depth_buffer && render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].is_image_acquired()) {
|
||||||
return main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_image();
|
return render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_image();
|
||||||
} else {
|
} else {
|
||||||
return RID();
|
return RID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenXRAPI::post_draw_viewport(RID p_render_target) {
|
void OpenXRAPI::post_draw_viewport(RID p_render_target) {
|
||||||
if (!can_render()) {
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
if (instance == XR_NULL_HANDLE || session == XR_NULL_HANDLE || !render_state.running || !render_state.view_pose_valid || !render_state.should_render) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2130,30 +2228,33 @@ void OpenXRAPI::post_draw_viewport(RID p_render_target) {
|
|||||||
void OpenXRAPI::end_frame() {
|
void OpenXRAPI::end_frame() {
|
||||||
XrResult result;
|
XrResult result;
|
||||||
|
|
||||||
ERR_FAIL_COND(instance == XR_NULL_HANDLE);
|
ERR_FAIL_COND(session == XR_NULL_HANDLE);
|
||||||
|
|
||||||
if (!running) {
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
if (!render_state.running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_state.shouldRender && view_pose_valid) {
|
if (render_state.should_render && render_state.view_pose_valid) {
|
||||||
if (!has_xr_viewport) {
|
if (!render_state.has_xr_viewport) {
|
||||||
print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
|
print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
|
||||||
} else if (!main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
|
} else if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
|
||||||
print_line("OpenXR: No swapchain could be acquired to render to!");
|
print_line("OpenXR: No swapchain could be acquired to render to!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// must have:
|
// must have:
|
||||||
// - shouldRender set to true
|
// - should_render set to true
|
||||||
// - a valid view pose for projection_views[eye].pose to submit layer
|
// - a valid view pose for projection_views[eye].pose to submit layer
|
||||||
// - an image to render
|
// - an image to render
|
||||||
if (!frame_state.shouldRender || !view_pose_valid || !main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
|
if (!render_state.should_render || !render_state.view_pose_valid || !render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
|
||||||
// submit 0 layers when we shouldn't render
|
// submit 0 layers when we shouldn't render
|
||||||
XrFrameEndInfo frame_end_info = {
|
XrFrameEndInfo frame_end_info = {
|
||||||
XR_TYPE_FRAME_END_INFO, // type
|
XR_TYPE_FRAME_END_INFO, // type
|
||||||
nullptr, // next
|
nullptr, // next
|
||||||
frame_state.predictedDisplayTime, // displayTime
|
render_state.predicted_display_time, // displayTime
|
||||||
environment_blend_mode, // environmentBlendMode
|
environment_blend_mode, // environmentBlendMode
|
||||||
0, // layerCount
|
0, // layerCount
|
||||||
nullptr // layers
|
nullptr // layers
|
||||||
@ -2170,14 +2271,14 @@ void OpenXRAPI::end_frame() {
|
|||||||
|
|
||||||
// release our swapchain image if we acquired it
|
// release our swapchain image if we acquired it
|
||||||
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
||||||
if (main_swapchains[i].is_image_acquired()) {
|
if (render_state.main_swapchains[i].is_image_acquired()) {
|
||||||
main_swapchains[i].release();
|
render_state.main_swapchains[i].release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t eye = 0; eye < view_count; eye++) {
|
for (uint32_t eye = 0; eye < render_state.view_count; eye++) {
|
||||||
projection_views[eye].fov = views[eye].fov;
|
render_state.projection_views[eye].fov = render_state.views[eye].fov;
|
||||||
projection_views[eye].pose = views[eye].pose;
|
render_state.projection_views[eye].pose = render_state.views[eye].pose;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<OrderedCompositionLayer> ordered_layers_list;
|
Vector<OrderedCompositionLayer> ordered_layers_list;
|
||||||
@ -2210,9 +2311,9 @@ void OpenXRAPI::end_frame() {
|
|||||||
XR_TYPE_COMPOSITION_LAYER_PROJECTION, // type
|
XR_TYPE_COMPOSITION_LAYER_PROJECTION, // type
|
||||||
nullptr, // next
|
nullptr, // next
|
||||||
layer_flags, // layerFlags
|
layer_flags, // layerFlags
|
||||||
play_space, // space
|
render_state.play_space, // space
|
||||||
view_count, // viewCount
|
render_state.view_count, // viewCount
|
||||||
projection_views, // views
|
render_state.projection_views, // views
|
||||||
};
|
};
|
||||||
ordered_layers_list.push_back({ (const XrCompositionLayerBaseHeader *)&projection_layer, 0 });
|
ordered_layers_list.push_back({ (const XrCompositionLayerBaseHeader *)&projection_layer, 0 });
|
||||||
|
|
||||||
@ -2228,7 +2329,7 @@ void OpenXRAPI::end_frame() {
|
|||||||
XrFrameEndInfo frame_end_info = {
|
XrFrameEndInfo frame_end_info = {
|
||||||
XR_TYPE_FRAME_END_INFO, // type
|
XR_TYPE_FRAME_END_INFO, // type
|
||||||
nullptr, // next
|
nullptr, // next
|
||||||
frame_state.predictedDisplayTime, // displayTime
|
render_state.predicted_display_time, // displayTime
|
||||||
environment_blend_mode, // environmentBlendMode
|
environment_blend_mode, // environmentBlendMode
|
||||||
static_cast<uint32_t>(layers_list.size()), // layerCount
|
static_cast<uint32_t>(layers_list.size()), // layerCount
|
||||||
layers_list.ptr() // layers
|
layers_list.ptr() // layers
|
||||||
@ -2271,6 +2372,7 @@ double OpenXRAPI::get_render_target_size_multiplier() const {
|
|||||||
|
|
||||||
void OpenXRAPI::set_render_target_size_multiplier(double multiplier) {
|
void OpenXRAPI::set_render_target_size_multiplier(double multiplier) {
|
||||||
render_target_size_multiplier = multiplier;
|
render_target_size_multiplier = multiplier;
|
||||||
|
set_render_state_multiplier(multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRAPI::is_foveation_supported() const {
|
bool OpenXRAPI::is_foveation_supported() const {
|
||||||
@ -2414,10 +2516,6 @@ OpenXRAPI::OpenXRAPI() {
|
|||||||
|
|
||||||
submit_depth_buffer = GLOBAL_GET("xr/openxr/submit_depth_buffer");
|
submit_depth_buffer = GLOBAL_GET("xr/openxr/submit_depth_buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset a few things that can't be done in our class definition.
|
|
||||||
frame_state.predictedDisplayTime = 0;
|
|
||||||
frame_state.predictedDisplayPeriod = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenXRAPI::~OpenXRAPI() {
|
OpenXRAPI::~OpenXRAPI() {
|
||||||
@ -3132,7 +3230,7 @@ XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracke
|
|||||||
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
XrTime display_time = get_next_frame_time();
|
XrTime display_time = get_predicted_display_time();
|
||||||
if (display_time == 0) {
|
if (display_time == 0) {
|
||||||
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,11 @@
|
|||||||
#include "core/templates/rb_map.h"
|
#include "core/templates/rb_map.h"
|
||||||
#include "core/templates/rid_owner.h"
|
#include "core/templates/rid_owner.h"
|
||||||
#include "core/templates/vector.h"
|
#include "core/templates/vector.h"
|
||||||
|
#include "servers/rendering_server.h"
|
||||||
#include "servers/xr/xr_pose.h"
|
#include "servers/xr/xr_pose.h"
|
||||||
|
|
||||||
#include <openxr/openxr.h>
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
// Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initializing structs which ensures zeroing out unspecified members.
|
|
||||||
// Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set.
|
|
||||||
|
|
||||||
// forward declarations, we don't want to include these fully
|
// forward declarations, we don't want to include these fully
|
||||||
class OpenXRInterface;
|
class OpenXRInterface;
|
||||||
|
|
||||||
@ -77,7 +75,7 @@ public:
|
|||||||
static void free_queued();
|
static void free_queued();
|
||||||
void free();
|
void free();
|
||||||
|
|
||||||
bool acquire(XrBool32 &p_should_render);
|
bool acquire(bool &p_should_render);
|
||||||
bool release();
|
bool release();
|
||||||
RID get_image();
|
RID get_image();
|
||||||
};
|
};
|
||||||
@ -151,9 +149,6 @@ private:
|
|||||||
|
|
||||||
uint32_t view_count = 0;
|
uint32_t view_count = 0;
|
||||||
XrViewConfigurationView *view_configuration_views = nullptr;
|
XrViewConfigurationView *view_configuration_views = nullptr;
|
||||||
XrView *views = nullptr;
|
|
||||||
XrCompositionLayerProjectionView *projection_views = nullptr;
|
|
||||||
XrCompositionLayerDepthInfoKHR *depth_views = nullptr; // Only used by Composition Layer Depth Extension if available
|
|
||||||
|
|
||||||
enum OpenXRSwapChainTypes {
|
enum OpenXRSwapChainTypes {
|
||||||
OPENXR_SWAPCHAIN_COLOR,
|
OPENXR_SWAPCHAIN_COLOR,
|
||||||
@ -164,14 +159,11 @@ private:
|
|||||||
|
|
||||||
int64_t color_swapchain_format = 0;
|
int64_t color_swapchain_format = 0;
|
||||||
int64_t depth_swapchain_format = 0;
|
int64_t depth_swapchain_format = 0;
|
||||||
Size2i main_swapchain_size = { 0, 0 };
|
|
||||||
OpenXRSwapChainInfo main_swapchains[OPENXR_SWAPCHAIN_MAX];
|
|
||||||
|
|
||||||
|
bool play_space_is_dirty = true;
|
||||||
XrSpace play_space = XR_NULL_HANDLE;
|
XrSpace play_space = XR_NULL_HANDLE;
|
||||||
XrSpace view_space = XR_NULL_HANDLE;
|
XrSpace view_space = XR_NULL_HANDLE;
|
||||||
bool view_pose_valid = false;
|
|
||||||
XRPose::TrackingConfidence head_pose_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
XRPose::TrackingConfidence head_pose_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
|
||||||
bool has_xr_viewport = false;
|
|
||||||
|
|
||||||
bool emulating_local_floor = false;
|
bool emulating_local_floor = false;
|
||||||
bool should_reset_emulated_floor_height = false;
|
bool should_reset_emulated_floor_height = false;
|
||||||
@ -328,6 +320,72 @@ private:
|
|||||||
// convenience
|
// convenience
|
||||||
void copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len);
|
void copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len);
|
||||||
|
|
||||||
|
// Render state, Only accessible in rendering thread
|
||||||
|
struct RenderState {
|
||||||
|
bool running = false;
|
||||||
|
bool should_render = false;
|
||||||
|
bool has_xr_viewport = false;
|
||||||
|
XrTime predicted_display_time = 0;
|
||||||
|
XrSpace play_space = XR_NULL_HANDLE;
|
||||||
|
double render_target_size_multiplier = 1.0;
|
||||||
|
|
||||||
|
uint32_t view_count = 0;
|
||||||
|
XrView *views = nullptr;
|
||||||
|
XrCompositionLayerProjectionView *projection_views = nullptr;
|
||||||
|
XrCompositionLayerDepthInfoKHR *depth_views = nullptr; // Only used by Composition Layer Depth Extension if available
|
||||||
|
bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled.
|
||||||
|
bool view_pose_valid = false;
|
||||||
|
|
||||||
|
Size2i main_swapchain_size;
|
||||||
|
OpenXRSwapChainInfo main_swapchains[OPENXR_SWAPCHAIN_MAX];
|
||||||
|
} render_state;
|
||||||
|
|
||||||
|
static void _allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer);
|
||||||
|
static void _set_render_session_running(bool p_is_running);
|
||||||
|
static void _set_render_display_info(XrTime p_predicted_display_time, bool p_should_render);
|
||||||
|
static void _set_render_play_space(uint64_t p_play_space);
|
||||||
|
static void _set_render_state_multiplier(double p_render_target_size_multiplier);
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_allocate_view_buffers).bind(p_view_count, p_submit_depth_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_render_session_running(bool p_is_running) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_session_running).bind(p_is_running));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_render_display_info(XrTime p_predicted_display_time, bool p_should_render) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_display_info).bind(p_predicted_display_time, p_should_render));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_render_play_space(XrSpace p_play_space) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_play_space).bind(uint64_t(p_play_space)));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_render_state_multiplier(double p_render_target_size_multiplier) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_state_multiplier).bind(p_render_target_size_multiplier));
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
XrInstance get_instance() const { return instance; };
|
XrInstance get_instance() const { return instance; };
|
||||||
XrSystemId get_system_id() const { return system_id; };
|
XrSystemId get_system_id() const { return system_id; };
|
||||||
@ -384,9 +442,13 @@ public:
|
|||||||
bool initialize_session();
|
bool initialize_session();
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
XrSpace get_play_space() const { return play_space; }
|
_FORCE_INLINE_ XrSpace get_play_space() const { return play_space; }
|
||||||
XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; }
|
_FORCE_INLINE_ XrTime get_predicted_display_time() { return frame_state.predictedDisplayTime; }
|
||||||
bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; }
|
_FORCE_INLINE_ XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; }
|
||||||
|
_FORCE_INLINE_ bool can_render() {
|
||||||
|
ERR_ON_RENDER_THREAD_V(false);
|
||||||
|
return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && frame_state.shouldRender;
|
||||||
|
}
|
||||||
|
|
||||||
XrHandTrackerEXT get_hand_tracker(int p_hand_index);
|
XrHandTrackerEXT get_hand_tracker(int p_hand_index);
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ void OpenXRAPIExtension::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running);
|
ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_play_space"), &OpenXRAPIExtension::get_play_space);
|
ClassDB::bind_method(D_METHOD("get_play_space"), &OpenXRAPIExtension::get_play_space);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_predicted_display_time"), &OpenXRAPIExtension::get_predicted_display_time);
|
||||||
ClassDB::bind_method(D_METHOD("get_next_frame_time"), &OpenXRAPIExtension::get_next_frame_time);
|
ClassDB::bind_method(D_METHOD("get_next_frame_time"), &OpenXRAPIExtension::get_next_frame_time);
|
||||||
ClassDB::bind_method(D_METHOD("can_render"), &OpenXRAPIExtension::can_render);
|
ClassDB::bind_method(D_METHOD("can_render"), &OpenXRAPIExtension::can_render);
|
||||||
|
|
||||||
@ -130,8 +131,17 @@ uint64_t OpenXRAPIExtension::get_play_space() {
|
|||||||
return (uint64_t)OpenXRAPI::get_singleton()->get_play_space();
|
return (uint64_t)OpenXRAPI::get_singleton()->get_play_space();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t OpenXRAPIExtension::get_predicted_display_time() {
|
||||||
|
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
|
||||||
|
return (XrTime)OpenXRAPI::get_singleton()->get_predicted_display_time();
|
||||||
|
}
|
||||||
|
|
||||||
int64_t OpenXRAPIExtension::get_next_frame_time() {
|
int64_t OpenXRAPIExtension::get_next_frame_time() {
|
||||||
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
|
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
|
||||||
|
|
||||||
|
// In the past we needed to look a frame ahead, may be calling this unintentionally so lets warn the dev.
|
||||||
|
WARN_PRINT_ONCE("OpenXR: Next frame timing called, verify this is intended.");
|
||||||
|
|
||||||
return (XrTime)OpenXRAPI::get_singleton()->get_next_frame_time();
|
return (XrTime)OpenXRAPI::get_singleton()->get_next_frame_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ public:
|
|||||||
bool is_running();
|
bool is_running();
|
||||||
|
|
||||||
uint64_t get_play_space();
|
uint64_t get_play_space();
|
||||||
|
int64_t get_predicted_display_time();
|
||||||
int64_t get_next_frame_time();
|
int64_t get_next_frame_time();
|
||||||
bool can_render();
|
bool can_render();
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ void OpenXRInterface::_bind_methods() {
|
|||||||
ADD_SIGNAL(MethodInfo("session_stopping"));
|
ADD_SIGNAL(MethodInfo("session_stopping"));
|
||||||
ADD_SIGNAL(MethodInfo("session_focussed"));
|
ADD_SIGNAL(MethodInfo("session_focussed"));
|
||||||
ADD_SIGNAL(MethodInfo("session_visible"));
|
ADD_SIGNAL(MethodInfo("session_visible"));
|
||||||
|
ADD_SIGNAL(MethodInfo("session_loss_pending"));
|
||||||
|
ADD_SIGNAL(MethodInfo("instance_exiting"));
|
||||||
ADD_SIGNAL(MethodInfo("pose_recentered"));
|
ADD_SIGNAL(MethodInfo("pose_recentered"));
|
||||||
ADD_SIGNAL(MethodInfo("refresh_rate_changed", PropertyInfo(Variant::FLOAT, "refresh_rate")));
|
ADD_SIGNAL(MethodInfo("refresh_rate_changed", PropertyInfo(Variant::FLOAT, "refresh_rate")));
|
||||||
|
|
||||||
@ -1258,6 +1260,14 @@ void OpenXRInterface::on_state_stopping() {
|
|||||||
emit_signal(SNAME("session_stopping"));
|
emit_signal(SNAME("session_stopping"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenXRInterface::on_state_loss_pending() {
|
||||||
|
emit_signal(SNAME("session_loss_pending"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRInterface::on_state_exiting() {
|
||||||
|
emit_signal(SNAME("instance_exiting"));
|
||||||
|
}
|
||||||
|
|
||||||
void OpenXRInterface::on_pose_recentered() {
|
void OpenXRInterface::on_pose_recentered() {
|
||||||
emit_signal(SNAME("pose_recentered"));
|
emit_signal(SNAME("pose_recentered"));
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,29 @@
|
|||||||
#ifndef OPENXR_INTERFACE_H
|
#ifndef OPENXR_INTERFACE_H
|
||||||
#define OPENXR_INTERFACE_H
|
#define OPENXR_INTERFACE_H
|
||||||
|
|
||||||
|
// A note on multithreading and thread safety in OpenXR.
|
||||||
|
//
|
||||||
|
// Most entry points will be called from the main thread in Godot
|
||||||
|
// however a number of entry points will be called from the
|
||||||
|
// rendering thread, potentially while we're already processing
|
||||||
|
// the next frame on the main thread.
|
||||||
|
//
|
||||||
|
// OpenXR itself has been designed with threading in mind including
|
||||||
|
// a high likelihood that the XR runtime runs in separate threads
|
||||||
|
// as well.
|
||||||
|
// Hence all the frame timing information, use of swapchains and
|
||||||
|
// sync functions.
|
||||||
|
// Do note that repeated calls to tracking APIs will provide
|
||||||
|
// increasingly more accurate data for the same timestamp as
|
||||||
|
// tracking data is continuously updated.
|
||||||
|
//
|
||||||
|
// For our code we mostly implement this in our OpenXRAPI class.
|
||||||
|
// We store data accessed from the rendering thread in a separate
|
||||||
|
// struct, setting values through our renderer command queue.
|
||||||
|
//
|
||||||
|
// As some data is setup before we start rendering, and cleaned up
|
||||||
|
// after we've stopped, that is accessed directly from both threads.
|
||||||
|
|
||||||
#include "action_map/openxr_action_map.h"
|
#include "action_map/openxr_action_map.h"
|
||||||
#include "extensions/openxr_hand_tracking_extension.h"
|
#include "extensions/openxr_hand_tracking_extension.h"
|
||||||
#include "openxr_api.h"
|
#include "openxr_api.h"
|
||||||
@ -173,6 +196,8 @@ public:
|
|||||||
void on_state_visible();
|
void on_state_visible();
|
||||||
void on_state_focused();
|
void on_state_focused();
|
||||||
void on_state_stopping();
|
void on_state_stopping();
|
||||||
|
void on_state_loss_pending();
|
||||||
|
void on_state_exiting();
|
||||||
void on_pose_recentered();
|
void on_pose_recentered();
|
||||||
void on_refresh_rate_changes(float p_new_rate);
|
void on_refresh_rate_changes(float p_new_rate);
|
||||||
void tracker_profile_changed(RID p_tracker, RID p_interaction_profile);
|
void tracker_profile_changed(RID p_tracker, RID p_interaction_profile);
|
||||||
|
@ -688,6 +688,18 @@ void Viewport::_process_picking() {
|
|||||||
physics_picking_events.clear();
|
physics_picking_events.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifndef _3D_DISABLED
|
||||||
|
if (use_xr) {
|
||||||
|
if (XRServer::get_singleton() != nullptr) {
|
||||||
|
Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface();
|
||||||
|
if (xr_interface.is_valid() && xr_interface->is_initialized() && xr_interface->get_view_count() > 1) {
|
||||||
|
WARN_PRINT_ONCE("Object picking can't be used when stereo rendering, this will be turned off!");
|
||||||
|
physics_object_picking = false; // don't try again.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
_drop_physics_mouseover(true);
|
_drop_physics_mouseover(true);
|
||||||
|
|
||||||
|
@ -1048,6 +1048,10 @@ public:
|
|||||||
virtual void init() override;
|
virtual void init() override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
|
|
||||||
|
virtual bool is_on_render_thread() override {
|
||||||
|
return Thread::get_caller_id() == server_thread;
|
||||||
|
}
|
||||||
|
|
||||||
virtual void call_on_render_thread(const Callable &p_callable) override {
|
virtual void call_on_render_thread(const Callable &p_callable) override {
|
||||||
if (Thread::get_caller_id() == server_thread) {
|
if (Thread::get_caller_id() == server_thread) {
|
||||||
command_queue.flush_if_pending();
|
command_queue.flush_if_pending();
|
||||||
|
@ -3426,6 +3426,7 @@ void RenderingServer::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("get_rendering_device"), &RenderingServer::get_rendering_device);
|
ClassDB::bind_method(D_METHOD("get_rendering_device"), &RenderingServer::get_rendering_device);
|
||||||
ClassDB::bind_method(D_METHOD("create_local_rendering_device"), &RenderingServer::create_local_rendering_device);
|
ClassDB::bind_method(D_METHOD("create_local_rendering_device"), &RenderingServer::create_local_rendering_device);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("is_on_render_thread"), &RenderingServer::is_on_render_thread);
|
||||||
ClassDB::bind_method(D_METHOD("call_on_render_thread", "callable"), &RenderingServer::call_on_render_thread);
|
ClassDB::bind_method(D_METHOD("call_on_render_thread", "callable"), &RenderingServer::call_on_render_thread);
|
||||||
|
|
||||||
#ifndef DISABLE_DEPRECATED
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
@ -41,6 +41,32 @@
|
|||||||
#include "servers/display_server.h"
|
#include "servers/display_server.h"
|
||||||
#include "servers/rendering/rendering_device.h"
|
#include "servers/rendering/rendering_device.h"
|
||||||
|
|
||||||
|
// Helper macros for code outside of the rendering server, but that is
|
||||||
|
// called by the rendering server.
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
#define ERR_ON_RENDER_THREAD \
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton(); \
|
||||||
|
ERR_FAIL_NULL(rendering_server); \
|
||||||
|
ERR_FAIL_COND(rendering_server->is_on_render_thread());
|
||||||
|
#define ERR_ON_RENDER_THREAD_V(m_ret) \
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton(); \
|
||||||
|
ERR_FAIL_NULL_V(rendering_server, m_ret); \
|
||||||
|
ERR_FAIL_COND_V(rendering_server->is_on_render_thread(), m_ret);
|
||||||
|
#define ERR_NOT_ON_RENDER_THREAD \
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton(); \
|
||||||
|
ERR_FAIL_NULL(rendering_server); \
|
||||||
|
ERR_FAIL_COND(!rendering_server->is_on_render_thread());
|
||||||
|
#define ERR_NOT_ON_RENDER_THREAD_V(m_ret) \
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton(); \
|
||||||
|
ERR_FAIL_NULL_V(rendering_server, m_ret); \
|
||||||
|
ERR_FAIL_COND_V(!rendering_server->is_on_render_thread(), m_ret);
|
||||||
|
#else
|
||||||
|
#define ERR_ON_RENDER_THREAD
|
||||||
|
#define ERR_ON_RENDER_THREAD_V(m_ret)
|
||||||
|
#define ERR_NOT_ON_RENDER_THREAD
|
||||||
|
#define ERR_NOT_ON_RENDER_THREAD_V(m_ret)
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class TypedArray;
|
class TypedArray;
|
||||||
|
|
||||||
@ -1708,6 +1734,7 @@ public:
|
|||||||
bool is_render_loop_enabled() const;
|
bool is_render_loop_enabled() const;
|
||||||
void set_render_loop_enabled(bool p_enabled);
|
void set_render_loop_enabled(bool p_enabled);
|
||||||
|
|
||||||
|
virtual bool is_on_render_thread() = 0;
|
||||||
virtual void call_on_render_thread(const Callable &p_callable) = 0;
|
virtual void call_on_render_thread(const Callable &p_callable) = 0;
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
@ -122,17 +122,21 @@ public:
|
|||||||
|
|
||||||
/** rendering and internal **/
|
/** rendering and internal **/
|
||||||
|
|
||||||
|
// These methods are called from the main thread.
|
||||||
|
virtual Transform3D get_camera_transform() = 0; /* returns the position of our camera, only used for updating reference frame. For monoscopic this is equal to the views transform, for stereoscopic this should be an average */
|
||||||
|
virtual void process() = 0;
|
||||||
|
|
||||||
|
// These methods can be called from both main and render thread.
|
||||||
virtual Size2 get_render_target_size() = 0; /* returns the recommended render target size per eye for this device */
|
virtual Size2 get_render_target_size() = 0; /* returns the recommended render target size per eye for this device */
|
||||||
virtual uint32_t get_view_count() = 0; /* returns the view count we need (1 is monoscopic, 2 is stereoscopic but can be more) */
|
virtual uint32_t get_view_count() = 0; /* returns the view count we need (1 is monoscopic, 2 is stereoscopic but can be more) */
|
||||||
virtual Transform3D get_camera_transform() = 0; /* returns the position of our camera for updating our camera node. For monoscopic this is equal to the views transform, for stereoscopic this should be an average */
|
|
||||||
|
// These methods are called from the rendering thread.
|
||||||
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */
|
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */
|
||||||
virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) = 0; /* get each view projection matrix */
|
virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) = 0; /* get each view projection matrix */
|
||||||
virtual RID get_vrs_texture(); /* obtain VRS texture */
|
virtual RID get_vrs_texture(); /* obtain VRS texture */
|
||||||
virtual RID get_color_texture(); /* obtain color output texture (if applicable) */
|
virtual RID get_color_texture(); /* obtain color output texture (if applicable) */
|
||||||
virtual RID get_depth_texture(); /* obtain depth output texture (if applicable, used for reprojection) */
|
virtual RID get_depth_texture(); /* obtain depth output texture (if applicable, used for reprojection) */
|
||||||
virtual RID get_velocity_texture(); /* obtain velocity output texture (if applicable, used for spacewarp) */
|
virtual RID get_velocity_texture(); /* obtain velocity output texture (if applicable, used for spacewarp) */
|
||||||
|
|
||||||
virtual void process() = 0;
|
|
||||||
virtual void pre_render(){};
|
virtual void pre_render(){};
|
||||||
virtual bool pre_draw_viewport(RID p_render_target) { return true; }; /* inform XR interface we are about to start our viewport draw process */
|
virtual bool pre_draw_viewport(RID p_render_target) { return true; }; /* inform XR interface we are about to start our viewport draw process */
|
||||||
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* inform XR interface we finished our viewport draw process */
|
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* inform XR interface we finished our viewport draw process */
|
||||||
|
@ -51,7 +51,7 @@ XRServer *XRServer::singleton = nullptr;
|
|||||||
|
|
||||||
XRServer *XRServer::get_singleton() {
|
XRServer *XRServer::get_singleton() {
|
||||||
return singleton;
|
return singleton;
|
||||||
};
|
}
|
||||||
|
|
||||||
void XRServer::_bind_methods() {
|
void XRServer::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_world_scale"), &XRServer::get_world_scale);
|
ClassDB::bind_method(D_METHOD("get_world_scale"), &XRServer::get_world_scale);
|
||||||
@ -59,7 +59,7 @@ void XRServer::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("get_world_origin"), &XRServer::get_world_origin);
|
ClassDB::bind_method(D_METHOD("get_world_origin"), &XRServer::get_world_origin);
|
||||||
ClassDB::bind_method(D_METHOD("set_world_origin", "world_origin"), &XRServer::set_world_origin);
|
ClassDB::bind_method(D_METHOD("set_world_origin", "world_origin"), &XRServer::set_world_origin);
|
||||||
ClassDB::bind_method(D_METHOD("get_reference_frame"), &XRServer::get_reference_frame);
|
ClassDB::bind_method(D_METHOD("get_reference_frame"), &XRServer::get_reference_frame);
|
||||||
ClassDB::bind_method(D_METHOD("clear_reference_frame"), &XRServer::get_reference_frame);
|
ClassDB::bind_method(D_METHOD("clear_reference_frame"), &XRServer::clear_reference_frame);
|
||||||
ClassDB::bind_method(D_METHOD("center_on_hmd", "rotation_mode", "keep_height"), &XRServer::center_on_hmd);
|
ClassDB::bind_method(D_METHOD("center_on_hmd", "rotation_mode", "keep_height"), &XRServer::center_on_hmd);
|
||||||
ClassDB::bind_method(D_METHOD("get_hmd_transform"), &XRServer::get_hmd_transform);
|
ClassDB::bind_method(D_METHOD("get_hmd_transform"), &XRServer::get_hmd_transform);
|
||||||
|
|
||||||
@ -104,11 +104,20 @@ void XRServer::_bind_methods() {
|
|||||||
ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
|
ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
|
||||||
ADD_SIGNAL(MethodInfo("tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
|
ADD_SIGNAL(MethodInfo("tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
|
||||||
ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
|
ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
|
||||||
};
|
}
|
||||||
|
|
||||||
double XRServer::get_world_scale() const {
|
double XRServer::get_world_scale() const {
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
|
||||||
|
if (rendering_server && rendering_server->is_on_render_thread()) {
|
||||||
|
// Return the value with which we're currently rendering,
|
||||||
|
// if we're on the render thread
|
||||||
|
return render_state.world_scale;
|
||||||
|
} else {
|
||||||
|
// Return our current value
|
||||||
return world_scale;
|
return world_scale;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void XRServer::set_world_scale(double p_world_scale) {
|
void XRServer::set_world_scale(double p_world_scale) {
|
||||||
if (p_world_scale < 0.01) {
|
if (p_world_scale < 0.01) {
|
||||||
@ -118,19 +127,58 @@ void XRServer::set_world_scale(double p_world_scale) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
world_scale = p_world_scale;
|
world_scale = p_world_scale;
|
||||||
};
|
set_render_world_scale(world_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XRServer::_set_render_world_scale(double p_world_scale) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
XRServer *xr_server = XRServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(xr_server);
|
||||||
|
xr_server->render_state.world_scale = p_world_scale;
|
||||||
|
}
|
||||||
|
|
||||||
Transform3D XRServer::get_world_origin() const {
|
Transform3D XRServer::get_world_origin() const {
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
|
||||||
|
if (rendering_server && rendering_server->is_on_render_thread()) {
|
||||||
|
// Return the value with which we're currently rendering,
|
||||||
|
// if we're on the render thread
|
||||||
|
return render_state.world_origin;
|
||||||
|
} else {
|
||||||
|
// Return our current value
|
||||||
return world_origin;
|
return world_origin;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void XRServer::set_world_origin(const Transform3D &p_world_origin) {
|
void XRServer::set_world_origin(const Transform3D &p_world_origin) {
|
||||||
world_origin = p_world_origin;
|
world_origin = p_world_origin;
|
||||||
};
|
set_render_world_origin(world_origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XRServer::_set_render_world_origin(const Transform3D &p_world_origin) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
XRServer *xr_server = XRServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(xr_server);
|
||||||
|
xr_server->render_state.world_origin = p_world_origin;
|
||||||
|
}
|
||||||
|
|
||||||
Transform3D XRServer::get_reference_frame() const {
|
Transform3D XRServer::get_reference_frame() const {
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL_V(rendering_server, reference_frame);
|
||||||
|
|
||||||
|
if (rendering_server->is_on_render_thread()) {
|
||||||
|
// Return the value with which we're currently rendering,
|
||||||
|
// if we're on the render thread
|
||||||
|
return render_state.reference_frame;
|
||||||
|
} else {
|
||||||
|
// Return our current value
|
||||||
return reference_frame;
|
return reference_frame;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
|
void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
|
||||||
if (primary_interface == nullptr) {
|
if (primary_interface == nullptr) {
|
||||||
@ -156,27 +204,38 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
|
|||||||
} else if (p_rotation_mode == 2) {
|
} else if (p_rotation_mode == 2) {
|
||||||
// remove our rotation, we're only interesting in centering on position
|
// remove our rotation, we're only interesting in centering on position
|
||||||
new_reference_frame.basis = Basis();
|
new_reference_frame.basis = Basis();
|
||||||
};
|
}
|
||||||
|
|
||||||
// don't negate our height
|
// don't negate our height
|
||||||
if (p_keep_height) {
|
if (p_keep_height) {
|
||||||
new_reference_frame.origin.y = 0.0;
|
new_reference_frame.origin.y = 0.0;
|
||||||
};
|
}
|
||||||
|
|
||||||
reference_frame = new_reference_frame.inverse();
|
reference_frame = new_reference_frame.inverse();
|
||||||
};
|
set_render_reference_frame(reference_frame);
|
||||||
|
}
|
||||||
|
|
||||||
void XRServer::clear_reference_frame() {
|
void XRServer::clear_reference_frame() {
|
||||||
reference_frame = Transform3D();
|
reference_frame = Transform3D();
|
||||||
|
set_render_reference_frame(reference_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XRServer::_set_render_reference_frame(const Transform3D &p_reference_frame) {
|
||||||
|
// Must be called from rendering thread!
|
||||||
|
ERR_NOT_ON_RENDER_THREAD;
|
||||||
|
|
||||||
|
XRServer *xr_server = XRServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(xr_server);
|
||||||
|
xr_server->render_state.reference_frame = p_reference_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform3D XRServer::get_hmd_transform() {
|
Transform3D XRServer::get_hmd_transform() {
|
||||||
Transform3D hmd_transform;
|
Transform3D hmd_transform;
|
||||||
if (primary_interface != nullptr) {
|
if (primary_interface != nullptr) {
|
||||||
hmd_transform = primary_interface->get_camera_transform();
|
hmd_transform = primary_interface->get_camera_transform();
|
||||||
};
|
}
|
||||||
return hmd_transform;
|
return hmd_transform;
|
||||||
};
|
}
|
||||||
|
|
||||||
void XRServer::add_interface(const Ref<XRInterface> &p_interface) {
|
void XRServer::add_interface(const Ref<XRInterface> &p_interface) {
|
||||||
ERR_FAIL_COND(p_interface.is_null());
|
ERR_FAIL_COND(p_interface.is_null());
|
||||||
@ -185,12 +244,12 @@ void XRServer::add_interface(const Ref<XRInterface> &p_interface) {
|
|||||||
if (interfaces[i] == p_interface) {
|
if (interfaces[i] == p_interface) {
|
||||||
ERR_PRINT("Interface was already added");
|
ERR_PRINT("Interface was already added");
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
interfaces.push_back(p_interface);
|
interfaces.push_back(p_interface);
|
||||||
emit_signal(SNAME("interface_added"), p_interface->get_name());
|
emit_signal(SNAME("interface_added"), p_interface->get_name());
|
||||||
};
|
}
|
||||||
|
|
||||||
void XRServer::remove_interface(const Ref<XRInterface> &p_interface) {
|
void XRServer::remove_interface(const Ref<XRInterface> &p_interface) {
|
||||||
ERR_FAIL_COND(p_interface.is_null());
|
ERR_FAIL_COND(p_interface.is_null());
|
||||||
@ -200,33 +259,33 @@ void XRServer::remove_interface(const Ref<XRInterface> &p_interface) {
|
|||||||
if (interfaces[i] == p_interface) {
|
if (interfaces[i] == p_interface) {
|
||||||
idx = i;
|
idx = i;
|
||||||
break;
|
break;
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
ERR_FAIL_COND_MSG(idx == -1, "Interface not found.");
|
ERR_FAIL_COND_MSG(idx == -1, "Interface not found.");
|
||||||
print_verbose("XR: Removed interface \"" + p_interface->get_name() + "\"");
|
print_verbose("XR: Removed interface \"" + p_interface->get_name() + "\"");
|
||||||
emit_signal(SNAME("interface_removed"), p_interface->get_name());
|
emit_signal(SNAME("interface_removed"), p_interface->get_name());
|
||||||
interfaces.remove_at(idx);
|
interfaces.remove_at(idx);
|
||||||
};
|
}
|
||||||
|
|
||||||
int XRServer::get_interface_count() const {
|
int XRServer::get_interface_count() const {
|
||||||
return interfaces.size();
|
return interfaces.size();
|
||||||
};
|
}
|
||||||
|
|
||||||
Ref<XRInterface> XRServer::get_interface(int p_index) const {
|
Ref<XRInterface> XRServer::get_interface(int p_index) const {
|
||||||
ERR_FAIL_INDEX_V(p_index, interfaces.size(), nullptr);
|
ERR_FAIL_INDEX_V(p_index, interfaces.size(), nullptr);
|
||||||
|
|
||||||
return interfaces[p_index];
|
return interfaces[p_index];
|
||||||
};
|
}
|
||||||
|
|
||||||
Ref<XRInterface> XRServer::find_interface(const String &p_name) const {
|
Ref<XRInterface> XRServer::find_interface(const String &p_name) const {
|
||||||
for (int i = 0; i < interfaces.size(); i++) {
|
for (int i = 0; i < interfaces.size(); i++) {
|
||||||
if (interfaces[i]->get_name() == p_name) {
|
if (interfaces[i]->get_name() == p_name) {
|
||||||
return interfaces[i];
|
return interfaces[i];
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
return Ref<XRInterface>();
|
return Ref<XRInterface>();
|
||||||
};
|
}
|
||||||
|
|
||||||
TypedArray<Dictionary> XRServer::get_interfaces() const {
|
TypedArray<Dictionary> XRServer::get_interfaces() const {
|
||||||
Array ret;
|
Array ret;
|
||||||
@ -238,14 +297,14 @@ TypedArray<Dictionary> XRServer::get_interfaces() const {
|
|||||||
iface_info["name"] = interfaces[i]->get_name();
|
iface_info["name"] = interfaces[i]->get_name();
|
||||||
|
|
||||||
ret.push_back(iface_info);
|
ret.push_back(iface_info);
|
||||||
};
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
}
|
||||||
|
|
||||||
Ref<XRInterface> XRServer::get_primary_interface() const {
|
Ref<XRInterface> XRServer::get_primary_interface() const {
|
||||||
return primary_interface;
|
return primary_interface;
|
||||||
};
|
}
|
||||||
|
|
||||||
void XRServer::set_primary_interface(const Ref<XRInterface> &p_primary_interface) {
|
void XRServer::set_primary_interface(const Ref<XRInterface> &p_primary_interface) {
|
||||||
if (p_primary_interface.is_null()) {
|
if (p_primary_interface.is_null()) {
|
||||||
@ -256,7 +315,7 @@ void XRServer::set_primary_interface(const Ref<XRInterface> &p_primary_interface
|
|||||||
|
|
||||||
print_verbose("XR: Primary interface set to: " + primary_interface->get_name());
|
print_verbose("XR: Primary interface set to: " + primary_interface->get_name());
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
void XRServer::add_tracker(const Ref<XRTracker> &p_tracker) {
|
void XRServer::add_tracker(const Ref<XRTracker> &p_tracker) {
|
||||||
ERR_FAIL_COND(p_tracker.is_null());
|
ERR_FAIL_COND(p_tracker.is_null());
|
||||||
@ -272,7 +331,7 @@ void XRServer::add_tracker(const Ref<XRTracker> &p_tracker) {
|
|||||||
trackers[tracker_name] = p_tracker;
|
trackers[tracker_name] = p_tracker;
|
||||||
emit_signal(SNAME("tracker_added"), tracker_name, p_tracker->get_tracker_type());
|
emit_signal(SNAME("tracker_added"), tracker_name, p_tracker->get_tracker_type());
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
void XRServer::remove_tracker(const Ref<XRTracker> &p_tracker) {
|
void XRServer::remove_tracker(const Ref<XRTracker> &p_tracker) {
|
||||||
ERR_FAIL_COND(p_tracker.is_null());
|
ERR_FAIL_COND(p_tracker.is_null());
|
||||||
@ -285,7 +344,7 @@ void XRServer::remove_tracker(const Ref<XRTracker> &p_tracker) {
|
|||||||
// and remove it
|
// and remove it
|
||||||
trackers.erase(tracker_name);
|
trackers.erase(tracker_name);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Dictionary XRServer::get_trackers(int p_tracker_types) {
|
Dictionary XRServer::get_trackers(int p_tracker_types) {
|
||||||
Dictionary res;
|
Dictionary res;
|
||||||
@ -307,7 +366,7 @@ Ref<XRTracker> XRServer::get_tracker(const StringName &p_name) const {
|
|||||||
// tracker hasn't been registered yet, which is fine, no need to spam the error log...
|
// tracker hasn't been registered yet, which is fine, no need to spam the error log...
|
||||||
return Ref<XRTracker>();
|
return Ref<XRTracker>();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
PackedStringArray XRServer::get_suggested_tracker_names() const {
|
PackedStringArray XRServer::get_suggested_tracker_names() const {
|
||||||
PackedStringArray arr;
|
PackedStringArray arr;
|
||||||
@ -369,9 +428,9 @@ void XRServer::_process() {
|
|||||||
// ignore, not a valid reference
|
// ignore, not a valid reference
|
||||||
} else if (interfaces[i]->is_initialized()) {
|
} else if (interfaces[i]->is_initialized()) {
|
||||||
interfaces.write[i]->process();
|
interfaces.write[i]->process();
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
void XRServer::pre_render() {
|
void XRServer::pre_render() {
|
||||||
// called from RendererViewport.draw_viewports right before we start drawing our viewports
|
// called from RendererViewport.draw_viewports right before we start drawing our viewports
|
||||||
@ -383,8 +442,8 @@ void XRServer::pre_render() {
|
|||||||
// ignore, not a valid reference
|
// ignore, not a valid reference
|
||||||
} else if (interfaces[i]->is_initialized()) {
|
} else if (interfaces[i]->is_initialized()) {
|
||||||
interfaces.write[i]->pre_render();
|
interfaces.write[i]->pre_render();
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XRServer::end_frame() {
|
void XRServer::end_frame() {
|
||||||
@ -396,14 +455,13 @@ void XRServer::end_frame() {
|
|||||||
// ignore, not a valid reference
|
// ignore, not a valid reference
|
||||||
} else if (interfaces[i]->is_initialized()) {
|
} else if (interfaces[i]->is_initialized()) {
|
||||||
interfaces.write[i]->end_frame();
|
interfaces.write[i]->end_frame();
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XRServer::XRServer() {
|
XRServer::XRServer() {
|
||||||
singleton = this;
|
singleton = this;
|
||||||
world_scale = 1.0;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
XRServer::~XRServer() {
|
XRServer::~XRServer() {
|
||||||
primary_interface.unref();
|
primary_interface.unref();
|
||||||
@ -412,4 +470,4 @@ XRServer::~XRServer() {
|
|||||||
trackers.clear();
|
trackers.clear();
|
||||||
|
|
||||||
singleton = nullptr;
|
singleton = nullptr;
|
||||||
};
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "core/os/thread_safe.h"
|
#include "core/os/thread_safe.h"
|
||||||
#include "core/templates/rid.h"
|
#include "core/templates/rid.h"
|
||||||
#include "core/variant/variant.h"
|
#include "core/variant/variant.h"
|
||||||
|
#include "rendering_server.h"
|
||||||
|
|
||||||
class XRInterface;
|
class XRInterface;
|
||||||
class XRTracker;
|
class XRTracker;
|
||||||
@ -92,10 +93,46 @@ private:
|
|||||||
|
|
||||||
Ref<XRInterface> primary_interface; /* we'll identify one interface as primary, this will be used by our viewports */
|
Ref<XRInterface> primary_interface; /* we'll identify one interface as primary, this will be used by our viewports */
|
||||||
|
|
||||||
double world_scale; /* scale by which we multiply our tracker positions */
|
double world_scale = 1.0; /* scale by which we multiply our tracker positions */
|
||||||
Transform3D world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */
|
Transform3D world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */
|
||||||
Transform3D reference_frame; /* our reference frame */
|
Transform3D reference_frame; /* our reference frame */
|
||||||
|
|
||||||
|
// As we may be updating our main state for our next frame while we're still rendering our previous frame,
|
||||||
|
// we need to keep copies around.
|
||||||
|
struct RenderState {
|
||||||
|
double world_scale = 1.0; /* scale by which we multiply our tracker positions */
|
||||||
|
Transform3D world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */
|
||||||
|
Transform3D reference_frame; /* our reference frame */
|
||||||
|
} render_state;
|
||||||
|
|
||||||
|
static void _set_render_world_scale(double p_world_scale);
|
||||||
|
static void _set_render_world_origin(const Transform3D &p_world_origin);
|
||||||
|
static void _set_render_reference_frame(const Transform3D &p_reference_frame);
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_render_world_scale(double p_world_scale) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&XRServer::_set_render_world_scale).bind(p_world_scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_render_world_origin(const Transform3D &p_world_origin) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&XRServer::_set_render_world_origin).bind(p_world_origin));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_render_reference_frame(const Transform3D &p_reference_frame) {
|
||||||
|
// If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
|
||||||
|
RenderingServer *rendering_server = RenderingServer::get_singleton();
|
||||||
|
ERR_FAIL_NULL(rendering_server);
|
||||||
|
|
||||||
|
rendering_server->call_on_render_thread(callable_mp_static(&XRServer::_set_render_reference_frame).bind(p_reference_frame));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static XRServer *singleton;
|
static XRServer *singleton;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user