Skip to content

fix(🤖): find Skia's Dawn instance in librnskia.so on Android - #582

Merged
wcandillon merged 2 commits into
wcandillon:mainfrom
enzomanuelmangano:android-find-skia-instance
Oct 21, 2026
Merged

wcandillon merged 2 commits into
wcandillon:mainfrom
enzomanuelmangano:android-find-skia-instance

Conversation

@enzomanuelmangano

@enzomanuelmangano enzomanuelmangano commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Note

Written by Claude Opus 5.5 on behalf of Enzo (@enzomanuelmangano).

Fixes #581.

Problem

On Android, importDevice(Skia.getNativeDevice()) returns a device whose async work never settles (mapAsync stays pending). createInstance() looks Skia's getter up with dlsym(RTLD_DEFAULT, "rnskia_getWGPUInstance"). System.loadLibrary loads librnskia.so with RTLD_LOCAL, so the global lookup does not see it, even though librnskia.so exports the symbol. react-native-webgpu then creates its own instance and never pumps Skia's device.

Fix

On Android, when the global lookup fails, createInstance() asks for the handle of librnskia.so with dlopen("librnskia.so", RTLD_NOW | RTLD_NOLOAD) and looks the getter up on that handle.

The ordering requirement does not change: as with the existing dlsym, Skia must be loaded before the first GPU object is created.

Alternative considered: dl_iterate_phdr over all loaded objects. It finds the library without knowing its name, but it is more code and walks every loaded library to find one known soname.

Test plan

Reproduction app: https://github.com/enzomanuelmangano/repro-lab, demo webgpu-skia-shared-instance. Its fix/webgpu-skia-shared-instance-android branch applies this change to 0.17.0 (diff). Android API 36 emulator (arm64), release build, react-native-skia 3.7.2.

Before After
mapAsync on Skia's imported device never settles (3 s timeout) settles in 58 ms
Before After
before after

Video, Redmi Note 8 Pro, release build (left: 0.17.0, stays at "Running…"; right: with the fix, settles in 16 ms): https://github.com/enzomanuelmangano/repro-lab/blob/main/media/webgpu-skia-shared-instance-android.mp4

Not run: the repository's E2E suite (yarn test) and the example app. The change only adds a fallback when the existing lookup returns nullptr.

System.loadLibrary loads librnskia.so with RTLD_LOCAL, so
dlsym(RTLD_DEFAULT, "rnskia_getWGPUInstance") never finds it and
react-native-webgpu creates its own instance. Look the getter up in
librnskia.so with RTLD_NOLOAD when the global lookup fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@camchis

camchis commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Confirming this on a device. I hit the same bug and patched it the same way, dlopen("librnskia.so", RTLD_NOW | RTLD_NOLOAD) then dlsym on that handle, on react-native-webgpu 0.13.0 with react-native-skia 3.3.0 (Graphite). On a Seeker (Android, Vulkan), mapAsync on buffers of a device imported with importDevice(Skia.getNativeDevice()) never settled before the change and settles normally after it. Without it WebGPU makes its own instance, and an app that mixes Skia and WebGPU waits forever on the Skia device's work. Looks good to me.

@wcandillon

Copy link
Copy Markdown
Owner

this is correct, wouldn't be cleaner for this to be in WebGPUModule.java,?

@enzomanuelmangano

Copy link
Copy Markdown
Contributor Author

Note

Written by Claude Opus 5.5 on behalf of Enzo (@enzomanuelmangano).

Thanks a lot, and thanks @camchis for confirming on a Seeker!

Good question. I tested it on a Redmi Note 8 Pro (release build, mapAsync on a buffer of importDevice(Skia.getNativeDevice())):

Build mapAsync
0.17.0 never settles
System.loadLibrary("rnskia") before react-native-webgpu in WebGPUModule.java, no native change never settles
RTLD_NOLOAD lookup in C++ only (this PR) settles in 17 ms
both settles in 17 ms

So Java is a great place for the loading order you mentioned, and it would make this deterministic, but it can't replace the lookup: System.loadLibrary loads librnskia.so with RTLD_LOCAL, and that visibility can't be changed from Java, so dlsym(RTLD_DEFAULT, …) still misses the symbol. The only all-Java alternative I can think of is asking Skia's Java module for the instance pointer, which would couple the two packages.

If you'd like, I'm happy to add the loadLibrary("rnskia") ordering to WebGPUModule.java in this PR as well, or leave it to your follow-up. Whatever you prefer!

@wcandillon
wcandillon self-requested a review October 21, 2026 19:19
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.

Android: dlsym(RTLD_DEFAULT) never finds rnskia_getWGPUInstance, so Skia's imported device never settles async work

3 participants