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

Migrate Android framework to SDL #6105

Merged
merged 25 commits into from
Apr 29, 2024
Merged

Migrate Android framework to SDL #6105

merged 25 commits into from
Apr 29, 2024

Conversation

hwsmm
Copy link
Contributor

@hwsmm hwsmm commented Jan 4, 2024

This PR lets Android devices use SDL for windowing/input. osuTK usage got removed in the process of doing it.
It also removes Android input handling code and replaces them with existing SDL components.

I did test using Vulkan with it and it kind of works, but it was just full of glitches (a lot of flashes and tearing). Veldrid backends (OpenGL/Vulkan) crash if they are brought up from background. I don't know well about graphics, anyway.. the only graphics backend that works well is Legacy OpenGL.

Java is needed because SDL Android heavily relies on JNI, and its binding is made with Xamarin.Android.

I didn't reuse the code, but brought an idea from https://github.com/0x0ade/SDL2Droid-CS, it helped me a lot!

Tested on my tablet with Android 14.

You can use this osu! branch if you want to test.

Screenshot_20240103_162707

@bdach
Copy link
Collaborator

bdach commented Jan 4, 2024

Interesting that you got this to work (I tried what sounds to be the exact same approach, more or less, and it didn't work - input was broken, and so was debugging). Interested to see if this actually works.

The packaging 200% needs work though. We cannot be consuming random binaries from random sources. The build toolchain for our SDL2 fork (https://github.com/ppy/SDL2-CS) must be used for SDL binaries. Maybe the extra wrapper code must live there too (or else, in a separate repository), and if possible, the changes to the SDL Android project should be upstreamed.

@hwsmm
Copy link
Contributor Author

hwsmm commented Jan 4, 2024

I understand that reproducibility and integrity matter a lot in packaging, so I even considered excluding my binaries from this PR, but I thought it would cause some big inconvenience for testing, so I just went with having binaries included. I should have mentioned it.

I will look into that repo and open another PR there if I come up with an okay solution.

@Susko3
Copy link
Member

Susko3 commented Jan 4, 2024

SDL Android looks for the SDL main function from dynamic library symbol. Let me know if there's any better way. I'd be happy to make it better.

You could try using UnmanagedCallersOnlyAttribute for this. I think this will expose the function from the managed osu.Framework.Android.dll or SampleGame.Android.dll so hopefully SDL might be able to see it. I think that this requires compiling with NativeAOT.

With this, SDL2AndroidMainSetter should be completely unnecessary.

Example code from another project, here the native library expects a obs_module_load to be exposed on the DLL, this project is compiled with NativeAOT and the native library can see and call the function.

@bdach
Copy link
Collaborator

bdach commented Jan 4, 2024

I think that this requires compiling with NativeAOT.

I'm not sure it will be relevant but the last time the words "AOT" and "android" were in close proximity things fizzled out very quickly due to being terminally broken. So I'm not holding my breath...

@hwsmm
Copy link
Contributor Author

hwsmm commented Jan 5, 2024

Above PR adds SDL2-CS-Android project to SDL2-CS. Changes in Java code are no longer needed, too.
I'll commit changes that are needed from framework-side once it gets merged, since there isn't a NuGet package to depend on yet.

You can test it by removing osu.Framework.Android.SDL2 reference from osu.Framework.Android and add a reference to SDL2-CS-Android in osu.Framework.Android.

Edit: Tried AOT, but it doesn't seem to launch on my device...

@Susko3
Copy link
Member

Susko3 commented Jan 6, 2024

libsdl-org/SDL@3a482eb could be used to avoid the need for SDL2AndroidMainSetter. It's currently only available in SDL3, but we could backport it to SDL2.

@hwsmm
Copy link
Contributor Author

hwsmm commented Jan 6, 2024

I will just push my local framework as local SDL2-CS is needed to test at the moment anyway, and tidy up once things look okay.

With @Susko3's suggestion, I got it working and successfully removed SDL2AndroidMainSetter! You can test it by using SDL2-CS update-android-binaries branch.

@Joehuu
Copy link
Member

Joehuu commented Feb 10, 2024

On the framework apps (test and samplegame), I get a potential crash when rotating 90 degrees (osu! not affected as it can't rotate 90):

02-10 14:12:41.974 12820 12853 E AndroidRuntime: FATAL EXCEPTION: SDLThread
02-10 14:12:41.974 12820 12853 E AndroidRuntime: Process: osu.Framework.Tests.Android, PID: 12820
02-10 14:12:41.974 12820 12853 E AndroidRuntime: android.runtime.JavaProxyThrowable: [NUnit.Framework.AssertionException]: Stored Displays don't match actual displays: Stored displays:
02-10 14:12:41.974 12820 12853 E AndroidRuntime:   Name: 0, Bounds: {X=0,Y=0,Width=2400,Height=1080}, DisplayModes: 0, Index: 0
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 
02-10 14:12:41.974 12820 12853 E AndroidRuntime: Actual displays:
02-10 14:12:41.974 12820 12853 E AndroidRuntime:   Name: 0, Bounds: {X=0,Y=0,Width=1080,Height=2400}, DisplayModes: 0, Index: 0
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Logging.ThrowingTraceListener.Fail(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at System.Diagnostics.TraceInternal.Fail(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at System.Diagnostics.TraceInternal+TraceProvider.Fail(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at System.Diagnostics.Debug.Fail(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at System.Diagnostics.Debug.Assert(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Platform.SDL2Window.assertDisplaysMatchSDL(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Platform.SDL2Window.<handleWindowEvent>b__290_0(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Threading.ScheduledDelegate.InvokeTask(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Threading.Scheduler.Update(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Platform.SDL2Window.RunFrame(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Platform.SDL2Window.RunMainLoop(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Platform.SDL2Window.Run(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Platform.GameHost.Run(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at osu.Framework.Android.AndroidGameActivity.<CreateSDLMainRunnable>b__17_0(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at Java.Lang.Runnable.Run(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at Java.Lang.IRunnableInvoker.n_Run(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(Unknown Source:0)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at mono.java.lang.Runnable.n_run(Native Method)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at mono.java.lang.Runnable.run(Runnable.java:31)
02-10 14:12:41.974 12820 12853 E AndroidRuntime: 	at java.lang.Thread.run(Thread.java:1012)

This may also resolve ppy/osu#5236 for android, but there's a resizing bug which may be a blocker as it's really broken (on Pixel 6a with Android 14 beta if that changes anything):

screen-20240210-143706.mp4

Copy link
Member

@Joehuu Joehuu left a comment

Choose a reason for hiding this comment

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

As commented above.

@hwsmm
Copy link
Contributor Author

hwsmm commented Feb 12, 2024

I actually have run into that crash when I was writing this PR, but didn't fix it eventually since assertDisplaysMatchSDL is only present in Debug configuration. I fixed it in my last commit.

/// <summary>
/// Asserts that the current <see cref="Displays"/> match the actual displays as reported by SDL.
/// </summary>
/// <remarks>
/// This assert is not fatal, as the <see cref="Displays"/> will get updated sooner or later
/// in <see cref="handleDisplayEvent"/> or <see cref="handleWindowEvent"/>.
/// </remarks>
[Conditional("DEBUG")]
private void assertDisplaysMatchSDL()

I can't reproduce the resizing bug in the video you posted, In the latest release (osuTK), my game doesn't get moved/resized at all, so I can't see what I am typing because keyboard hides bottom half, and in my build (SDL2), the game goes up (only showing bottom half), so I can see my chat fine.

My phone is Galaxy A51 on Android 13, so something may be different with keyboard handling, but I'm not really sure..

@Joehuu
Copy link
Member

Joehuu commented Feb 13, 2024

I can't seem to reproduce the resizing bug anymore. Probably incidentally fixed with the new commits.

The textboxes still have an off-by-one screen offset bug though:

  • click/focus chat text box (no screen offset)
  • click chat text box again (puts it into view)
  • click settings text box (still offsets the screen where the chat text box used to be)

@hwsmm
Copy link
Contributor Author

hwsmm commented Apr 20, 2024

I applied most of the review suggestions, but there seem to be some remaining issues (keyboard screen offsets and multi-touch assert fail for now?), but I currently don't have much time to actually investigate into them.. I should be more free after a week, so I'll look into them then.

Though if anyone suggests fixes for the forementioned issues in the meantime, I'll try applying them (or just commit them yourself if you have perms?).

@Susko3
Copy link
Member

Susko3 commented Apr 20, 2024

The main problem with the keyboard is that we're calling SDL_StartTextInput before SDL_SetTextInputRect. This leads to the keyboard using the stale text input rect value.

From SDL_SetTextInputRect documentation:

To start text input in a given location, this function is intended to be called before SDL_StartTextInput, although some platforms support moving the rectangle even while text input (and a composition) is active.

I'll try to figure something out.

@Susko3
Copy link
Member

Susko3 commented Apr 20, 2024

I've reported #6105 (review) upstream in libsdl-org/SDL#9591. This issue is not unique to android and can fail on Windows too.

@bdach
Copy link
Collaborator

bdach commented Apr 23, 2024

@Susko3 is this fine to move forward after all of the other PRs?

@Susko3
Copy link
Member

Susko3 commented Apr 23, 2024

Yep this is good to go, just needs merge master + removal of no longer necessary workarounds:

  1. Revert SDL3Window_Windowing.cs
  2. AndroidGameWindow.Create() workarounds

Also the AndroidGameHost shouldn't be disposed.

@bdach
Copy link
Collaborator

bdach commented Apr 23, 2024

Revert SDL3Window_Windowing.cs

All of it? What about this thing? I don't see it being moved anywhere?

@Susko3
Copy link
Member

Susko3 commented Apr 23, 2024

What about this thing? I don't see it being moved anywhere?

You're correct, that has to stay, I must have confused it with the dropped events that were fixed with #6259.

Not sure why it's still here, I asked during review and it was supposed
to be removed.
@bdach
Copy link
Collaborator

bdach commented Apr 26, 2024

Also the AndroidGameHost shouldn't be disposed.

Have removed this in dad23c0.

2. AndroidGameWindow.Create() workarounds

I can't seem to be able to remove these. Doing so results in the window never becoming focused and as such touch input doesn't work.

I'd really like to move this along now so I think this can be fixed as a follow-up.

bdach
bdach previously approved these changes Apr 26, 2024
Comment on lines 47 to 51
// blocks back button
SDL.SDL3.SDL_SetHint(SDL.SDL3.SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1"u8);

// hints are here because they don't apply well in another location such as SDL3Window

Copy link
Contributor Author

@hwsmm hwsmm Apr 26, 2024

Choose a reason for hiding this comment

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

The reason why I didn't remove them was because SDL docs had this comment on the hint:

 * This is necessary for the right mouse button to work on some Android
 * devices, or to be able to trap the back button for use in your code
 * reliably. If this hint is true, the back button will show up as an
 * SDL_EVENT_KEY_DOWN / SDL_EVENT_KEY_UP pair with a keycode of
 * SDL_SCANCODE_AC_BACK.
 *
 * The variable can be set to the following values:
 *
 * - "0": Back button will be handled as usual for system. (default)
 * - "1": Back button will be trapped, allowing you to handle the key press
 *   manually. (This will also let right mouse click work on systems where the
 *   right mouse button functions as back.)

https://wiki.libsdl.org/SDL3/SDL_HINT_ANDROID_TRAP_BACK_BUTTON

I should have made it more clear..

Not sure how it works without the hint though, but I guess it may be fine if it works?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Seemed fine to me /shrug

Copy link
Member

@Susko3 Susko3 left a comment

Choose a reason for hiding this comment

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

Seems to work well, just need to remove the osuTK.Android reference as it's no longer needed.

}

/// <summary>
/// Updates the <see cref="IWindow.SafeAreaPadding"/>, taking into account screen insets that may be obstructing this <see cref="AndroidGameView"/>.
Copy link
Member

Choose a reason for hiding this comment

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

Should be updated to mention the proper class, and remove using osuTK.Android;

Suggested change
/// Updates the <see cref="IWindow.SafeAreaPadding"/>, taking into account screen insets that may be obstructing this <see cref="AndroidGameView"/>.
/// Updates the <see cref="IWindow.SafeAreaPadding"/>, taking into account screen insets that may be obstructing this <see cref="AndroidGameSurface"/>.

@@ -19,6 +19,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osuTK.Android" Version="1.0.211" />
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<PackageReference Include="ppy.osuTK.Android" Version="1.0.211" />

Not needed if the xmldoc reference in AndroidGameSurface is removed.

@Susko3
Copy link
Member

Susko3 commented Apr 26, 2024

just need to remove the osuTK.Android reference as it's no longer needed.

Or you can merge right now, and we do this later in osuTK cleanup.

@bdach bdach merged commit 508d7e0 into ppy:master Apr 29, 2024
17 of 19 checks passed
@hwsmm hwsmm deleted the sdl-cs-android branch May 4, 2024 05:37
peppy added a commit to peppy/osu-framework that referenced this pull request May 21, 2024
This reverts commit 508d7e0, reversing
changes made to 169bd07.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants