Skip to content
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

ndk-glue: impossible to poll events in a non-blocking fashion #111

Closed
nwessing opened this issue Jan 13, 2021 · 12 comments · Fixed by #161
Closed

ndk-glue: impossible to poll events in a non-blocking fashion #111

nwessing opened this issue Jan 13, 2021 · 12 comments · Fixed by #161
Labels
type: documentation Awareness, docs, examples, etc.

Comments

@nwessing
Copy link

it's impossible to poll events in ndk-glue without blocking the thread. In the NDK samples I have seen ALooper is used to poll events with a timeout value that can be configured to block or not. Where the ndk-glue module will read the file descriptor directly with a blocking call. This makes it impossible to program games, or any application that wants to keep running without having to block for events/input.

@MarijnS95
Copy link
Member

MarijnS95 commented Jan 13, 2021

@nwessing When used with winit you have the option to set ControlFlow to Poll instead of Wait, this will disable blocking on the looper (the one that is set up by ndk-glue before calling main(), and retrieved in winit here).

If you are not using winit you can still do the same, call ThreadLooper::for_thread() within main() to get the looper set up by ndk-glue - the one that contains the reading end of PIPE (the same that is used for blocking reads in poll_events). From there you can call ThreadLooper::poll_once_timeout with a timeout of 0: if it returns Ok(Poll::Event { ident: 0 }) an event is ready to be collected with poll_events().

@MarijnS95
Copy link
Member

Related side-note:

@dvc94ch You added the event loop logic to winit, and it seems it's erroneously using the data field - which should be a pointer to user-data - to distinguish the event pipe from the INPUT_QUEUE:

https://github.com/rust-windowing/winit/blob/d1a7749df56656e356840112ebcbb3c9f34de248/src/platform_impl/android/mod.rs#L33-L37

Instead ident should be used there which is also set to 0 for the event pipe and 1 for the INPUT_QUEUE.

Shall I clean that up and send some PRs? Important is to get winit updated first, so that we can properly set this field to std::ptr::null() in ndk-glue after.

@nwessing
Copy link
Author

I see, I was using the glue module directly, I am experimenting with using the Oculus Mobile SDK in Rust, and it doesn't make too much sense to use winit since I need to hook into a lot of android specific functionality directly anyways. I will use the winit/android implementation as guide then for how to use the ndk-glue crate. Thanks for pointing me in the right direction!

@dvc94ch
Copy link
Contributor

dvc94ch commented Jan 13, 2021

@MarijnS95 ups, it is always 0 because it was set to std::ptr::null().

@MarijnS95
Copy link
Member

MarijnS95 commented Jan 13, 2021

@dvc94ch it's actually set to 1 for the input queue which is why it works. Have a commit written up, will submit that in a bit ;)

@nwessing using the links provided in the comment and the winit source should help you on your way, but don't hesitate to ask more questions: ndk-glue is largely undocumented and it'd be great to get some usage feedback!

@MarijnS95
Copy link
Member

For reference, this is taken care of in rust-windowing/winit#1826.

@kanerogers
Copy link

Chiming in to say that I'm also mucking about with the Oculus Mobile SDK in a similar manner to @nwessing, and some documentation on an easy, non-blocking way to get these events would be marvelous. 😄

@MarijnS95
Copy link
Member

@kanerogers Does my comment above and the docstrings introduced in #112 provide that? Let us know if anything is missing, then I guess someone has to work that into a minimal looper example.

@nwessing
Copy link
Author

@kanerogers I have this working pretty well at this point. I've defined the following function:

pub const LOOPER_ID_MAIN: u32 = 0;
pub const LOOPER_ID_INPUT: u32 = 1;

pub fn poll_all_ms(block: bool) -> Option<ndk_glue::Event> {
    let looper = ThreadLooper::for_thread().unwrap();
    let result = if block {
        let result = looper.poll_all();
        result
    } else {
        looper.poll_all_timeout(std::time::Duration::from_millis(0u64))
    };

    match result {
        Ok(Poll::Event { ident, .. }) => {
            let ident = ident as u32;
            if ident == LOOPER_ID_MAIN {
                ndk_glue::poll_events()
            } else if ident == LOOPER_ID_INPUT {
                if let Some(input_queue) = ndk_glue::input_queue().as_ref() {
                    while let Some(event) = input_queue.get_event() {
                        if let Some(event) = input_queue.pre_dispatch(event) {
                            input_queue.finish_event(event, false);
                        }
                    }
                }

                None
            } else {
                unreachable!("Unrecognized looper identifer");
            }
        }
        _ => None,
    }
}

And it is called in my game loop like this:

while !app.destroy_requested { // Main game loop
        loop { // event pump loop
            let block = !app.destroy_requested && app.ovr.is_none();
            if let Some(event) = poll_all_ms(block) {
                trace!("event: {:?}", event);
                app.handle_lifecycle_event(&event);
                app.update_vr_mode();
            } else {
                break;
            }
        }    
     // update and render
}

app.ovr is an Option<*mut ovr_sys::ovrMobile> (which is mutated in update_vr_mode) that is set to None when the window is destroyed or the app is in a paused state. (It follows the logic from the VrCubeWorld_NativeActivity Sample.

@kanerogers
Copy link

kanerogers commented Jan 26, 2021

@MarijnS95 your documentation definitely helped! Thanks so much! You are a fantastic maintainer of a fantastic project. ❤️

@nwessing Thanks so much for this! 😄 It looks like we are doing the exact same thing (porting VrCubeWorld_NativeActivity to rust) at the same time - lovely to know I'm not alone.

@MarijnS95
Copy link
Member

@kanerogers Neat! I'm hoping to get some remaining PRs reviewed/merged before merging #112 and bumping the minor version this weekend 😁. Will try to stuff in a minimal looper example similar to the code pasted above 🎉!

@kanerogers
Copy link

kanerogers commented Jan 28, 2021 via email

@MarijnS95 MarijnS95 added type: documentation Awareness, docs, examples, etc. status: waiting Waiting for a response or another PR labels May 18, 2021
MarijnS95 added a commit to MarijnS95/ndk that referenced this issue Jul 26, 2021
Originally proposed [here], this adds a simple looper example
demonstrating how to use Android's `Looper` through the NDK bindings in
similar fashion to `winit`.  It uses `ndk_glue`'s event pipe, Android's
`InputQueue` and a custom Unix pipe to transfer events to the Looper
(thread).

[here]: rust-mobile#111 (comment)
@MarijnS95 MarijnS95 removed the status: waiting Waiting for a response or another PR label Jul 26, 2021
MarijnS95 added a commit to MarijnS95/ndk that referenced this issue Aug 17, 2021
Originally proposed [here], this adds a simple looper example
demonstrating how to use Android's `Looper` through the NDK bindings in
similar fashion to `winit`.  It uses `ndk_glue`'s event pipe, Android's
`InputQueue` and a custom Unix pipe to transfer events to the Looper
(thread).

[here]: rust-mobile#111 (comment)
msiglreith pushed a commit that referenced this issue Aug 23, 2021
* ndk-example: Demonstrate Looper usage without winit

Originally proposed [here], this adds a simple looper example
demonstrating how to use Android's `Looper` through the NDK bindings in
similar fashion to `winit`.  It uses `ndk_glue`'s event pipe, Android's
`InputQueue` and a custom Unix pipe to transfer events to the Looper
(thread).

[here]: #111 (comment)
rib pushed a commit to rust-mobile/ndk-glue that referenced this issue Dec 6, 2022
* ndk-example: Demonstrate Looper usage without winit

Originally proposed [here], this adds a simple looper example
demonstrating how to use Android's `Looper` through the NDK bindings in
similar fashion to `winit`.  It uses `ndk_glue`'s event pipe, Android's
`InputQueue` and a custom Unix pipe to transfer events to the Looper
(thread).

[here]: rust-mobile/ndk#111 (comment)
rib pushed a commit to rust-mobile/ndk-context that referenced this issue Dec 7, 2022
* ndk-example: Demonstrate Looper usage without winit

Originally proposed [here], this adds a simple looper example
demonstrating how to use Android's `Looper` through the NDK bindings in
similar fashion to `winit`.  It uses `ndk_glue`'s event pipe, Android's
`InputQueue` and a custom Unix pipe to transfer events to the Looper
(thread).

[here]: rust-mobile/ndk#111 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation Awareness, docs, examples, etc.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants