azesmway/react-native-unity

After calling the unload method, black screen is shown on android

Opened this issue · 11 comments

Hello!
Thanks for maintaining this library!

I have noticed this kind of behavior on android:
Open example app > Open screen with unity scene > Unload & Go back > Open screen with unity scene again > Unity scene is black.

Video demo:

DouWan_20221219_143743.mp4

Relevant files:

MacOS: 13.01
The example project is taken from @azesmway's zip file, exported from unity by him, in other issues' comment sections in this repository but this issue happens of project in this repository too.

Does anyone have any idea if the fix for this issue exists?

Same issue, but no need to unloadUnity, just destroying and recreating the component (through navigation) simply lead to blank screen (white screen in my case). I was suspecting if I should manually clean up the old unity stuff so I tried calling unloadUnity before unmount, but it leads to null pointer dereference in vk::CommandBuffer::BindVertexBuffers in libunity.so...

Another information I would to offer is that both onPlayerQuit and onPlayerUnload did not get fired. Not sure if they are related...


Update: previously I was calling unloadUnity in the clean up code of useEffect, so onPlayerUnload did not fire. After moving the call to outside onPlayerUnload did work, but it's still a blank screen after getting back to the screen containing UnityView.


To be precise, I get another null pointer dereference exception when letting UnityView destroy itself:

01-09 18:55:11.321  4485  4485 I SurfaceView: onWindowVisibilityChanged(8) false android.view.SurfaceView{dbf0a7d VFE...... ......ID 0,0-1080,2005 #7f090163 app:id/unitySurfaceView} of ViewRootImpl@cdd1dd5[MainActivity]
01-09 18:55:11.322  4485  4485 I SurfaceView: surfaceDestroyed callback.size 1 #2 android.view.SurfaceView{dbf0a7d VFE...... ......ID 0,0-1080,2005 #7f090163 app:id/unitySurfaceView}
01-09 18:55:11.322  4485  4485 D Unity   : PersistentUnitySurface.preserveContent: android.view.SurfaceView{dbf0a7d VFE...... ......ID 0,0-1080,2005 #7f090163 app:id/unitySurfaceView}
01-09 18:55:11.323  4485  4485 D Unity   : PersistentUnitySurface.PlaceholderView.Copy: android.view.SurfaceView{dbf0a7d VFE...... ......ID 0,0-1080,2005 #7f090163 app:id/unitySurfaceView}
01-09 18:55:11.352  4485  7315 D Unity   : SetWindow 0 0x0
01-09 18:55:11.352  4485  7353 W tamory.petamor: 0xebadde09 skipped times: 0
01-09 18:55:11.353  4485  7353 E CRASH   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-09 18:55:11.353  4485  7353 E CRASH   : Version '2021.3.16f1 (4016570cf34f)', Build type 'Development', Scripting Backend 'il2cpp', CPU 'arm64-v8a'
01-09 18:55:11.353  4485  7353 E CRASH   : Build fingerprint: 'samsung/star2qltezh/star2qltechn:10/QP1A.190711.020/G9650ZHU9FUE3:user/release-keys'
01-09 18:55:11.353  4485  7353 E CRASH   : Revision: '14'
01-09 18:55:11.353  4485  7353 E CRASH   : ABI: 'arm64'
01-09 18:55:11.353  4485  7353 E CRASH   : Timestamp: 2023-01-09 18:55:11+0800
01-09 18:55:11.353  4485  7353 E CRASH   : pid: 4485, tid: 7353, name: Thread-81  >>> com.example <<<
01-09 18:55:11.353  4485  7353 E CRASH   : uid: 10250
01-09 18:55:11.353  4485  7353 E CRASH   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xb9
01-09 18:55:11.353  4485  7353 E CRASH   : Cause: null pointer dereference
01-09 18:55:11.353  4485  7353 E CRASH   :     x0  0000000000000000  x1  0000007510cecba0  x2  00000074965c1380  x3  0000000000000000
01-09 18:55:11.353  4485  7353 E CRASH   :     x4  0000007510cecba0  x5  00000007fe7861cc  x6  0000000000000000  x7  0000000000000005
01-09 18:55:11.354  4485  7353 E CRASH   :     x8  0000000000000000  x9  00000074b367c3dc  x10 00000000ffffffff  x11 0000007510cecbf0
01-09 18:55:11.354  4485  7353 E CRASH   :     x12 0000007510cecba0  x13 0000000000000000  x14 0000000000000000  x15 0000007510cecbe0
01-09 18:55:11.354  4485  7353 E CRASH   :     x16 0000000000000000  x17 00000074965c1380  x18 0000006a344bc000  x19 0000000000000001
01-09 18:55:11.354  4485  7353 E CRASH   :     x20 0000000000000001  x21 0000006a90bc9780  x22 0000006a90bd0c30  x23 0000006a90bda380
01-09 18:55:11.354  4485  7353 E CRASH   :     x24 0000000000000001  x25 0000006a90bd0c30  x26 0000006a90bda380  x27 0000006b7b7fc020
01-09 18:55:11.354  4485  7353 E CRASH   :     x28 0000006a200000c0  x29 0000006b7b7fbba0
01-09 18:55:11.354  4485  7353 E CRASH   :     sp  0000006b7b7fb880  lr  0000006b4ec8a334  pc  0000006b4ec8a34c
01-09 18:55:11.354  4485  7353 E CRASH   : 
01-09 18:55:11.354  4485  7353 E CRASH   : backtrace:
01-09 18:55:11.354  4485  7353 E CRASH   :       #00 pc 000000000088534c  /data/app/com.example-P4RgAXnLb3uA7t88jr3TVg==/lib/arm64/libunity.so (GfxDeviceVKBase::EnsureCurrentCommandBuffer(vk::CommandBuffer::Type, bool)+340) (BuildId: 9f7c3d6ea46a437a3f214a16d511bbb3bf2131fa)
01-09 18:55:11.354  4485  7353 E CRASH   :       #01 pc 0000000000880b3c  /data/app/com.example-P4RgAXnLb3uA7t88jr3TVg==/lib/arm64/libunity.so (GfxDeviceVK::ClearImpl(GfxClearFlags, ColorRGBAf const*, int, unsigned int, float, unsigned int)+368) (BuildId: 9f7c3d6ea46a437a3f214a16d511bbb3bf2131fa)
01-09 18:55:11.354  4485  7353 E CRASH   :       #02 pc 000000000095c6a4  /data/app/com.example-P4RgAXnLb3uA7t88jr3TVg==/lib/arm64/libunity.so (GfxDeviceWorker::RunCommand(ThreadedStreamBuffer&)+2240) (BuildId: 9f7c3d6ea46a437a3f214a16d511bbb3bf2131fa)
01-09 18:55:11.354  4485  7353 E CRASH   :       #03 pc 0000000000963d74  /data/app/com.example-P4RgAXnLb3uA7t88jr3TVg==/lib/arm64/libunity.so (GfxDeviceWorker::RunExt(ThreadedStreamBuffer&)+44) (BuildId: 9f7c3d6ea46a437a3f214a16d511bbb3bf2131fa)
01-09 18:55:11.354  4485  7353 E CRASH   :       #04 pc 0000000000963d3c  /data/app/com.example-P4RgAXnLb3uA7t88jr3TVg==/lib/arm64/libunity.so (GfxDeviceWorker::Run()+140) (BuildId: 9f7c3d6ea46a437a3f214a16d511bbb3bf2131fa)
01-09 18:55:11.354  4485  7353 E CRASH   :       #05 pc 000000000095bbcc  /data/app/com.example-P4RgAXnLb3uA7t88jr3TVg==/lib/arm64/libunity.so (GfxDeviceWorker::RunGfxDeviceWorker(void*)+4) (BuildId: 9f7c3d6ea46a437a3f214a16d511bbb3bf2131fa)
01-09 18:55:11.354  4485  7353 E CRASH   :       #06 pc 000000000036ab18  /data/app/com.example-P4RgAXnLb3uA7t88jr3TVg==/lib/arm64/libunity.so (Thread::RunThreadWrapper(void*)+796) (BuildId: 9f7c3d6ea46a437a3f214a16d511bbb3bf2131fa)
01-09 18:55:11.354  4485  7353 E CRASH   :       #07 pc 00000000000e6f20  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) (BuildId: d00c50b4798c95f2447d684ed3ea7dcb)
01-09 18:55:11.354  4485  7353 E CRASH   :       #08 pc 00000000000850c8  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: d00c50b4798c95f2447d684ed3ea7dcb)
01-09 18:55:11.365  4485  7364 I SwappyDisplayManager: Terminating looper thread
01-09 18:55:11.369  4485  4485 I SurfaceView: remove() android.view.SurfaceView{dbf0a7d VFE...... ......ID 0,0-1080,2005 #7f090163 app:id/unitySurfaceView} Surface(name=SurfaceView - com.example/com.example.MainActivity@dbf0a7d@0)/@0xad431a3
01-09 18:55:11.372  4485  4485 I SurfaceView: onWindowVisibilityChanged(0) true android.view.SurfaceView{dbf0a7d VFE...... ......ID 0,0-1080,2005 #7f090163 app:id/unitySurfaceView} of ViewRootImpl@cdd1dd5[MainActivity]
01-09 18:55:11.387   861   861 I SurfaceFlinger:      DEVICE | 0x7c28c51af0 | 0000 | RGBA_8888 |   0.0    0.0 1080.0 2220.0 |    0    0 1080 2220 | com.example/com.example.MainActivity$_4485#0
01-09 18:55:11.396  1625  2800 V WindowManager: Relayout 4485: mAttrs={(0,0)(fillxfill) sim={adjust=resize} layoutInDisplayCutoutMode=always ty=BASE_APPLICATION fmt=RGBA_8888 wanim=0x1030305 preferredDisplayMode=1 sysuil=true
01-09 18:55:11.399  4485  4485 I ViewRootImpl@cdd1dd5[MainActivity]: Relayout returned: old=(0,0,1080,2220) new=(0,0,1080,2220) req=(1080,2220)0 dur=8 res=0x1 s={true 501223137280} ch=false
01-09 18:55:11.406  4485  4485 I SurfaceView: surfaceCreated 1 #8 android.view.SurfaceView{dbf0a7d VFE...... ......ID 0,0-1,1 #7f090163 app:id/unitySurfaceView}
01-09 18:55:11.407  4485  7315 D Unity   : SetWindow 0 0x7495e03010
01-09 18:55:11.407  4485  7315 D Unity   : AndroidDisplayManagerVulkan::AttachWindow(0x7495e03010, 0)

I added a loading indicator and wait for 1 second each time I come to my unityView screen.

{loading ? (
        <Loader color={colors.primary} />
      ) : (
        <UnityView
          ref={unityRef}
          style={{flex: 1}}
          onUnityMessage={result => {
            console.log('onUnityMessage:', result.nativeEvent.message);
          }}
        />
      )}

added useEffect focus function of navigation and in that function I made loading true and after one second I made it false and then I can see the unityView instead of black/white screen.

@aawolf1099 Sounds interesting. Would you mind sharing more details about the "useEffect focus function"? Did you mean useFocusEffect?

Okay I found the focus event. I set loading to true inside the listener, and then back to false 1 second later. It is no longer a white screen, but the FPS drops significantly when it is back. Another issue is that it is keeps running in the background because the window is still focused. Attempting to manually pauseUnity on blur and resumeUnity on focus leads to black screen (not white, though).


Okay I tried set loading to false in blur event handler and it does pause Unity.

After looking into the source code, I found androidKeepPlayerMounted is another workaround. It keeps the player mounted so that we can manually pauseUnity on blur and resumeUnity on focus. It does not lead to any null pointer dereference or segment fault because the lifecycle is simpler in this case.

while using androidKeepPlayerMounted, I have been experiencing issues while using react-native-navigation's navigate.reset() when it includes a path that contains unity. #62 has been my issue so far, I am wondering if anyone here has ran into a similar issue or has fixed the problem for their project.

After looking into the source code, I found androidKeepPlayerMounted is another workaround. It keeps the player mounted so that we can manually pauseUnity on blur and resumeUnity on focus. It does not lead to any null pointer dereference or segment fault because the lifecycle is simpler in this case.

Thanks for finding this! It was very helpful. I ended up using the androidKeepPlayerMounted option and useFocusEffect with pauseUnity on blur and resumeUnity on focus and it worked like a charm.

Considering the naming of the option, any Idea if this would work on IOS? I plan on testing it but figured I'd ask first to determine if there was another method for IOS or whether it'd be tested yet.

It seems on iOS the Unity instance is kept alive in the background already (not sure if it is "mounted" or not).

@Luluno01 Thank you. That's good to know concerning iOS.

Although the Android mounting method works great, my use case necessitates that I completely restart my game when a new user logs in. As such, I tried using the unload method along with the delay that @aawolf1099 mentions, but it leaves the game in an undesirable state and trying to rerender the game again crashes the app.

@Luluno01 Thank you. That's good to know concerning iOS.

Although the Android mounting method works great, my use case necessitates that I completely restart my game when a new user logs in. As such, I tried using the unload method along with the delay that @aawolf1099 mentions, but it leaves the game in an undesirable state and trying to rerender the game again crashes the app.

This is perfectly legit requirement. Unfortunately, it seems Unity doesn't implement any interface to restart itself on Android. My current approach is to unload everything from within the game and reload the scene, which helps a bit, but if you are using some Unity plugins or features that touch native code or native resources, it might be insufficient to just reload the scene.

Considering how unstable it is to embed Unity view into React Native, I start to think if not embedding it directly is a better idea. On Android, it is easy to do so. You just add android:process=":unity" to UnityPlayerActivity and use an intent to start a separate Unity activity (communication between React Native and Unity will be another problem). It is then easy to restart the Unity process without killing the entire app. On iOS, however, I haven't found a similar solution where you can start a new task window that runs in a separate process but still belongs to the same app.

Actually I just found another workaround. I found that when there is a black screen, if I send the app to the background and then switch back, it starts to render properly. So, I made some changes (#78) to the binding to expose UnityPlayer.windowFocusChanged, which is invoked under the hood when the app is sending to the background and bringing to the foreground, and it does get the view out of black screen.