Blog

GLSurfaceView 커밋 읽기 #3

December 28, 2013

GLSurfaceView 커밋 읽기 #3

오늘 살펴볼 커밋도 Jack Palevich의 커밋입니다.

commit ea62b95e7c562e8d0052441d8a0d7de6d919320f
Author: Jack Palevich 
Date:   Mon Mar 18 22:44:58 2013 -0700

    Fix createSurface / eglCreateWindowSurface race.
    
    Previously we could have returned from createSurface on the main thread
    before calling eglCreateWindowSurface on the GLThread. That could lead
    to a problem because the surface could be destroyed before
    eglCreateWindowSurface got a chance to run.
    
    Bug: 8328715
    Change-Id: I273149f7d4b165abbe218a91ff54083f3f498cb0

이 패치를 보기 이전에 간단히 배경 설명을 드리겠습니다.

GLSurfaceView는 내부에 GLThread를 가지고 있고 이 스레드는 내부적으로 이중 무한 루프를 돌리도록 구성되어 있습니다. 이 무한 루프들은 CPU 자원을 적절히 소모히기 위해 notifyll과 wait를 통해 자원을 통제하고 양보하도록 되어있습니다.

무한 루프 내부의 무한 루프가 다른 작업을 하기 전에 먼저 돌게 되는데요. 외부에서 종료를 원할 때, 처리할 이벤트가 있을 때, 그릴 준비가 완료되었을 때 밖으로 나갑니다. 진짜 그릴 수 있을 때만 내부의 이중 루프를 벗어나 단일 루프가 반복되며 그림을 그리는 것이지요.

GLSurfaceView가 그려지기 위해서는 문맥을 담은 EGLContext와 그릴 평면인 EGLSurface와 그려질 EglDisplay 등이 필요합니다. EGLSurface는 GLSurfaceView에서 오래된 골치거리였던 것 같습니다.

@@ -1445,6 +1445,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
                                 Log.i("GLThread", "waiting tid=" + getId()
                                     + " mHaveEglContext: " + mHaveEglContext
                                     + " mHaveEglSurface: " + mHaveEglSurface
+                                    + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
                                     + " mPaused: " + mPaused
                                     + " mHasSurface: " + mHasSurface
                                     + " mSurfaceIsBad: " + mSurfaceIsBad

로그를 추가했네요. 넘어갑시다.

@@ -1468,8 +1469,14 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
                         if (LOG_SURFACE) {
                             Log.w("GLThread", "egl createSurface");
                         }
-                        if (!mEglHelper.createSurface()) {
+                        if (mEglHelper.createSurface()) {
                             synchronized(sGLThreadManager) {
+                                mFinishedCreatingEglSurface = true;
+                                sGLThreadManager.notifyAll();
+                            }
+                        } else {
+                            synchronized(sGLThreadManager) {
+                                mFinishedCreatingEglSurface = true;
                                 mSurfaceIsBad = true;
                                 sGLThreadManager.notifyAll();
                             }

이전에는 mEglHelper.createSurface가 실패했을 때만 mSurfaceIsBad를 true로 체크하고 notifyAll을 했더 것을 볼 수 있습니다. mEglHelper.createSurface는 EGLSurface를 얻어옵니다.

SurfaceIsBad가 true인 경우에는 readyToDraw 메서드가 실패하고 렌더링을 취소합니다. 이전과의 차이는 성공했을 때도 sGLThreadManager.notifyAll()을 호출 하여 sGLThreadManager.wait()를 호출하고 있던 스레드를 깨웁니다.

그럼 sGLThreadManager.wait()를 호출하고 잠드는 메서드에 무언가 변화가 있을 것입니다. 아래를 보세요.

@@ -1595,8 +1602,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
                     Log.i("GLThread", "surfaceCreated tid=" + getId());
                 }
                 mHasSurface = true;
+                mFinishedCreatingEglSurface = false;
                 sGLThreadManager.notifyAll();
-                while((mWaitingForSurface) && (!mExited)) {
+                while (mWaitingForSurface
+                       && !mFinishedCreatingEglSurface
+                       && !mExited) {
                     try {
                         sGLThreadManager.wait();
                     } catch (InterruptedException e) {

위의 코드는 GLThread의 surfaceCreated() 메서드입니다. 해당 메서드는 GLSurfaceView 내부의 surfaceCreated에 의해 호출되죠. mFinishedCreatingEglSurface가 추가되었습니다. mFinishedCreatingEglSurface가 true가 될 때까지 기다리게 변경된 코드입니다.

변경된 코드에서는 EGLSurface가 생성되거나 실패되는게 확실해져야 surfaceCreated가 종료됩니다. !mExited가 있기 때문에 객체가 종료되는 경우에도 종료될 것으로 볼 수 있습니다.

이전까지는 EGLSurface의 생성이 처리되지 않아도 surfaceCreated가 호출되었음을 예상해 볼 수 있습니다. 주석을 보면 EGLSurface가 생성되기전에 surface가 파괴되는 경우에 문제가 되는 것을 알 수 있습니다. surface는 GLSurfaceView가 가지고 있던 것이라고 보시면 되는데 쉽게 보아서 GLSurfaceView가 종료되었다고 생각하시면 됩니다.

@@ -1735,6 +1745,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
         private boolean mWaitingForSurface;
         private boolean mHaveEglContext;
         private boolean mHaveEglSurface;
+        private boolean mFinishedCreatingEglSurface;
         private boolean mShouldReleaseEglContext;
         private int mWidth;
         private int mHeight;

mFinishedCreatingEglSurface를 사용하기 위해 선언한 부분입니다.

같이 읽기