-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OpenXR: Change timing of xrWaitFrame and fix XR multithreading issues #89734
OpenXR: Change timing of xrWaitFrame and fix XR multithreading issues #89734
Conversation
One of the issues I've run into is that I may decide to turn Also there is a potential minor breakage in |
ba57f21
to
f6ff398
Compare
74d1a8e
to
bb190d7
Compare
bb190d7
to
f1b07a8
Compare
|
||
void XRServer::_bind_methods() { | ||
ClassDB::bind_method(D_METHOD("get_world_scale"), &XRServer::get_world_scale); | ||
ClassDB::bind_method(D_METHOD("set_world_scale", "scale"), &XRServer::set_world_scale); | ||
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("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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note this is a bug that we missed for ages, it was pointing to the wrong method. This obviously breaks ABI, I'm assuming it is fine to add this as an exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I think an exception for this would be fine, since it was broken previously
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interestingly the CI isn't complaining about it..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's pretty weird that the CI isn't complaining indeed. The method hash should have changed.
Set this to ready for review, I think I've got all issues resolved in core except for the cleanup part. Right now I don't think this is a problem as we'll stop the rendering thread before we hit this code, but potentially there needs to be some more done there. I also tested this on my Quest 3 and it seems to be working fine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rendering is not my area, but looking at the code, this all makes sense to me at high-level. It should definitely be reviewed by someone more knowledgeable about rendering, though.
I tested on my Quest 3 using the demo project from godot_openxr_vendors on its master
branch.
Everything seems to work when the using single threaded rendering, but I get a segfault on start up when using multi-threaded rendering. For some reason, the debug symbols aren't showing up in the stack trace (even though they should be there!) so I don't have much more in the way of details to share. I'll try to get some more info...
Small update here:
|
@dsnopek I am glad that you located the root cause of the flickering issue 💯 And yes, I believe the |
@dsnopek no, that's not the correct fix. This is the whole reason this PR introduces the We do this for a lot of state where the main thread may change the value for the current frame being prepared, while the rendering thread should still be using the previous value while rendering the frame it's rendering (or even the value from before, the way this works is that the changes are added to a queue that is executed right before we render a frame). So it should be completely safe to set So this suggest we're either still using I was going to have a meetup with @reduz to discuss more about the internal workings of the rendering thread setup, so I get a better understanding of that last bit. |
@dsnopek so just to clarify, set_render_display_info(frame_state.predictedDisplayTime, frame_state.shouldRender); is the moment we copy the resulting values from the main thread to the render thread, and that call gets queued and won't actually be executed until the render thread is ready to render this frame. So the code running on the render thread should remain blissfully unaware we set |
It is checking In if (!can_render()) {
return false;
} ... and the definition of _FORCE_INLINE_ bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && frame_state.shouldRender; } What should we be doing here instead? |
@BastiaanOlij What about this? diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 4ced4196d1..150d7eb726 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -2156,7 +2156,7 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
// We found an XR viewport!
render_state.has_xr_viewport = true;
- if (!can_render()) {
+ if (instance == XR_NULL_HANDLE || session == XR_NULL_HANDLE || !running || !render_state.should_render) {
return false;
}
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 5e811879be..dc2e7feaf7 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -445,7 +445,6 @@ public:
_FORCE_INLINE_ XrSpace get_play_space() const { return play_space; }
_FORCE_INLINE_ XrTime get_predicted_display_time() { return frame_state.predictedDisplayTime; }
_FORCE_INLINE_ XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; }
- _FORCE_INLINE_ bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && frame_state.shouldRender; }
XrHandTrackerEXT get_hand_tracker(int p_hand_index);
I haven't had a chance to test it, but it uses |
@xwovr , @dsnopek as to the This rarely happens, the situation we very often ran into this causing issues was when you take a screenshot or start a recording, that is often where the Quest hangs onto the current image it's processing in the swapchain a little longer and we get a reason to have to wait before we can render to that image again. But the Note also that this code is called from the rendering thread ( |
Good catch, I had a problem around there where I lost a bunch of work during a rebase, I had removed all calls to Obviously that change got lost and this is indeed wrong. (same for running btw, which equally has a companion in render_state) |
2c887b9
to
beb6248
Compare
Ok, rebased and applied both the swapchain fix and fixed up Haven't tested yet (still compiling) but just so @dsnopek can check this again as well. |
beb6248
to
03547c6
Compare
Thanks! The latest is working great in my testing :-) It looks like you still haven't addressed Clay's review above, but otherwise, this is a definite improvement, and I think worth moving forward with. |
Seeing it looked like there was still too much going on for this to be merged into 4.3, I was giving some other things priority. There are also still some questions needing answering on cleanup, we can accept how things work right now and I think it will be safe but still. Though we can also treat that as a future thing to improve if it actually turns out to be an issue. Also I have had no chance at all to look into any issues with the compatibility renderer. |
Personally, I don't think we necessarily need to fix multi-threaded rendering with the compatibility renderer in this PR: it's broken before this PR, and stays broken after. So, I think it'd be OK to continue working on that in follow-up PRs. However, single-threaded rendering with the compatibility renderer still works with this PR, and I think there's improvements here even for single-threaded rendering. |
Yup, hey I'm all for including it in 4.3, and as long as you don't enable multithreading the impact is very low. But at the same time I don't want to rush this. |
03547c6
to
4d216b8
Compare
@akien-mga did a rebase of this and a few final tests. Discussing this with @dsnopek and @clayjohn we believe this is worth merging for 4.3. Right now rendering until multi-threading is broken and this is a step in the right direction, there is still plenty to do in the rendering engine itself, so multi-threading remains an experimental mode. We're not expecting any regressions in single threaded mode so we feel its a pretty safe merge. |
4d216b8
to
cbab7dc
Compare
Thanks! |
This PR changes the timing at which
xrWaitFrame
is called.This also makes a start at ensuring the OpenXR code is thread safe if the renderer is run in a separate thread.
It is important to note that much of the OpenXR data such as the instance and session are initialised before rendering commences and access should be safe. However there is also much data that can be overwritten during frame processing while the previous frame is rendering and I've only just started making that safe.
The change to
xrWaitFrame
should improve timing issues regardless of whether rendering is run in a separate thread or not.For testing OpenXR with rendering happening in a separate thread, set this project settings:
Todos:
Note: This has only been tested on the Vulkan renderer, running multi-threaded in OpenGL may have issues unrelated to the work we're doing here.