crashinvaders / gdx-basis-universal

KTX2/Basis Universal supercompressed GPU textures for libGDX game framework.
17 stars 2 forks source link

Couldn't load shared library 'gdx-basis-universal' for target: Android #2

Closed Peter-Warlock closed 6 months ago

Peter-Warlock commented 6 months ago

Hello,

Crash on old devices with that crash stack:

g2.b0: Couldn't load shared library 'gdx-basis-universal' for target: Android
  at g2.c0.d(SourceFile:123)
  at l2.e.D(SourceFile:23)
  at l2.a.<init>(SourceFile:11)
  at l2.f.prepare(SourceFile:28)
  at o1.l.v(SourceFile:34)
  at o1.l.<init>(SourceFile:4)
  at x6.o0.<init>(SourceFile:83)
  at x6.y.k(SourceFile:312)
  at l1.l.onSurfaceChanged(SourceFile:44)
  at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1512)
  at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)

Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH in "libgdx-basis-universal.so" (built with --hash-style=gnu?)
  at java.lang.Runtime.loadLibrary(Runtime.java:364)
  at java.lang.System.loadLibrary(System.java:526)
  at g2.c0.d(SourceFile:29)
  at l2.e.D(SourceFile:23) 
  at l2.a.<init>(SourceFile:11) 
  at l2.f.prepare(SourceFile:28) 
  at o1.l.v(SourceFile:34) 
  at o1.l.<init>(SourceFile:4) 
  at x6.o0.<init>(SourceFile:83) 
  at x6.y.k(SourceFile:312) 
  at l1.l.onSurfaceChanged(SourceFile:44) 
  at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1512) 
  at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240) 

The same problem and how to fix it was described here: https://github.com/google/filament/issues/2176 (suggestion to fix here: https://stackoverflow.com/a/59649817/671393)

metaphore commented 6 months ago

Hello, and thanks for reporting this. Interestingly enough, I created the same issue for another native-based library a year ago and totally forgot about it.

Let me try to rebuild the Android natives with the --hash-style=both flag. Maybe it's a cure for the case.

metaphore commented 6 months ago

Alright, hopefully, this patch works. The new version was just published and should be available on Maven Central soon. Please update the game and let me know if the problem persists.

Let's keep the issue open till we get a clear confirmation that it was fixed.

Peter-Warlock commented 6 months ago

I've updated to v1.0.1. Even compared old libgdx-basis-universal.so files with the new one. It's indeed new (has a difference) but the problem still here. Tested on Android Emulator with API 22 and on real Android Device with API 19. The same crash stack with the same:

Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH in "libgdx-basis-universal.so" (built with --hash-style=gnu?)

metaphore commented 6 months ago

I see, thank you! Seems like the flag wasn't set properly. It's good to know it can be tested on emulator, I didn't think about it.

Let me try another time and I will let you know when I make it work on the emulator 👌

metaphore commented 6 months ago

My searches led me there https://github.com/android/ndk/issues/964 Sounds like there's no easy fix for this, apart from downgrading to an old NDK, which doesn't sound all that good to me.

At this point, I'd just set the minimum required Android API to 23 and move on.

Peter-Warlock commented 6 months ago

But why libgdx.so have no this problems? Why libgdx.so loaded without errors? Maybe it's indeed just linker setting?

Peter-Warlock commented 6 months ago

Maybe here some solution: https://stackoverflow.com/questions/28638809/android-ndk-unsatisfiedlinkerror-dlopen-failed-empty-missing-dt-hash

metaphore commented 6 months ago

Ok. It wasn't NDK issue. It was me being stupid. A long time ago I copied the jnigen configuration from somewhere and there was a minimum Android API set to 26. As far as I know, the library doesn't have any other limitations on min API other than those that come with NDK itself. And NDK r26 minimum supported API is 21. So changing that in jnigen config finally made it spin on my API 22 arm64 emulator :tada:

There are some other issues with some of the textures that are just solid black. But it all sounds like another problem - #3 . While this one has been solved :ok_hand: image

metaphore commented 6 months ago

@Peter-Warlock FIY, I want to get to the bottom of that black texture issue before I make another release. In the meantime, you may want to switch to 1.0.2-SNAPSHOT from https://oss.sonatype.org/content/repositories/snapshots. At least it fixes the problem with the natives on old Android devices. And there's a chance it could also work on your API 19 device (even though it's not officially supported by NDK).

Peter-Warlock commented 6 months ago

Seems, like some problem with a SNAPSHOT. After updating to 1.0.2-SNAPSHOT I got build.gradle sync error:

Could not find basisu-wrapper-1.0.2-SNAPSHOT-sources.jar (com.crashinvaders.basisu:basisu-wrapper:1.0.2-SNAPSHOT:20231108.211447-2).
Searched in the following locations:
    https://oss.sonatype.org/content/repositories/snapshots/com/crashinvaders/basisu/basisu-wrapper/1.0.2-SNAPSHOT/basisu-wrapper-1.0.2-20231108.211447-2-sources.jar

I also uses HTML project and need this file. But ok. Just for test I commented this line and build.gradle sync completed with success. But even Desktop App craches with error: Caused by: java.lang.NoClassDefFoundError: com/crashinvaders/basisu/wrapper/BasisuWrapper Caused by: java.lang.ClassNotFoundException: com.crashinvaders.basisu.wrapper.BasisuWrapper

Android version also crashes with that error message:

java.lang.NoClassDefFoundError: Failed resolution of: Lcom/crashinvaders/basisu/wrapper/BasisuWrapper;
at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:43)
at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:32)
at com.crashinvaders.basisu.gdx.BasisuTextureData.prepare(BasisuTextureData.java:137)
at com.badlogic.gdx.graphics.Texture.load(Texture.java:156)
....
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.crashinvaders.basisu.wrapper.BasisuWrapper" on path: DexPathList[...]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:43) 
at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:32) 
at com.crashinvaders.basisu.gdx.BasisuTextureData.prepare(BasisuTextureData.java:137) 
at com.badlogic.gdx.graphics.Texture.load(Texture.java:156) 
metaphore commented 6 months ago

Yep, sorry about that. Something is wrong with the snapshot publishing routine. I investigated it a little today and it seems that all the release artifacts get uploaded to Sonatype just fine, except for the main .jar file. Don't know yet what causes the issue, but there's a similar issue with my other projects. Might be something is wrong with the configuration. I'll keep digging...

metaphore commented 6 months ago

Ok, the snapshot should be fixed now. @Peter-Warlock please try again. In case Gradle is stuck with the old dependencies, try this:

./gradlew --refresh-dependencies
Peter-Warlock commented 6 months ago

Tested on the emulator API 34, 23, 22, 21 - worked well.

On the emulator API 19 it crashes with the error:

FATAL EXCEPTION: GLThread 142
Process: com.test, PID: 2921
com.badlogic.gdx.utils.SharedLibraryLoadRuntimeException: Couldn't load shared library 'gdx-basis-universal' for target: Android
    at com.badlogic.gdx.utils.SharedLibraryLoader.load(SharedLibraryLoader.java:128)
    at com.crashinvaders.basisu.gdx.BasisuNativeLibLoader.loadIfNeeded(BasisuNativeLibLoader.java:23)
    at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:39)
    at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:32)
    at com.crashinvaders.basisu.gdx.BasisuTextureData.prepare(BasisuTextureData.java:137)
    at com.badlogic.gdx.graphics.Texture.load(Texture.java:156)

Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "android_set_abort_message" referenced by "libgdx-basis-universal.so"...
    at java.lang.Runtime.loadLibrary(Runtime.java:364)
    at java.lang.System.loadLibrary(System.java:526)
    at com.badlogic.gdx.utils.SharedLibraryLoader.load(SharedLibraryLoader.java:122)
    at com.crashinvaders.basisu.gdx.BasisuNativeLibLoader.loadIfNeeded(BasisuNativeLibLoader.java:23) 
    at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:39) 
    at com.crashinvaders.basisu.gdx.BasisuData.<init>(BasisuData.java:32) 
    at com.crashinvaders.basisu.gdx.BasisuTextureData.prepare(BasisuTextureData.java:137) 

On the real device with API 19 it crashes with the same error but with different "caused by" reason:

g2.b0: Couldn't load shared library 'gdx-basis-universal' for target: Android
    at g2.c0.d(SourceFile:123)
    at l2.d.L(SourceFile:23)

Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "wcstoull" referenced by "libgdx-basis-universal.so"...
    at java.lang.Runtime.loadLibrary(Runtime.java:364)
    at java.lang.System.loadLibrary(System.java:526)
Peter-Warlock commented 6 months ago

Another question about "solid black textures". Is this a result of using mipmapping or some other problem that may occur on the user's devices?

And since I mentioned mipmapping, what about mipmapping? Is there a way to use it?

metaphore commented 6 months ago

Yep, I would expect the library to work on anything below Android API 21 as this is the min supported platform for the current NDK.

And regarding the mipmaps, they are fully supported by basis universal, it's just a question of implementation. Currently a basis/ktx2 texture gets loaded only with 0 level mipmap. It's done for simplicity sake, because for every mipmap level it would require another iteration of transcoding and I'm not sure how to handle that well. I'll look into that for the next release.

Peter-Warlock commented 6 months ago

So, "solid black textures" from your screenshot above is a result of using mipmapping or some other problem that may occur on the user's devices?

metaphore commented 6 months ago

I put everything that I know till this day to this issue #3 In short, it has nothing to do with mipmaps. For some reason emulators (up to Android 8) spit an OpenGL error when I try to load a ASTC texture to GPU, however OpenGL API clearly indicates that it has support for ASTC. That might be only an emulator related issue, I don't have any API 21-27 real device to test at the moment. In the other hand, this is only related to ASTC, whereas the same basis texture transcoded to ETC2 works with no problems. So in any case an extra IF could be added to the transcoder texture type selector, to always use ETC2 over ASTC on the old Android devices. But I'd prefer to investigate further on that and find what's the actual problem is.

Peter-Warlock commented 6 months ago

Hmm. This is interesting. Does this issue occur in both hardware and software emulation mode?

image

metaphore commented 6 months ago

It does, I don't think it makes any difference.

Peter-Warlock commented 6 months ago

You said: That might be only an emulator related issue, I don't have any API 21-27 real device to test at the moment. Different emulator graphics implementations may behave differently.

I have real device with Android API 24 (Android 7.0). I can run some test if you need.

metaphore commented 6 months ago

Well I mean API 24 real device would be perfect to see if this is only an emulator-related behavior! I'd appreciate it if you could try.

The options are:

  1. Clone the project, assemble and install the demo on an Android device locally:
    ./gradlew demo:android:installDebug
  2. Or you can download this zipped APK, unpack it and install manually.
Peter-Warlock commented 6 months ago

Done. It seems to work without problems. Sometimes, after rotating the image, some textures (upper left) lost detail and upper centered texture got some black line at bottom. I don't know if this is normal or not. So I've archived two screenshots showing this difference. It was not on every rotation. I made about 20+ screens at all and sometime it was, sometimes not. Maybe it's just result of rotation (not exact angle because of float vals).

I also archived the logs from Logcat.

Here the screen of my Logcat with API level of tested device: image

Here your screenshot: image

Logcat log: bususu-demo_log.zip

2 screens showing difference: Screenshot_2023-11-12_com.crashinvaders.basisu.demo.zip

metaphore commented 6 months ago

Awesome, thanks! Sounds like the issue might be not all that critical and it should be safe to move on. I'll keep the #3 open so whoever stumbles upon a similar issue may add details.

Peter-Warlock commented 6 months ago

Just for info. For testing, I downloaded your zipped APK and run it.

Peter-Warlock commented 5 months ago

And regarding the mipmaps, they are fully supported by basis universal, it's just a question of implementation. Currently a basis/ktx2 texture gets loaded only with 0 level mipmap. It's done for simplicity sake, because for every mipmap level it would require another iteration of transcoding and I'm not sure how to handle that well. I'll look into that for the next release.

Hi,

Another question about mipmapping. :) As far as I can see, libgdx uses MipMapGenerator.generateMipMap() to generate all mipmaps for textures. Maybe it can be used to create mipmaps as a quick solution for Basisu\ktx2 textures without another iteration of transcoding?

Basisu\ktx2 - Looks good and I like that the texture takes up so little space, but without mipmaps some textures don't look as good.

metaphore commented 5 months ago

Yep, after a quick googling, it sounds pretty reasonable actually, since that should be the usual way to set up mipmaps for OpenGL. I wonder why would Basis include mipmap generator if mipmaps could be simply created on the fly by a rendering API? I'll give it a spin.

PS: Next time please open a new issue if the topic is unrelated 🙏