in source/SkiaSharp.Views/SkiaSharp.Views/Platform/Android/GLTextureView.cs [770:1074]
private void GuardedRun()
{
eglHelper = new EglHelper(textureViewWeakRef);
haveEglContext = false;
haveEglSurface = false;
try
{
IGL10 gl = null;
var createEglContext = false;
var createEglSurface = false;
var createGlInterface = false;
var lostEglContext = false;
var sizeChanged = false;
var wantRenderNotification = false;
var doRenderNotification = false;
var askedToReleaseEglContext = false;
var w = 0;
var h = 0;
Java.Lang.IRunnable ev = null;
while (true)
{
lock (threadManager)
{
while (true)
{
if (shouldExit)
{
return;
}
if (eventQueue.Count > 0)
{
ev = eventQueue.Dequeue();
break;
}
// Update the pause state.
var pausing = false;
if (paused != requestPaused)
{
pausing = requestPaused;
paused = requestPaused;
Monitor.PulseAll(threadManager);
LogDebug($"[GLThread {Id}] paused is now {paused}");
}
// Do we need to give up the EGL context?
if (shouldReleaseEglContext)
{
LogDebug($"[GLThread {Id}] Releasing EGL context because asked to");
StopEglSurfaceLocked();
StopEglContextLocked();
shouldReleaseEglContext = false;
askedToReleaseEglContext = true;
}
// Have we lost the EGL context?
if (lostEglContext)
{
StopEglSurfaceLocked();
StopEglContextLocked();
lostEglContext = false;
}
// When pausing, release the EGL surface:
if (pausing && haveEglSurface)
{
LogDebug($"[GLThread {Id}] Releasing EGL surface because paused");
StopEglSurfaceLocked();
}
// When pausing, optionally release the EGL Context:
if (pausing && haveEglContext)
{
textureViewWeakRef.TryGetTarget(out GLTextureView view);
var preserveEglContextOnPause = view == null ? false : view.PreserveEGLContextOnPause;
if (!preserveEglContextOnPause || threadManager.ShouldReleaseEGLContextWhenPausing())
{
StopEglContextLocked();
LogDebug($"[GLThread {Id}] Releasing EGL context because paused");
}
}
// When pausing, optionally terminate EGL:
if (pausing)
{
if (threadManager.ShouldTerminateEGLWhenPausing())
{
eglHelper.Finish();
LogDebug($"[GLThread {Id}] Terminating EGL because paused");
}
}
// Have we lost the TextureView surface?
if ((!hasSurface) && (!waitingForSurface))
{
LogDebug($"[GLThread {Id}] Noticed TextureView surface lost");
if (haveEglSurface)
{
StopEglSurfaceLocked();
}
waitingForSurface = true;
surfaceIsBad = false;
Monitor.PulseAll(threadManager);
}
// Have we acquired the surface view surface?
if (hasSurface && waitingForSurface)
{
LogDebug($"[GLThread {Id}] Noticed TextureView surface acquired");
waitingForSurface = false;
Monitor.PulseAll(threadManager);
}
if (doRenderNotification)
{
LogDebug($"[GLThread {Id}] Sending render notification");
wantRenderNotification = false;
doRenderNotification = false;
renderComplete = true;
Monitor.PulseAll(threadManager);
}
// Ready to draw?
if (IsReadyToDraw())
{
// If we don't have an EGL context, try to acquire one.
if (!haveEglContext)
{
if (askedToReleaseEglContext)
{
askedToReleaseEglContext = false;
}
else if (threadManager.TryAcquireEglContextLocked(this))
{
try
{
eglHelper.Start();
}
catch (Exception)
{
threadManager.ReleaseEglContextLocked(this);
throw;
}
haveEglContext = true;
createEglContext = true;
Monitor.PulseAll(threadManager);
}
}
if (haveEglContext && !haveEglSurface)
{
haveEglSurface = true;
createEglSurface = true;
createGlInterface = true;
sizeChanged = true;
}
if (haveEglSurface)
{
if (surfaceSizeChanged)
{
sizeChanged = true;
w = width;
h = height;
wantRenderNotification = true;
LogDebug($"[GLThread {Id}] Noticing that we want render notification");
// Destroy and recreate the EGL surface.
createEglSurface = true;
surfaceSizeChanged = false;
}
requestRender = false;
Monitor.PulseAll(threadManager);
break;
}
}
LogDebug($"[GLThread {Id}] Waiting mHaveEglContext={haveEglContext} mHaveEglSurface={haveEglSurface} mFinishedCreatingEglSurface={finishedCreatingEglSurface} paused={paused} hasSurface={hasSurface} surfaceIsBad={surfaceIsBad} mWaitingForSurface={waitingForSurface} mWidth={width} mHeight={height} mRequestRender={requestRender} mRenderMode={renderMode}");
// By design, this is the only place in a GLThread thread where we Wait().
Monitor.Wait(threadManager);
}
} // end of lock(sGLThreadManager)
if (ev != null)
{
ev.Run();
ev = null;
continue;
}
if (createEglSurface)
{
LogDebug($"[GLThread {Id}] EGL create surface");
if (eglHelper.CreateSurface())
{
lock (threadManager)
{
finishedCreatingEglSurface = true;
Monitor.PulseAll(threadManager);
}
}
else
{
lock (threadManager)
{
finishedCreatingEglSurface = true;
surfaceIsBad = true;
Monitor.PulseAll(threadManager);
}
continue;
}
createEglSurface = false;
}
if (createGlInterface)
{
gl = eglHelper.CreateGL().JavaCast<IGL10>();
threadManager.CheckGLDriver(gl);
createGlInterface = false;
}
if (createEglContext)
{
LogDebug($"[GLThread {Id}] OnSurfaceCreated");
if (textureViewWeakRef.TryGetTarget(out GLTextureView view))
{
view.renderer.OnSurfaceCreated(gl, eglHelper.EglConfig);
}
createEglContext = false;
}
if (sizeChanged)
{
LogDebug($"[GLThread {Id}] OnSurfaceChanged({w}, {h})");
if (textureViewWeakRef.TryGetTarget(out GLTextureView view))
{
view.renderer.OnSurfaceChanged(gl, w, h);
}
sizeChanged = false;
}
{
LogDebug($"[GLThread {Id}] OnDrawFrame");
if (textureViewWeakRef.TryGetTarget(out GLTextureView view))
{
view.renderer.OnDrawFrame(gl);
}
}
var swapError = eglHelper.Swap();
switch (swapError)
{
case EGL10.EglSuccess:
break;
case EGL11.EglContextLost:
LogDebug($"[GLThread {Id}] EGL context lost");
lostEglContext = true;
break;
default:
// Other errors typically mean that the current surface is bad,
// probably because the TextureView surface has been destroyed,
// but we haven't been notified yet.
LogError($"[GLThread {Id}] eglSwapBuffers failed: {swapError}");
lock (threadManager)
{
surfaceIsBad = true;
Monitor.PulseAll(threadManager);
}
break;
}
if (wantRenderNotification)
{
doRenderNotification = true;
}
}
}
finally
{
lock (threadManager)
{
StopEglSurfaceLocked();
StopEglContextLocked();
}
}
}