본문 바로가기
Opengles 3.0 with Android

Chapter 1.0 EGL Context 생성하여 GLSurfaceView 에 적용하기

by SimonLee 2024. 10. 10.

EGL 코드와 렌더링이 정상적으로 될수 있는지를 확인 하기 위하여

Opengl Version, Renderer, Vendor, Extension 정보를 출력해본다.

void printOpenGLESInfo(){
    printGLString("Version",	GL_VERSION);
    printGLString("Vendor",		GL_VENDOR);
    printGLString("Renderer",	GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);
    printGLString("GL Shading Language", GL_SHADING_LANGUAGE_VERSION);
}
static void printGLString(const char *name, GLenum s) {
    LOGI("GL %s = %s\n", name, (const char *) glGetString(s));
}

 

이제 EGL Context를 생성해보자.

GPU에서 지원받는 EGL Config 배열을 전달 받고, 원하는 config를 선택해야 한다.

선택한 config로 EGL Context를 생성해야 한다.

EGL Context 생성 함수를 EGLContextFactory를 상속받은 클래스에 override 구현을 해야 한다.

 

1. GLSurfaceView.EGLContextFactory 클래스를 상속받는 클래스를 생성한다.

- createContext(), destroyContext() 함수를 오버라이드 한다.

- createContext 함수에서는 EGLContext를 생성하여 리턴한다.

- destroyContext 함수는 파라메터로 전달받은 EGLContext를 파괴한다.

- 위 객체를 setEGLContextFactory( ) 함수 파라메터로 넣어줘야 context 생성.

 

2. GLSurfaceView.EGLConfigChooser 클래스를 상속 받는 클래스를 생성한다.

chooseConfig() 함수를 오버 라이드 한다.

- config type 들 지원되는 생상 범위와 depth, stencil, 렌더 타입을 지정한다.

- setEGLConfigChooser( ) 파라메터에 EGLConfigShooser 상속 받은 클래스 객체를 넣어주어야

위에서 실행한 createContext 함수 내부 파라메터로 context를 전달 받는다.

 

 

아래 코드에서는 gles 3.0으로 설정한 코드이다.

private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
    private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; // 이건 고정인가봐
    public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
        checkEglError("Before eglCreateContext", egl);

        int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, (int)glVersion, EGL10.EGL_NONE };
        EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);

        checkEglError("After eglCreateContext", egl);
        return context;// returns null if 3.0 is not supported;
    }

    public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
        egl.eglDestroyContext(display, context);
    }
}

createContext 함수에서 eglCreateContext() 사용하여 context를 리턴한다.

EGL_CONTEXT_CLIENT_VERSION을 0x3098로 해주지 않으면

eglCreateContext: EGL error: 0x3004 에러 가 발생한다.

 

gl 3.0 버전을 사용하기 위해서는 attrib_list 두번째 인자 glversion을 3으로 놓고

egl attribute의 EGL_RENDERABLE_TYPE도 0x40으로 설정을 해주어야 한다.


EGL_RENDERABLE_TYPE

- 비트 플래그로 여러 값을 조합하여 사용할 수 있으며, 각 값은 지원되는 그래픽 API를 나타냅니다.

  • EGL_OPENGL_ES_BIT (0x0001): OpenGL ES 1.x 버전을 지원하는 설정을 의미합니다.
  • EGL_OPENGL_ES2_BIT (0x0004) : OpenGL ES 2.0을 지원하는 설정을 선택할 때 사용됩니다. OpenGL ES 2.0 컨텍스트를 생성할 수 있습니다.
  • EGL_OPENGL_ES3_BIT_KHR (0x0040): OpenGL ES 3.x 버전을 지원하는 설정을 의미합니다. OpenGL ES 3.x 컨텍스트를 생성할 수 있습니다.
  • EGL_OPENVG_BIT (0x0002): OpenVG API를 지원하는 설정을 나타냅니다.
  • EGL_OPENGL_BIT (0x0008): OpenGL을 지원하는 설정을 의미합니다. (일반적으로 OpenGL이 모바일 기기에서는 잘 사용되지 않지만, 일부 장치에서는 지원합니다.)

렌더 타입에서 설정한 opengl 버전과 eglCreateContext attrib 에서 설정한 version과 같아야 한다.!

그렇지 않은 경우 EGL_NO_CONTEXT를 null을 리턴할 수 있다.

 private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
        public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
            mRedSize = r;
            mGreenSize = g;
            mBlueSize = b;
            mAlphaSize = a;
            mDepthSize = depth;
            mStencilSize = stencil;
        }

        private static int EGL_OPENGL_ES2_BIT = 0x4;
        private static int EGL_OPENGL_ES3_BIT_KHR = 0x40;
        private static int[] s_configAttribs =
        {
                EGL10.EGL_RED_SIZE, 4,
                EGL10.EGL_GREEN_SIZE, 4,
                EGL10.EGL_BLUE_SIZE, 4,
                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
                EGL10.EGL_NONE
        };

        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
            int[] num_config = new int[1];
            egl.eglChooseConfig(display, s_configAttribs, null, 0, num_config);
            int numConfigs = num_config[0];
            if (numConfigs <= 0) {
                throw new IllegalArgumentException("No configs match configSpec");
            }

            /* Allocate then read the array of minimally matching EGL configs
             */
            EGLConfig[] configs = new EGLConfig[numConfigs];
            egl.eglChooseConfig(display, s_configAttribs, configs, numConfigs, num_config);

            int[] renderableType = new int[1];
            EGLConfig config = configs[0];
            printConfig(egl, display, config);

            EGLConfig newConfig = chooseConfig(egl, display, configs);
            if (newConfig != null) config = newConfig;
            return config;
        }

        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
                                      EGLConfig[] configs) {
            for(EGLConfig config : configs) {
                int d = findConfigAttrib(egl, display, config,
                        EGL10.EGL_DEPTH_SIZE, 0);
                int s = findConfigAttrib(egl, display, config,
                        EGL10.EGL_STENCIL_SIZE, 0);

                // We need at least mDepthSize and mStencilSize bits
                if (d < mDepthSize || s < mStencilSize)
                    continue;

                // We want an *exact* match for red/green/blue/alpha
                int r = findConfigAttrib(egl, display, config,
                        EGL10.EGL_RED_SIZE, 0);
                int g = findConfigAttrib(egl, display, config,
                        EGL10.EGL_GREEN_SIZE, 0);
                int b = findConfigAttrib(egl, display, config,
                        EGL10.EGL_BLUE_SIZE, 0);
                int a = findConfigAttrib(egl, display, config,
                        EGL10.EGL_ALPHA_SIZE, 0);

                if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
                    return config;
            }
            return null;
        }
 }

 

정리하면

GPU에서 지원되는 EGL Configs를 배열 형태로 리턴 받아서 원하는 Config를 선택해서 

선택을 해주면 된다.

선택한 EGL Config 사용하여 EGL Context를 생성한다.

 

추가로 

지원되는 configs의 값을 확인하기 위하여 eglGetConfigAttrib() 사용 한다.

private void printConfig(EGL10 egl, EGLDisplay display,
                         EGLConfig config) {
    int[] attributes = {
            EGL10.EGL_BUFFER_SIZE,
            EGL10.EGL_ALPHA_SIZE,
            EGL10.EGL_BLUE_SIZE,
            EGL10.EGL_GREEN_SIZE,
            EGL10.EGL_RED_SIZE,
            EGL10.EGL_DEPTH_SIZE,
            EGL10.EGL_STENCIL_SIZE,
            EGL10.EGL_CONFIG_CAVEAT,
            EGL10.EGL_CONFIG_ID,
            EGL10.EGL_LEVEL,
            EGL10.EGL_MAX_PBUFFER_HEIGHT,
            EGL10.EGL_MAX_PBUFFER_PIXELS,
            EGL10.EGL_MAX_PBUFFER_WIDTH,
            EGL10.EGL_NATIVE_RENDERABLE,
            EGL10.EGL_NATIVE_VISUAL_ID,
            EGL10.EGL_NATIVE_VISUAL_TYPE,
            0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
            EGL10.EGL_SAMPLES,
            EGL10.EGL_SAMPLE_BUFFERS,
            EGL10.EGL_SURFACE_TYPE,
            EGL10.EGL_TRANSPARENT_TYPE,
            EGL10.EGL_TRANSPARENT_RED_VALUE,
            EGL10.EGL_TRANSPARENT_GREEN_VALUE,
            EGL10.EGL_TRANSPARENT_BLUE_VALUE,
            0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
            0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
            0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
            0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
            EGL10.EGL_LUMINANCE_SIZE,
            EGL10.EGL_ALPHA_MASK_SIZE,
            EGL10.EGL_COLOR_BUFFER_TYPE,
            EGL10.EGL_RENDERABLE_TYPE,
            0x3042 // EGL10.EGL_CONFORMANT
    };
    String[] names = {
            "EGL_BUFFER_SIZE",
            "EGL_ALPHA_SIZE",
            "EGL_BLUE_SIZE",
            "EGL_GREEN_SIZE",
            "EGL_RED_SIZE",
            "EGL_DEPTH_SIZE",
            "EGL_STENCIL_SIZE",
            "EGL_CONFIG_CAVEAT",
            "EGL_CONFIG_ID",
            "EGL_LEVEL",
            "EGL_MAX_PBUFFER_HEIGHT",
            "EGL_MAX_PBUFFER_PIXELS",
            "EGL_MAX_PBUFFER_WIDTH",
            "EGL_NATIVE_RENDERABLE",
            "EGL_NATIVE_VISUAL_ID",
            "EGL_NATIVE_VISUAL_TYPE",
            "EGL_PRESERVED_RESOURCES",
            "EGL_SAMPLES",
            "EGL_SAMPLE_BUFFERS",
            "EGL_SURFACE_TYPE",
            "EGL_TRANSPARENT_TYPE",
            "EGL_TRANSPARENT_RED_VALUE",
            "EGL_TRANSPARENT_GREEN_VALUE",
            "EGL_TRANSPARENT_BLUE_VALUE",
            "EGL_BIND_TO_TEXTURE_RGB",
            "EGL_BIND_TO_TEXTURE_RGBA",
            "EGL_MIN_SWAP_INTERVAL",
            "EGL_MAX_SWAP_INTERVAL",
            "EGL_LUMINANCE_SIZE",
            "EGL_ALPHA_MASK_SIZE",
            "EGL_COLOR_BUFFER_TYPE",
            "EGL_RENDERABLE_TYPE",
            "EGL_CONFORMANT"
    };
    int[] value = new int[1];
    for (int i = 0; i < attributes.length; i++) {
        int attribute = attributes[i];
        String name = names[i];
        if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
            Log.w(TAG, String.format("  %s: %d\n", name, value[0]));
        } else {
            // Log.w(TAG, String.format("  %s: failed\n", name));
            while (egl.eglGetError() != EGL10.EGL_SUCCESS);
        }
    }
}