Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upDo not wait for rAF to be requested in render loop #25343
Conversation
highfive
commented
Dec 19, 2019
|
Heads up! This PR modifies the following files:
|
highfive
commented
Dec 19, 2019
| self.raf_sender.borrow().is_none(), | ||
| "RAF loop already set up" | ||
| ); | ||
| fn setup_raf_loop(&self, frame_receiver: IpcReceiver<Frame>) { | ||
| let this = Trusted::new(self); | ||
| let global = self.global(); | ||
| let window = global.as_window(); | ||
| let (task_source, canceller) = window | ||
| .task_manager() | ||
| .dom_manipulation_task_source_with_canceller(); |
This comment has been minimized.
This comment has been minimized.
jdm
Dec 19, 2019
Member
To reduce the time between when we're signalled with a new frame and when we run the rAF callback, we should use a high-priority task source. The spec doesn't actually specify a particular ordering, so we should be able to get away with jumping the queue. For example, we could use a dedicated sender/receiver pair and use try_recv on it before calling select! in ScriptThread::handle_msgs.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
gterzian
Jan 1, 2020
•
Member
I think that's a good idea, and maybe it could be further improved by not going through the router to receive the frame.
I think it could be done through:
- Whenever a callback is added:
- set a
awaiting_frameflag on the script-thread. - send a
MainThreadScriptMsg::WakeUpto ensure the main loop doesn't block on the select
- set a
- After the microtask checkpoint:
- remove the
awaiting_frameflag, and, if it was true:- block on the frame receiver, and
- immediately run the callbacks when receiving it.
- remove the
- This would require also running 2(and the microtask checkpoint) if
sequentialis empty(in the case that the only event is theMainThreadScriptMsg::WakeUp, since it will be filtered out by the task-queue). It also requires Xr to always send a message on the frame receiver to unblock the script-thread.
So that could be a way to block directly on a IpcReceiver inside handle_msgs, with the benefit being always running the frame callbacks immediately after you receive the frame, and not going through the router.
Then I guess the script-thread could make the xr_frame_ pair an ipc-channel where the sender would be cloned inside request_new_xr_frame and send to Xr?
This comment has been minimized.
This comment has been minimized.
gterzian
Jan 1, 2020
•
Member
Ah ok I see you also need a way to call from the script-thread into the session, so instead of an awaiting_frame flag, you could have an Option<SessionAwaitingFrame> on the script-thread, which would be set by request_new_xr_frame and taken before optionally blocking on the frame receiver from within handle_msgs. I assume you can have only one active Xr session per script-thread?
This comment has been minimized.
This comment has been minimized.
gterzian
Jan 1, 2020
Member
Like https://github.com/gterzian/servo/tree/restructure_xr_frame_handling
If it seems like a good idea I could just open a PR as a follow-up.
This comment has been minimized.
This comment has been minimized.
Manishearth
Jan 1, 2020
Author
Member
You can have multiple online sessions, but only one immersive
We first need to figure out why this is breaking the tests. I bet it's because of a race between data getting sent and read from the mock device.
This comment has been minimized.
This comment has been minimized.
gterzian
Jan 2, 2020
•
Member
I think the problem with the timeouts is that with the current changes, the call to render_animation_frame from script only occurs if the session is immersive(and the changes in Xr from servo/webxr#114 makes the render loop dependent on rendering a frame).
Previously, a new frame would be requested at the end of running callbacks, regardless of whether the session was immersive.
This comment has been minimized.
This comment has been minimized.
Manishearth
Jan 2, 2020
Author
Member
That was indeed the issue, was poking at this earlier. Fixed now.
Opening a followup PR would be great, but we should test if it affects FPS.
|
The FPS values seem to slightly go up from 12-15 to 15-20. Timing values (in microseconds, I forgot what a nanosecond was and divided wrong) between the time the openxr backend constructs the frame ("Frame") and the time we trigger callbacks in script ("Now") :
Seems like we're losing around 2-6ms on just the communication delay. Digging further into this |
|
More timings. It seems like we're losing time in waiting for the script router to pick up the IPC message, which bolsters my initial hunch that IPC is slow for some reason.
|
|
Hmm, there's actually a consistent 1ms gap between the time we That's not the only contributing factor though. |
|
2-6ms is a lot of extra time! |
|
So |
|
Hmm, that all sounds like stuff that's out of our control? The only thing I can think of is decoupling the calls to input |
|
It might be that we're using the APIs incorrectly, too. I tried using unbounded spaces as recommended, that didn't fix it. Going for jdm's scheduling patch now to see if I can at least fix the communication overhead, which is still a nontrivial chunk. |
|
Perhaps we should look into calling begin_frame before render_animation_frame, to be more like the SDK sample at https://github.com/microsoft/OpenXR-SDK-VisualStudio/blob/4378fbda334809852331fccf2ffa9b4979e0684f/samples/BasicXrApp/OpenXrProgram.cpp#L609-L612. |
|
Timings with @jdm's patch. Delta is the time between frame creation and frame reception in script, delta_task is the time between the script process receiving it and the script thread receiving it. Callback time is the time taken to execute raf callbacks (which we can expect to be somewhat large)
There are also a bunch of entries with much larger deltas sprinkled throughout. Some are in a sequence:
the larger deltas are paired with slightly higher time taken in all the openxr stuff in wait_for_animation_frame, but it doesn't fully account for it. It's possible we're doing a lot of computation elsewhere that's just slowing everything down. (Perhaps it's because we're still rendering the webpage but not displaying it?) |
|
Other interesting things to time include:
I wonder if we could send the swap buffer message, start layout, wait for layout to complete, then join with the swap buffer completion response to avoid blocking twice sequentially... |
|
Mmm, some of those may not actually make sense here; I didn't realize we call swap_buffers directly in immersive mode. |
|
That being said, we do run layout, and we do dirty the canvas, and both of those involve blocking the script thread on receiving a response from another thread, so they could contribute to the deltas. |
|
Running layout should only block if the document is dirty or animations are running? Which hopefully pages aren't doing once they've entered immersive mode. |
|
We mark the canvas dirty right after we swap the base layer's buffer: servo/components/script/dom/xrsession.rs Lines 366 to 372 in bac9903 |
|
which makes sense for the test backend, and not for any other. |
|
Ah, that's a holdover from the pre-surfman days. |
|
We should probably merge these since I was seeing an improvement from 12-15FPS to 15-20FPS. |
|
Removing the dirty-marking does not impact FPS visibly. Worth doing anyway. |
|
This is still marked as a draft, fyi. |
Do not wait for rAF to be requested This does not make the render loop _completely_ independent of script: it still waits for script to submit a frame. If we don't, frames will be dropped (and I'm not sure what strategy to use there). I'm going to test this soon and see if it actually improves our framerate. Servo side: servo/servo#25343 r? @jdm
|
Hm? doesn't seem to be. But i need to update this to deal with the other one landing |
|
|
|
Oops:
|
|
Oh, it's possible that the synchronization of mock data no longer works with this. |
|
@bors-servo r=jdm |
|
|
|
Do not wait for rAF to be requested in render loop Servo-side of servo/webxr#114 r? @jdm
|
@bors-servo r- oops, push didn't work |
|
@bors-servo try- retry r=jdm |
|
|
Do not wait for rAF to be requested in render loop Servo-side of servo/webxr#114 r? @jdm
|
|
Manishearth commentedDec 19, 2019
Servo-side of servo/webxr#114
r? @jdm