본문 바로가기

프로그래밍/Android

[Android] EGL : 1 - 기본 구성

Grafika , WebRTC EGL 관련 내용들 검토를 위해 정리한 내용으로, 대부분의 소스는 grafika 에서 참조.

일반적인 비디오 전송시에는  크게 신경쓸 필요가 없으나 비디오 합성이나 3D 환경 구성을 위해서는 해당 내용에 대해 파악이 필요. 

 

 

EGL14 - api 17

EGL15 - api 29

 

 

EGL

EGL10 의 경우에는 EGLContext.getEGL(); 메쏘드를 통해 egl 객체를 생성한 뒤에 사용했으나 EGL14의 경우에는 대부분 메쏘드가 static 형태로 변경되었음.

EGL 을 사용하기 위한 기본 설정들로 한번 작성해 놓으면 변경할게 없는 보일러플레이트 코드들이다.

디스플레이, 컨텍스트, 버퍼 서피스 등을 생성한다.

 

Display

EGLDisplay mDisplay = EGL14.EGL_NO_DISPLAY;
mDisplay = createDisplay();

EGLDisplay createDisplay() {
    EGLDisplay eglDisplay = EGL14.eglGetDisplay( EGL14.EGL_DEFAULT_DISPLAY );
    if( eglDisplay == EGL14.EGL_NO_DISPLAY ) {
        throw new RuntimeException( "Unalbe to get EGL10 display");
    }

    int[] version = new int[2];
    if( !EGL14.eglInitialize( eglDisplay, version, 0, version, 1)) {
        throw new RuntimeException("Unable to initialize EGL10");
    }
    return eglDisplay;
}

 

 

Config

// 타입별로 설정해야 하는 속성에 차이가 있음
public static enum ConfigType {
    PLAIN,
    
    // EGL_SURFACE_TYPE, EGL_PBUFFER_BIT
    PIXEL_BUFFER,
    
    // EGL_RECORDABLE_ANDROID, 1
    RECODABLE
}

public static final int EGL_RECORDABLE_ANDROID = 0x3142;

 

EGLConfig createEglConfig(int configType, int version) {
    int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
    if( version >= 3 ) {
    	renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
    }
    
    int[] configAttributes = {
    	EGL14.EGL_RED_SIZE, 8,
        EGL14.EGL_GREEN_SIZE, 8,
        ELG14.EGL_BLUE_SIZE, 8,
        EGL14.EGL_ALPHA_SIZE, 8,
        //EGL14.EGL_DEPTH_SIZE, 16,
        //EGL14.EGL_STENCIL_SISE, 8,
        EGL14.EGL_RENDERABLE_TYPE, renderableType,
        EGL14.EGL_NONE, 0,
        EGL14.EGL_NONE
    }
    

    // 타입별 추가 속성 정의
    switch( configType ) {
        case PLAIN:
            break;

        case PIXEL_BUFFER:
            configAttributes[configAttributes.length - 3] = EGL14.EGL_SURFACE_TYPE;
            configAttributes[configAttributes.length - 2] = EGL14.EGL_PBUFFER_BIT;
            break;

        case RECORDABLE:
            configAttributes[configAttributes.length - 3] = EGL_RECORDABLE_ANDROID;
            configAttributes[configAttributes.length - 2] = 1
            break;

        default:
            throw new IllegalArgumentExcpetion();
    }

    EGLConfig[] configs = new EGLConfig[1];
    int[] numConfigs = new int[1];

    if( !EGL14.eglChooseConfig( eglDisplay, configAttributes, 0, configs, 0, configs.length, numConfigs, 0 ) {
        throw new RuntimeException("Unable to find RGB888");
    }

    // 속성값 체크 : 필요시
    int[] value = new int[1];
    EGLConfig config = configs[0];

        .
        .
    if( !EGL14.eglGetConfigAttrib( eglDisplay, config, EGL14.EGL_RED_SIZE, value )) {
        value[0] = 0;
    }
    	.
        .
        
    return config;
}

 

Context

EGLContext createContext( EGLConfig config ) {
	int[] contextAttributes = { 
    	EGL14.EGL_CONTEXT_CLIENT_VERSION, 
        3, 
        EGL14.EGL_NONE 
    };
    
    EGLContext eglContext = EGL14.eglCreateContext( eglDisplay, eglConfig, sharedContext, contextAttributes, 0);
    
    if( eglContext == EGL14.EGL_NO_CONTEXT ) {
    	// 버전을 GLES 2 로 변경해서 Config , Context 생성 시도
        EGLConfig config = createEglConfig( EGL_RECORDABLE, 2 );
        int[] contextAttributes2 = { 
    		EGL14.EGL_CONTEXT_CLIENT_VERSION, 
        	2, 
        	EGL14.EGL_NONE 
    	};
        eglContext = EGL14.eglCreateContext( eglDisplay, eglConfig, sharedContext, contextAttributes2, 0);
        
        if( eglContext == EGL14.EGL_NO_CONTEXT ) {
        	throw new RuntimeException("Failed to create EGL context");
        }
    }  
    
    // context쿼리해서 버전 값 얻기
    int[] values = new int[1];
    EGL14.eglQueryContext( eglDisplay, eglContext, EGL14.EGL_CONTEXT_CLIENT_VERSION, values, 0);
    
    return eglContext;
}

 

 

EGLSurface

Offscreen Surface

EGLSurface createSurface(int width, int height) {
    if( eglDisplay == EGL14.EGL_NO_DISPLAY || eglContext == EGL14.EGL_NO_CONTEXT || eglConfig == null ) {
        throw new RuntimeException("");
    }

    if( configType != ConfigType.PIXEL_BUFFER) {
        throw new RuntimeException( "" );
    }

    int[] surfaceAttribs = {EGL14.EGL_WIDTH, width, EGL14.EGL_HEIGH, height, EGL14.EGL_NONE };
    EGLSurface eglSurface = EGL14.eglCreatePbufferSurface( eglDisplay, eglConfig, surfaceAttribs, 0);
    if( eglSurface == EGL14.EGL_NO_SURFACE ) {
        throw new RuntimeException("Failed to create pixel buffer surface");
    }
    

    return eglSurface;
}

 

안드로이드 서피스에서 EGLSurface 생성

Offscreen surface 는 EGL을 위한 surface 이고, 안드로이드 Surface 는 프레임 버퍼를 가진  별도의 객체이다.  해당 안드로이드 surface에 egl 을 통해 렌더링하기 위해서는 별도의 egl surface를 생성해 사용해야 한다. 

 

안드로이드 SurfaceView 혹은 TextureView 등에서 Surface(TextureView의 경우 SurfaceTexture)가 비동기 콜백을 통해 생성되면 해당 서피스를 전달해 egl surface 를 만들 수 있다. 

 

EGLSurface createSurface( Surface surface ) {
    if( eglDisplay == EGL14.EGL_NO_DISPLAY || eglContext == EGL14.EGL_NO_CONTEXT || eglConfig == null ) {
        throw new RuntimeException("");
    }
    
    int[] surfaceAttrib = { EGL14.EGL_NONE };

    EGLSurface eglSurface = EGL14.eglCreateWindowSurface( eglDisplay, elgConfig, surface, surfaceAttribs, 0);
    if( eglSurface == EGL14.EGL_NO_SURFACE ) {
        throw new RuntimeException("");
    }
    
    return eglSurface;
}

 

안드로이드 서피스가 별도로 존재하지 않는 경우에도 opengl es 텍스처를 통해서 안드로이드 서피스, EGL서피스 생성이 가능하다. opengl es 텍스처에서 egl surface를 생성하는 경우 아래와 같은 순서로 이루어진다.

- OpenGL ES 텍스처생성 
- SurfaceTexture : opengl 텍스처를 통해 생성
- Surface : surface texture 를 통해 생성
- EGLSurface 생성

 

 

서피스 정보

너비, 높이 얻기

getSurfaceWidth(), getSurfaceHeight()

int result[] = new int[1];

// width
EGL14.eglQuerySurface( eglDisplay, eglSurface, EGL14.EGL_WIDTH, result, 0 );

// height
EGL14.eglQuerySurface( eglDisplay, eglSurface, EGL14.EGL_HEIGHT, result, 0 );

 

MakeCurrent

컨텍스트가 사용할 EGLSurface를 설정하는 메쏘드

current 가 설정되면, 아래처럼 context나 surface를 가져올 수 있다.

EGL14.eglGetCurrentContext()

EGL14.eglGetCurrentSurface( EGL14.EGL_DRAW);

void makeCurrent( EGLSurface eglSurface ) {
    if( !EGL14.eglMakeCurrent( eglDisplay, eglSurface, eglSurface, eglContext ) ) {
        throw new RuntimeException("");
    }
}

void makeCurrent( EGLSurface drawSurface, EGLSurface readSurface ) {
    if( !EGL14.eglMakeCurrent( eglDisplay, drawSurface, readSurface, eglContext )) {
        throw new RuntimeException("");
    }
}

 

 

서피스 삭제

void releaseSurface() {
    if( eglSurface != EGL14.EGL_NO_SURFACE ) {
        EGL14.eglDestroySurface( eglDisplay, eglSurface );
        eglSurface = EGL14.EGL_NO_SURFACE;
    }
}

 

제거

void release() {
    releaseSurface();
    EGL14.eglMakeCurrent( eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
    EGL14.eglDestroyContext( eglDisplay, eglContext );
    EGL14.eglReleaseThread();
    EGL14.eglTerminate(eglDisplay);
    
    eglContext = EGL14.EGL_NO_CONTEXT;
    eglDisplay = EGL14.EGL_NO_DISPLAY;
    eglConfig = null;
}