Unity-Technologies / com.unity.uiwidgets

UIWidgets is a Unity Package which helps developers to create, debug and deploy efficient, cross-platform Apps.
https://unity.cn/uiwidgets
626 stars 78 forks source link

fix potential crash when spawning UIWidgetsPanels inside the Update of a MonoBehaviour #307

Closed zhuxingwei closed 2 years ago

zhuxingwei commented 2 years ago

In this PR we focus on fixing a bug that leads to crash when spawning UIWidgetsPanels inside the Update function of a MonoBehaviour:

What we fix: When implementing UIWidgets, we heavily rely on two threads on the C++ side: (1) the main thread where the managed code (i.e., C# codes) are executed; (2) the render thread where the flutter engine is called to draw the UI elements.

Usually we cannot mess up with the two threads, e.g., changing stuffs in render thread from the main thread, which may cause undefined behaviors. However, we do such thing when calling "UIWidgetsPanel::OnEnable" where we have to do some initialization stuff, including some critical operations like reset the opengl context which is used in render thread: This operation turns out to be the root cause of the crash issue: Assume in frame N, on the main thread we call onEnable and initialize a new UIWidgetsPanel, then it will emits rendering tasks to the render thread so that the flutter engine will draw the ui elements. Note that these tasks are emited after the rendering progress of frame N so they won't be processed in frame N. Next, in frame N+1, if we create another UIWIdgetsPanel and call its onEnable, it will first reset the current opengl context and then switch to the render thread to fetch the gfx_render_thread_id: At this time, since we have switched to the render thread, the pending rendering tasks in frame N will be also processed. However, since we have reset the opengl context, these tasks are now running with an invalid opengl context, in which the status of the underlying opengl state machine is wrong (e.g., the cached frame buffer is unloaded). Therefore a crash happens.

How we fix: To fix this issue, we just need to guarantee that the operation to change the opengl context must happen after the pending rendering tasks for the previous frame are all finished. So we need to reorder the sequence of "reset the current opengl context" and "switch to render thread".

In this PR, we also add a new sample: UISpawnerTest for the regression tests on this issue