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

Reimplemented blocking_event_loop for OSX #462

Merged
merged 4 commits into from
Jul 14, 2024

Conversation

birhburh
Copy link
Contributor

While solving this problem, decided to take a less hacky approach

  • Reverted own NSApplication run implementation
  • Used same approach as for android and ios
  • Catched the bug with examples/post_processing.rs turning black, checked that it existed even before own NSApplication run implementation

@birhburh birhburh force-pushed the blocking_event_loop_osx branch 2 times, most recently from 7e05825 to 9abc2c1 Compare June 25, 2024 15:09
birhburh added 2 commits June 25, 2024 18:45
- Reverted own NSApplication run implementation
- Used same approach as for android and ios
@birhburh birhburh force-pushed the blocking_event_loop_osx branch from 9abc2c1 to ea7d82b Compare June 25, 2024 16:45
@markmurphydev
Copy link
Contributor

When I run any of the examples using this PR on macos, I get a crash when the window loads.

Seems to happen when macos.rs line 1240 gets called:

msg_send_![view, performSelectorOnMainThread:sel!(setNeedsDisplay:) withObject:YES waitUntilDone:NO];

crash_report.txt

@birhburh
Copy link
Contributor Author

birhburh commented Jun 26, 2024

@markmurphydev, thanks
it was a silly mistake:

diff --git a/src/native/macos.rs b/src/native/macos.rs
index 5ab3119..4820b66 100644
--- a/src/native/macos.rs
+++ b/src/native/macos.rs
@@ -1237,7 +1237,7 @@ where
 
             if !conf.platform.blocking_event_loop || update_requested {
                 unsafe {
-                    msg_send_![view, performSelectorOnMainThread:sel!(setNeedsDisplay:) withObject:YES waitUntilDone:NO];
+                    msg_send_![view, performSelectorOnMainThread:sel!(setNeedsDisplay:) withObject:nil waitUntilDone:NO];
                 }
             }
             thread::yield_now();

@markmurphydev
Copy link
Contributor

Thanks! With that change, the crash is fixed, but the quad.rs example has blocking_event_loop behavior, even when it's not set.

Looking at the old implementation, setNeedsDisplay: was called on view.
If I change the call to:

msg_send_![view, performSelectorOnMainThread:sel!(setNeedsDisplay:) withObject:view waitUntilDone:NO];

it seems to re-draw normally, and updates correctly while resizing.

@birhburh
Copy link
Contributor Author

@markmurphydev, yeah, checked again, thanks
But with withObject: view it wasn't working for metal

cargo run --example=quad metal

Because it expects YES as argument
Added new message so it passes YES

@not-fl3
Copy link
Owner

not-fl3 commented Jun 29, 2024

Great work, thanks for PR!

Tho I am a bit scared merging it while I do not quite understand whats going on here, and not being a mac user myself doesn't really help too much :(

I will review it eventually, but a brief description on what was wrong/how it works now would help!

@birhburh
Copy link
Contributor Author

@not-fl3, yeah, of course, thanks
Also delaying gave time @markmurphydev to test it additionally and find some bugs so it is cool

@birhburh
Copy link
Contributor Author

@not-fl3,
So I reverted my implementation of winapi-like message loop (because there will be probably some bugs on other osx versions because it is not a recommended by apple way so it may change)
Now it is using message loop in a separate thread as it was done for android and ios also
It is placed in run function and acts the same in relation to block_on_wait

So, now view delegate functions call send_message in its functions: key_down, key_up and so on and parallel thread processes them (in context of view and on main thread, because opengl bindings not work sometimes otherwise)

For or redrawing I used same approach as on ios
It is calling setNeedsDisplay: YES on main thread (and here it works for metal and opengl the same)

I was not using timer and always having 60 fps, but I left it not limited, so probably timer aproach also needed but not sure where to put it for now, probably it should be separate thing for miniquad on all platforms

I am not really sure about the need for MainThreadState, thing shared between two threads stored there, and I left some stuff there that is not probably needed in MacosDisplay itself but it is ok probably

@not-fl3
Copy link
Owner

not-fl3 commented Jul 14, 2024

I finally revived my hackintosh!

LGTM, but one little neat pick:

diff --git a/src/native/macos.rs b/src/native/macos.rs
index ad1aab7..d416467 100644
--- a/src/native/macos.rs
+++ b/src/native/macos.rs
@@ -713,12 +713,6 @@ unsafe fn view_base_decl(decl: &mut ClassDecl) {
         }
     }

-    extern "C" fn set_needs_display_hack(this: &Object, _: Sel) {
-        unsafe {
-            msg_send_![this, setNeedsDisplay: YES];
-        }
-    }
-
     decl.add_method(
         sel!(canBecomeKey),
         yes as extern "C" fn(&Object, Sel) -> BOOL,
@@ -789,10 +783,6 @@ unsafe fn view_base_decl(decl: &mut ClassDecl) {
         sel!(processMessage:),
         process_message as extern "C" fn(&Object, Sel, ObjcId),
     );
-    decl.add_method(
-        sel!(setNeedsDisplayHack),
-        set_needs_display_hack as extern "C" fn(&Object, Sel),
-    );
 }

 pub fn define_opengl_view_class() -> *const Class {
@@ -1282,7 +1272,7 @@ where

             if !conf.platform.blocking_event_loop || update_requested {
                 unsafe {
-                    msg_send_![view, performSelectorOnMainThread:sel!(setNeedsDisplayHack) withObject:nil waitUntilDone:NO];
+                    msg_send_![view, performSelectorOnMainThread:sel!(setNeedsDisplay:) withObject:YES waitUntilDone:NO];
                 }
             }
             thread::yield_now();

this seems to work fine for me for both opengl and metal, am I missing something?
(I think I re-invented one of the versions already posted here in the comments, so I guess it doesn't work, but somehow it does for me?)

@birhburh
Copy link
Contributor Author

birhburh commented Jul 14, 2024

@not-fl3, it works for me too, but it works differently on arm chips
YES defined diferently for Arm and Intel chips in objc crate
Found this article:
https://www.jviotti.com/2024/01/05/is-objective-c-bool-a-boolean-type-it-depends.html
I don't read it yet but...
Apple, why? This is just a simple bool type?
@markmurphydev, could you please recheck if the latest commit of this PR works on the arm chip both for opengl and metal?

@birhburh
Copy link
Contributor Author

@not-fl3,
Ah yes, I remembered that I found this question on stack overflow and decided just to use safe option with a separate function because performSelectorOnMainThread doesn't like primitive types (I didn't found why)
https://stackoverflow.com/questions/6120614/passing-primitives-through-performselectoronmainthread

@not-fl3
Copy link
Owner

not-fl3 commented Jul 14, 2024

ufff I had no idea! I'll add a link to this very stackoverflow answer somewhere around set_needs_display_hack

Thanks for the PR, really impressive work!

@not-fl3 not-fl3 merged commit 611f727 into not-fl3:master Jul 14, 2024
10 checks passed
// Basically reimplementing msg_send![ns_app, run] here
let () = msg_send![ns_app, finishLaunching];
let state = SendHack(state_original.clone());
thread::spawn(move || {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, somehow I ended up here again :)
I runned into this discussion: https://hero.handmade.network/forums/code-discussion/t/1409-main_game_loop_on_os_x#7886, and this made me question, do we really need this thread::spawn and [nsapp run] later? If we process the messages, maybe we can process them on right main thread?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this was my previous approach
#443
And I thought that I'm smart that I found it
Because it was my first experience of programming for osx and I found how to do window loop even though apple recommends using timer approach for games
But it seems that it isn't this simple and introduces multiple bugs, that should've been taken in account
#455
#464
And there would be more of them I think
So I decided to not fix bugs that I cannot predict and used a simple way

As I understand, sokol-app uses a timer approach and [NSApplication run]
https://github.com/floooh/sokol/blob/master/sokol_app.h#L3582

But, raylib via glfw uses nextEventMatchingMask loop and I can try to analyze their code and implement it as before, but possibly with less bugs
https://github.com/glfw/glfw/blob/master/src/cocoa_window.m#L1523

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants