Skip to content
This repository has been archived by the owner on Feb 24, 2024. It is now read-only.

CH03 An Introduction to EGL

Hyunwoo Jo edited this page Sep 18, 2023 · 1 revision

CH 03. An Introduction to EGL

Created: 2023년 8월 2일 오후 7:00 Tags: Windowing system

2장, 'Hello Triangle: OpenGL ES 3.0 예제'에서는 OpenGL ES 3.0을 사용하여 창에 삼각형을 그리고, 창을 열고 관리하기 위해 자체 설계한 몇 가지 사용자 정의 함수를 사용했습니다. 이러한 기법은 예제를 단순화하지만, 자체 시스템에서 OpenGL ES 3.0으로 작업해야 하는 방법을 모호하게 만들 수 있습니다. 크로노스 그룹에서 콘텐츠 개발을 위해 제공하는 API 제품군의 일부로, (대부분) 플랫폼에 독립적인 API인 EGL을 사용하여 드로잉 표면(drawing surfaces)을 관리할 수 있습니다(창은 한 가지 유형에 불과하며 다른 유형에 대해서는 나중에 설명하겠습니다). EGL은 다음과 같은 메커니즘을 제공합니다:

  • 장치의 native windowing system과 통신하기
  • 사용 가능한 drawing surfaces의 유형 및 구성 쿼리하기
  • drawing surfaces 만들기
  • OpenGL ES 3.0과 기타 그래픽 렌더링 API(예: 데스크톱 OpenGL 및 하드웨어 가속 벡터 그래픽을 위한 크로스 플랫폼 API인 OpenVG, 또는 윈도우 시스템의 기본 드로잉 명령) 간의 렌더링 동기화
  • 텍스처 맵과 같은 렌더링 리소스 관리

이 장에서는 창을 여는 데 필요한 기본 사항을 소개합니다. 추후 텍스처 맵을 만드는 것과 같은 다른 operation을 설명하면서 필요한 EGL 명령어에 대해 설명합니다.

Communicating with the Windowing System

EGL은 OpenGL ES 3.0(및 기타 크로노스 그래픽 API)과 컴퓨터에서 실행되는 기본 창 시스템(예: GNU/Linux 시스템, Microsoft Windows 또는 Mac OS X의 Quartz에서 흔히 볼 수 있는 X 창 시스템(X Window System) 사이에 “glue” 레이어를 제공합니다. EGL이 사용 가능한 drawing surfaces 유형이나 기본 시스템의 다른 특성을 확인하려면 먼저 윈도우 시스템과의 통신 채널을 열어야 합니다. Apple은 EAGL이라는 자체 iOS 구현 EGL API를 제공합니다.

💡 X 창 시스템(X Window System)이란, 간단히 말해 windows를 생성하고 마우스와 키보드로부터 event를 받아 늘이기/줄이기, 옮기기 기능 및 그리고 다른 windows간의 겹침현상 해결 등의 기능을 제공하는 framework이다. 여타 Windows System과 달리 X Window System은 서버-클라이언트 구조를 채택한다. 여기서 서버의 역할은 client들의 drawing 이벤트들을 취합하여 front 버퍼에 그려 화면에 보이게 하고 모니터 상의 이벤트가 발생했을때 연관된 클라이언트에 이벤트를 보내주는 역할이라 생각하면 된다. 그리고 클라이언트는 각각의 windows 창이라고 생각하면 되고 그리거나 표현할 컨텐츠를 server에 보내주는 역할이라 생각하면 된다.

참조: https://en.wikipedia.org/wiki/X_Window_System https://magcius.github.io/xplain/article/x-basics.html

모든 windowing system은 서로 다른 의미를 갖기 때문에 EGL은 기본 windowing system과의 인터페이스를 위한 모든 시스템 종속성을 캡슐화하는 기본 불투명 타입인 EGLDisplay를 제공합니다. EGL을 사용하는 모든 애플리케이션이 수행해야 하는 첫 번째 작업은 로컬 EGL 디스플레이와의 연결을 생성하고 초기화하는 것입니다. 이 작업은 예제 3-1에 표시된 것처럼 두 번의 호출 순서로 수행됩니다.

EGLint majorVersion;
EGLint minorVersion;
EGLDisplay display = eglGetDisplay ( EGL_DEFAULT_DISPLAY );
if ( display == EGL_NO_DISPLAY )
{
 // Unable to open connection to local windowing system
 //로컬 윈도우 시스템에 대한 연결을 열 수 없습니다.
}
if ( !eglInitialize ( display, &majorVersion, &minorVersion ) )
{
 // Unable to initialize EGL; handle and recover
 //EGL을 초기화할 수 없음; 처리 및 복구
}

EGL 디스플레이 서버에 대한 연결을 열려면 다음 함수를 호출합니다:

☝ EGLDisplay : eglGetDisplay(EGLNativeDisplayType displayId)

displayId : 디스플레이 연결을 지정하며, 기본 연결에는 EGL_DEFAULT_DISPLAY를 사용합니다.

EGLNativeDisplayType은 네이티브 창 시스템의 디스플레이 유형과 일치하도록 정의됩니다. 예를 들어, Microsoft Windows에서 EGLNativeDisplayType은 Microsoft Windows 장치 컨텍스트에 대한 핸들인 HDC로 정의됩니다. 그러나 코드를 다른 운영 체제 및 플랫폼으로 쉽게 이동할 수 있도록 EGL_DEFAULT_DISPLAY 토큰이 허용되며, 이 토큰은 기본 네이티브 디스플레이에 대한 연결을 반환합니다. 디스플레이 연결을 사용할 수 없는 경우, eglGetDisplayEGL_NO_DISPLAY를 반환합니다. 이 오류는 EGL을 사용할 수 없음을 나타내며 OpenGL ES 3.0을 사용할 수 없음을 나타냅니다. 더 많은 EGL 연산을 논의하기 전에 EGL이 어떻게 오류를 처리하고 애플리케이션에 보고하는지 간략하게 설명해야 합니다.

Cheaking for Errors

EGL의 대부분의 함수는 성공하면 EGL_TRUE를 반환하고 그렇지 않으면 EGL_FALSE를 반환합니다. 그러나 EGL은 단순히 호출이 실패했는지 여부를 알려주는 것 이상의 기능을 수행하며 실패 이유를 나타내는 오류를 기록합니다. 그러나 오류 코드는 사용자에게 직접 반환되지 않으므로 다음 함수를 호출하여 오류 코드를 명시적으로 EGL에 쿼리해야 합니다:

EGLint eglGetError()

이 함수는 특정 스레드에서 호출된 가장 최근의 EGL 함수의 오류 코드를 반환합니다. EGL_SUCCESS가 반환되면 반환할 상태가 없습니다. 호출이 완료되면 오류 코드를 직접 반환하는 것과 비교했을 때 이것이 왜 신중한 접근 방식인지 궁금할 수 있습니다. 함수 반환 코드를 무시하라고 권장하지는 않지만, 선택적으로 오류 코드를 하면 제대로 작동하는 애플리케이션에서 중복 코드를 줄일 수 있습니다. 개발 및 디버깅 중에, 그리고 중요한 애플리케이션에서는 지속적으로 오류를 확인해야 하지만, 애플리케이션이 예상대로 작동한다는 확신이 들면 오류 확인 횟수를 줄일 수 있습니다. (개발중에만 어떤 error인지 eglGetError로 확인하고 릴리즈 후에는 에러를 확인하는 횟수를 줄이기 위해서)

Initializing EGL

연결을 성공적으로 열었으면 다음 함수를 호출하여 EGL을 초기화해야 합니다:

💡 EGLBoolean eglInitialize(EGLDisplay *display*, EGLint **majorVersion*, EGLint **minorVersion*)

display : EGL 디스플레이 연결을 지정합니다. majorVersion : EGL이 반환한 major version 번호를 지정합니다.

minorVersion : EGL 구현에서 반환한 minor version 번호를 지정합니다. 구현이 반환하는 부 버전 번호를 지정합니다.

이 함수는 EGL의 내부 데이터 구조를 초기화하고 EGL 구현의 메이저 및 마이너 버전 번호를 반환합니다. EGL을 초기화할 수 없는 경우 이 함수를 호출하면 EGL_FALSE를 반환하고 EGL의 오류 코드를 다음과 같이 설정합니다.

  • 디스플레이가 유효한 EGLDisplay를 지정하지 않은 경우 EGL_BAD_DISPLAY.
  • EGL을 초기화할 수 없는 경우 EGL_NOT_INITIALIZED.

Determining the Available Surface Configurations

EGL을 초기화하면 사용할 수 있는 렌더링 표면(rendering surfaces)의 유형과 구성을 결정할 수 있습니다. 이 작업에는 두 가지 방법이 있습니다:

  • 모든 surface configuration을 쿼리하여 최적의 Configuration을 직접 찾습니다.
  • 요구 사항 집합을 지정하고 EGL이 가장 적합한 Configuration을 추천하도록 합니다.

대부분의 경우 두 번째 옵션이 구현하기가 더 간단하며 첫 번째 옵션을 사용했을 때와 같은 결과를 얻을 수 있습니다. 두 경우 모두 EGL은 특정 surface와 그 특성에 대한 정보(예: 각 컬러 컴포넌트의 비트 수 또는 해당 EGLConfig와 연관된 뎁스 버퍼(있는 경우))가 포함된 EGL 내부 데이터 구조에 대한 식별자인 EGLConfig를 반환합니다. 나중에 설명할 eglGetConfigAttrib함수를 사용하여 EGLConfig의 모든 어트리뷰트를 쿼리할 수 있습니다. 기본 윈도우 시스템에서 지원하는 모든 EGL surface configuration을 쿼리하려면 이 함수를 호출합니다:

💡 EGLBoolean eglGetConfigs(EGLDisplay *display*, EGLConfig **configs*, EGLint *maxReturnConfigs*, EGLint **numConfigs*)

display : EGL 디스플레이 연결을 지정합니다. configs : 구성 목록을 지정합니다. maxReturnConfigs : 구성의 크기를 지정합니다. numConfigs : 반환되는 구성의 크기를 지정합니다.

이 함수는 호출이 성공하면 EGL_TRUE를 반환합니다. 호출에 실패하면 EGL_FALSE를 반환하고 EGL의 오류 코드를 다음과 같이 설정합니다.

  • 디스플레이가 초기화되지 않은 경우 EGL_NOT_INITIALIZED.
  • numConfigs가 NULL인 경우 EGL_BAD_PARAMETER.

eglGetConfigs를 호출하는 방법은 두 가지가 있습니다. 첫째, configs 값에 NULL을 지정하면 시스템은 EGL_TRUE를 반환하고 numConfigs 를 사용 가능한 EGLConfigs의 수로 설정합니다.

시스템의 EGLConfig에 대한 추가 정보는 반환되지 않지만, 사용 가능한 Congiuration의 수를 알고있다면 원할 경우 전체 EGLConfig 집합을 가져올 수 있는 충분한 메모리를 할당할 수 있습니다.

또는 초기화되지 않은 EGLConfig값의 배열을 할당하고 이를 configs 파라미터로 eglGetConfigs에 전달할 수 있습니다. maxReturnConfigs를 할당된 배열의 크기로 설정하면 반환되는 구성의 최대 개수를 지정할 수 있습니다. 호출이 완료되면 수정된 구성의 항목 수로 numConfigs가 업데이트됩니다. 그런 다음 반환된 값의 목록을 처리하기 시작하여 다양한 구성의 특성을 쿼리하여 필요에 가장 적합한 구성을 결정할 수 있습니다.

Querying EGLConfig Attributes

이제 EGL이 EGLConfig와 관련된 값에 대해 설명하고 해당 값을 검색하는 방법을 설명하겠습니다.

EGLConfig에는 EGL에서 사용할 수 있는 surface에 대한 모든 정보가 포함되어 있습니다. 여기에는 사용 가능한 색상 수, 구성과 관련된 추가 버퍼(나중에 설명하는 뎁스 및 스텐실 버퍼 등), 표면 유형 및 기타 여러 특성에 대한 정보가 포함됩니다. 다음은 EGLConfig에서 쿼리할 수 있는 어트리뷰트 목록입니다.

이 장에서는 이 중 일부만 설명하지만, 전체 목록은 표 3-1에 참조용으로 나와 있습니다. EGLConfig와 관련된 특정 속성(attribute)을 쿼리하려면 다음 함수를 사용합니다:

💡 EGLBoolean eglGetConfigAttrib( EGLDisplay *display*, EGLConfig *config*, EGLint *attribute*, EGLint **value*)

display : specifies the EGL display connection config : specifies the configuration to be queried attribute : specifies the particular attribute to be returned value : specifies the value returned

이 함수는 호출에 성공하면 EGL_TRUE를 반환합니다. 실패하면 EGL_FALSE를 반환하고, 어트리뷰트가 유효한 어트리뷰트가 아닌 경우 EGL_BAD_ATTRIBUTE에러가 반환됩니다. 이 호출은 연결된 EGLConfig의 특정 어트리뷰트 값을 반환합니다. 이를 통해 최종적으로 렌더링 surface을 생성하기 위해 어떤 구성을 선택할지 제어할 수 있습니다. 하지만 표 3-1을 보면 옵션이 너무 많아서 다소 겁이 날 수도 있습니다. EGL은 애플리케이션에 중요한 것을 지정할 수 있는 또 다른 루틴인 eglChooseConfig를 제공하며, 요청에 따라 가장 적합한 구성을 반환합니다.

Table 3-1 EGLConfig Attributes

Table 3-1 EGLConfig Attributes

Table 3-1 EGLConfig Attributes (Continued)

Table 3-1 EGLConfig Attributes (Continued)

Letting EGL Chooese the Configuration

EGL이 적합한(matching) EGLConfigs를 선택하도록 하려면 이 함수를 사용합니다:

💡 EGLBoolean eglChooseConfig( EGLDisplay *display*, const EGLint **attribList*, EGLConfig **configs*, EGLint *maxReturnConfigs*, EGLint **numConfigs*)

display : EGL 디스플레이 연결을 지정합니다. attribList : configs로 일치시킬 속성 목록을 지정합니다. configs : 설정 목록을 지정합니다. maxReturnConfigs : 컨피그의 크기를 지정합니다. numConfigs : 반환되는 구성의 크기를 지정합니다.

이 함수는 호출에 성공하면 EGL_TRUE를 반환합니다. 실패하면 EGL_FALSE가 반환되고, attribList에 정의되지 않은 EGL 속성 또는 인식할 수 없거나 범위를 벗어난 속성 값이 포함된 경우 EGL_BAD_ATTRIBUTE오류가 반환됩니다.

애플리케이션의 올바른 작동에 중요한 모든 속성에 대해 관련 기본값과 함께 속성 목록을 제공해야 합니다. 예를 들어 5비트 빨강과 파랑, 6비트 녹색(일반적으로 사용되는 "RGB 565" 형식)의 렌더링 표면, 뎁스 버퍼, OpenGL ES 3.0을 지원하는 EGLConfig가 필요한 경우 예제 3-2에 표시된 배열을 선언할 수 있습니다. 속성 목록에 명시적으로 지정되지 않은 값의 경우 EGL은 표 3-1에 표시된 기본값을 사용합니다. 또한 속성에 숫자 값을 지정할 때 일치하는 EGLConfig가 있는 경우 EGL은 반환된 구성에 최소한 해당 값이 포함되도록 보장합니다.

EGLint attribList[] =
{
 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
 EGL_RED_SIZE, 5,
 EGL_GREEN_SIZE, 6,
 EGL_BLUE_SIZE, 5,
 EGL_DEPTH_SIZE, 1,
 EGL_NONE
};

참고: EGL_OPENGL_ES3_BIT_KHR 속성을 사용하려면 EGL_KHR_create_context 확장이 필요합니다. 이 속성은 eglext.h(EGL v1.4)에 정의되어 있습니다. 또한 OpenGL ES 3.0은 OpenGL ES 2.0과 역호환되므로 일부 구현은 항상 OpenGL ES 2.0 컨텍스트를 OpenGL ES 3.0 컨텍스트로 승격한다는 점에 유의할 필요가 있습니다.

이 속성 집합을 선택 기준으로 사용하려면 예제 3-3을 확인하세요.

const EGLint MaxConfigs = 10;
EGLConfig configs[MaxConfigs]; //10개의 configs만 허용됩니다.
EGLint numConfigs;
if ( !eglChooseConfig( display, attribList, configs, MaxConfigs,
&numConfigs ) )
{
 // 뭔가 작동하지 않음 ... 오류 상황 처리
}
else
{
 // Everything is okay; continue to create a rendering surface
 //렌더링 surface 생성을 계속 진행하세요.
}

eglChooseConfig가 성공적으로 반환되면, 조건과 일치하는 EGLConfig세트가 반환됩니다. 둘 이상의 EGLConfig가 일치하는 경우(최대 지정한 최대 구성 수까지), eglChooseConfig는 다음 순서를 사용하여 구성을 정렬합니다:

💡
  1. EGL_CONFIG_CAVEAT 값에 따라. 구성 경고가 없는 구성(EGL_CONFIG_CAVEAT 값이 EGL_NONE인 경우), 느린 렌더링 구성(EGL_SLOW_CONFIG), 비준수 구성(EGL_NON_CONFORMANT_CONFIG) 순으로 우선순위가 지정됩니다.
  2. EGL_COLOR_BUFFER_TYPE에 지정된 버퍼 유형별.
  3. 컬러 버퍼의 비트 수를 내림차순 크기로 표시합니다. 버퍼의 비트 수는 EGL_COLOR_BUFFER_TYPE에 따라 다르며, 최소한 특정 컬러 채널에 지정된 값이어야 합니다. 버퍼 유형이 EGL_RGB_BUFFER인 경우 비트 수는 EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE의 총합으로 계산됩니다. 컬러 버퍼 유형이 EGL_LUMINANCE_BUFFER인 경우 비트 수는 EGL_LUMINANCE_SIZE와 EGL_ALPHA_SIZE의 합으로 계산됩니다.
  4. 오름차순으로 EGL_BUFFER_SIZE 값에 따라.
  5. 오름차순으로 EGL_SAMPLE_BUFFERS의 값으로.
  6. 오름차순으로 EGL_SAMPLES 수에 따라.
  7. 오름차순으로 EGL_DEPTH_SIZE 값에 따라.
  8. 오름차순으로 EGL_STENCIL_SIZE 값에 따라.
  9. EGL_ALPHA_MASK_SIZE 값으로 (OpenVG surface에만 적용 가능).
  10. 구현에 따라 EGL_NATIVE_VISUAL_TYPE에 의해 결정됩니다.
  11. 오름차순으로 EGL_CONFIG_ID 값에 의해.

이 목록에 언급되지 않은 매개변수는 정렬 프로세스에서 사용되지 않습니다.

참고: 세 번째 정렬 규칙으로 인해 지정한 것과 일치하는 최상의 형식을 얻으려면 반환된 결과를 검토하는 추가 로직을 추가해야 합니다. 예를 들어 "565" RGB 형식을 요청하면 반환된 결과에 "888" 형식이 먼저 표시됩니다.

예제 3-3에서 언급했듯이 eglChooseConfig가 성공적으로 반환되면 그릴 무언가를 계속 만들 수 있는 충분한 정보를 얻게 됩니다. 기본적으로 사용자가 원하는 렌더링 표면 유형을 지정하지 않으면(EGL_SURFACE_TYPE 속성을 지정하여) EGL은 사용자가 온스크린 창을 원한다고 가정합니다.

Creating an On-Screen Rendering Area: The EGL Window

렌더링 요구 사항을 충족하는 적절한 EGLConfig가 있으면 창을 생성할 준비가 된 것입니다. 창을 생성하려면 다음 함수를 호출합니다:

💡 EGLSurface eglCreateWindowSurface( EGLDisplay *display*, EGLConfig *config*, EGLNativeWindowType *window*, const EGLint **attribList*)

display : EGL 디스플레이 연결을 지정합니다. config : configuration을 지정합니다. window : native window 지정합니다. attribList : list of window attributes을 지정합니다; NULL일 수 있습니다.

이 함수는 네이티브 디스플레이 관리자에 대한 연결과 이전 단계에서 얻은 EGLConfig를 인자로 받습니다. 또한 이전에 생성한 native windowing system의 창이 필요합니다. EGL은 다양한 창 시스템과 OpenGL ES 3.0 사이의 소프트웨어 계층이므로 네이티브 창을 만드는 방법을 설명하는 것은 이 가이드의 범위를 벗어납니다. 해당 환경에서 창을 생성하는 데 필요한 사항은 네이티브 창 시스템의 설명서를 참조하세요.

마지막으로, 이 호출은 list of attributes을 받지만 이 목록은 표 3-1에 표시된 속성과는 다릅니다. EGL은 다른 렌더링 API(특히 OpenVG)를 지원하기 때문에 OpenGL ES 3.0으로 작업할 때는 eglCreateWindowSurface에서 허용하는 일부 속성이 적용되지 않습니다(표 3-2 참조). 여기서는 렌더링할 프론트 버퍼 또는 백 버퍼의 버퍼를 지정할 때 단일 어트리뷰트를 사용합니다.

💡 Token : EGL_RENDER_BUFFER Description : 렌더링에 사용할 버퍼를 지정합니다(EGL_SINGLE_BUFFER 값 사용) 또는 백버퍼(EGL_BACK_BUFFER). Default Value : EGL_BACK_BUFFER

참고: OpenGL ES 3.0 window rendering surfaces의 경우 더블 버퍼링된 창만 지원됩니다.

속성 목록은 비어 있을 수도 있고(즉, attribList의 값으로 NULL 포인터를 전달한 경우), 첫 번째 요소로 EGL_NONE 토큰으로 채워진 목록일 수도 있습니다. 이러한 경우 모든 관련 속성은 기본값을 사용합니다. eglCreateWindowSurface가 실패할 수 있는 방법은 여러 가지가 있으며, 그 중 하나라도 발생하면 호출에서 EGL_NO_SURFACE가 반환되고 특정 에러가 설정됩니다. 이 상황이 발생하면 표 3-3에 표시된 이유 중 하나를 반환하는 eglGetError를 호출하여 실패 원인을 확인할 수 있습니다.

💡 Table 3-3 Possible Errors When eglCreateWindowSurface Fails **EGL_BAD_MATCH :**

이 상황은 다음과 같은 경우에 발생합니다:

  • 네이티브 창의 속성이 제공된 EGLConfig의 속성과 일치하지 않습니다.
  • 제공된 EGLConfig가 창으로 렌더링을 지원하지 않는 경우(즉, EGL_SURFACE_TYPE 속성에 EGL_WINDOW_BIT가 설정되어 있지 않은 경우).

EGL_BAD_CONFIG : 이 오류는 제공된 EGLConfig가 시스템에서 지원되지 않는 경우 발생합니다.

EGL_BAD_NATIVE_WINDOW : 제공된 기본 창 핸들이 유효하지 않은 경우 이 오류가 지정됩니다. EGL_BAD_ALLOC : 이 오류는 eglCreateWindowSurface가 새 EGL 창에 대한 리소스를 할당할 수 없거나 제공된 네이티브 창과 연결된 EGLConfig가 이미 있는 경우 발생합니다.

이 모든 것을 종합하면 창을 생성하는 코드는 다음과 같습니다.

EGLint attribList[] =
{
 EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
 EGL_NONE
);
EGLSurface window = eglCreateWindowSurface ( display, config,
nativeWindow,
attribList );
if ( window == EGL_NO_SURFACE )
{
 switch ( eglGetError ( ) )
 {
case EGL_BAD_MATCH:
// Check window and EGLConfig attributes to determine
// compatibility, or verify that the EGLConfig
// supports rendering to a window
break;
case EGL_BAD_CONFIG:
// Verify that provided EGLConfig is valid
break;
case EGL_BAD_NATIVE_WINDOW:
// Verify that provided EGLNativeWindow is valid
break;
case EGL_BAD_ALLOC:
// Not enough resources available; handle and recover
break;
 }
}

이렇게 하면 그릴 수 있는 공간이 만들어지지만, 창에서 OpenGL ES 3.0을 성공적으로 사용하려면 아직 두 단계를 더 거쳐야 합니다. 하지만 윈도우만이 유용한 렌더링 표면은 아닙니다. 다음에는 다른 유형의 렌더링 서페이스를 소개한 후 논의를 마무리하겠습니다.

Creating an Off-Screen Rendering Area:EGL PBuffers

OpenGL ES 3.0을 사용하여 on-screen window으로 렌더링할 수 있을 뿐만 아니라, 픽셀 버퍼의 줄임말인 PBuffer라는 눈에 보이지 않는 off-screen surfaces으로 렌더링할 수도 있습니다. PBuffer는 window과 마찬가지로 OpenGL ES 3.0에서 사용할 수 있는 모든 하드웨어 가속을 최대한 활용할 수 있습니다. PBuffer는 텍스처 맵을 생성하는 데 가장 자주 사용됩니다. 텍스처에 렌더링만 하려는 경우, 더 효율적인 프레임버퍼 객체('12장, '프레임버퍼 객체'에서 다룸)를 사용하는 것이 좋습니다. 그러나 프레임버퍼 오브젝트를 사용할 수 없는 일부 경우(예: OpenGL ES로 오프스크린 표면을 렌더링한 다음 OpenVG와 같은 다른 API에서 텍스처로 사용하는 경우 > 텍스쳐맵 생성?!)에는 여전히 pbuffer가 유용할 수 있습니다. PBuffer를 생성하는 것은 몇 가지 사소한 차이점을 제외하면 EGL 창을 생성하는 것과 매우 유사합니다. PBuffer를 생성하려면 창을 생성할 때와 마찬가지로 한 가지 수정 사항만 추가하여 EGLConfig를 찾아야 합니다: EGL_PBUFFER_BIT을 포함하도록 EGL_SURFACE_TYPE의 값을 보강해야 합니다. 적절한 EGLConfig를 찾으면 다음 함수를 사용하여 pbuffer를 생성할 수 있습니다.

💡 EGLSurface eglCreatePbufferSurface( EGLDisplay display, EGLConfig config, const EGLint *attribList)

display : EGL 디스플레이 연결을 지정합니다. config : configuration을 지정합니다 attribList : 픽셀 버퍼 어트리뷰트 목록을 지정합니다(NULL일 수 있음).

창 생성과 마찬가지로 이 함수는 native display manager 및 선택한 EGLConfig에 대한 연결을 필요로 합니다. 이 호출에는 표 3-4에 설명된 대로 어트리뷰트 목록도 필요합니다.

Untitled 2

Untitled 3

eglCreatePbufferSurface가 실패할 수 있는 방법은 여러 가지가 있습니다. 창 생성과 마찬가지로, 이러한 실패 중 하나라도 발생하면 호출에서 EGL_NO_SURFACE가 반환되고 특정 에러가 설정됩니다. 이 경우 eglGetError는 표 3-5에 나열된 오류 중 하나를 반환합니다.

Untitled 4

이 모든 것을 종합하여 예제 3-5에 표시된 것처럼 pbuffer를 생성합니다.

EGLint attribList[] =
{
 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
 EGL_RED_SIZE, 5,
 EGL_GREEN_SIZE, 6,
 EGL_BLUE_SIZE, 5,
 EGL_DEPTH_SIZE, 1,
 EGL_NONE
};
const EGLint MaxConfigs = 10;
EGLConfig configs[MaxConfigs]; // We'll accept only 10 configs
EGLint numConfigs;
if ( !eglChooseConfig( display, attribList, configs, MaxConfigs,
&numConfigs ) )
{
 // Something did not work ... handle error situation
}
else
{
 // We have found a pbuffer-capable EGLConfig
}
// Proceed to create a 512 x 512 pbuffer
// (or the largest available)
EGLSurface pbuffer;
EGLint attribList[] =
{
 EGL_WIDTH, 512,
 EGL_HEIGHT, 512,
 EGL_LARGEST_PBUFFER, EGL_TRUE,
 EGL_NONE
);
pbuffer = eglCreatePbufferSurface( display, config, attribList);
if ( pbuffer == EGL_NO_SURFACE )
{
 switch ( eglGetError ( ) )
 {
case EGL_BAD_ALLOC:
// Not enough resources available; handle and recover
break;
case EGL_BAD_CONFIG:
// Verify that provided EGLConfig is valid
break;
case EGL_BAD_PARAMETER:
// Verify that EGL_WIDTH and EGL_HEIGHT are
// non-negative values
break;
case EGL_BAD_MATCH:
// Check window and EGLConfig attributes to determine
// compatibility and pbuffer-texture parameters
break;
 }
}
// Check the size of pbuffer that was allocated
EGLint width;
EGLint height;
if ( !eglQuerySurface ( display, pbuffer, EGL_WIDTH, &width ) ||
 !eglQuerySurface ( display, pbuffer, EGL_HEIGHT, &height ))
{
 // Unable to query surface information
}

Pbuffers는 윈도우와 마찬가지로 모든 OpenGL ES 3.0 렌더링 기능을 지원합니다. 가장 큰 차이점은 - 화면에 버퍼를 표시할 수 없다는 점을 제외하면 - 렌더링이 완료되면 창에서와 같이 버퍼를 교체하는 대신 버퍼의 값을 애플리케이션으로 복사하거나 버퍼의 바인딩을 텍스처로 수정한다는 점입니다.

Creating a Rendering Context

렌더링 컨텍스트는 작동에 필요한 모든 상태 정보를 포함하는 OpenGL ES 3.0 내부의 데이터 구조입니다. 예를 들어, 여기에는 2장 "Hello Triangle"의 예제 프로그램에서 사용된 버텍스 및 프래그먼트 셰이더와 버텍스 데이터 배열에 대한 참조가 포함되어 있습니다: OpenGL ES 3.0 예제." OpenGL ES 3.0이 그리려면 먼저 사용할 수 있는 컨텍스트가 있어야 합니다. 컨텍스트를 생성하려면 다음 함수를 사용합니다:

💡 EGLContext eglCreateContext( EGLDisplay display, EGLConfig config, EGLContext shareContext, const EGLint *attribList)

display : specifies the EGL display connection config : specifies the configuration shareContext : 여러 EGL 컨텍스트가 셰이더 프로그램 및 텍스처 맵과 같은 특정 유형의 데이터를 공유할 수 있으며, 공유하지 않으려면 EGL_NO_CONTEXT를 사용합니다. attribList : 생성할 컨텍스트의 속성 목록을 지정합니다. 단 하나의 속성, EGL_CONTEXT_CLIENT_VERSION만 허용됩니다.

다시 한 번, 디스플레이 연결과 애플리케이션의 요구 사항을 가장 잘 나타내는 EGLConfig가 필요합니다. 세 번째 파라미터인 shareContext는 셰이더 프로그램이나 텍스처 맵과 같은 특정 유형의 데이터를 여러 EGLContext가 공유할 수 있도록 합니다. 당분간은 다른 컨텍스트와 리소스를 공유하지 않음을 나타내는 EGL_NO_CONTEXT를 shareContext의 값으로 전달합니다. 마지막으로, 많은 EGL 호출과 마찬가지로 eglCreateContext의 연산에 특정한 속성 목록이 지정됩니다. 이 경우 표 3-6에서 설명하는 단일 속성인 EGL_CONTEXT_CLIENT_VERSION이 허용됩니다.

💡 Table 3-6 Attributes for Context Creation Using eglCreateContext Token : EGL_CONTEXT_CLIENT_VERSION Description : 사용 중인 OpenGL ES 버전과 관련된 컨텍스트 유형을 지정합니다. Default Value : 1 (OpenGL ES 1.X 컨텍스트 지정)

OpenGL ES 3.0을 사용하기 때문에 올바른 컨텍스트 유형을 얻으려면 항상 이 속성을 지정해야 합니다.

eglCreateContext가 성공하면 새로 생성된 컨텍스트에 대한 핸들을 반환합니다. 컨텍스트를 생성할 수 없는 경우, eglCreateContext는 EGL_NO_CONTEXT를 반환하고 실패 이유가 설정되어 있으며 eglGetError를 호출하여 얻을 수 있습니다. 현재 알려진 바로는 eglCreateContext가 실패할 수 있는 유일한 이유는 제공한 EGLConfig가 유효하지 않은 경우이며, 이 경우 eglGetError가 반환하는 오류는 EGL_BAD_ CONFIG입니다.

예제 3-6은 적절한 EGLConfig를 선택한 후 컨텍스트를 생성하는 방법을 보여 줍니다.

const EGLint attribList[] =
{
 // EGL_KHR_create_context is required
 EGL_CONTEXT_CLIENT_VERSION, 3,
 EGL_NONE
};
EGLContext context = eglCreateContext ( display, config,
EGL_NO_CONTEXT,
attribList );
if ( context == EGL_NO_CONTEXT )
{
 EGLError error = eglGetError ( );
 if ( error == EGL_BAD_CONFIG )
 {
// Handle error and recover
 }
}

다른 에러도 생성될 수 있지만, 여기서는 잘못된 EGLConfig에러만 확인하겠습니다. EGLContext를 성공적으로 생성한 후에는 렌더링하기 전에 마지막 한 단계를 완료해야 합니다.

Making an EGLContext Current

애플리케이션이 다양한 용도로 여러 EGLContext를 생성했을 수도 있으므로 ,특정 EGLContext를 렌더링 표면과 연관시키는 방법(일반적으로 "make current"라고 하는 프로세스)이 필요합니다. 특정 EGLContext를 EGLSurface에 연결하려면 다음 호출을 사용합니다.

💡 EGLBoolean eglMakeCurrent( EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)

display : specifies the EGL display connection draw : specifies the EGL draw surface read : specifies the EGL read surface context : surfaces에 첨부할 EGL 렌더링 컨텍스트를 지정합니다.

이 함수는 호출에 성공하면 EGL_TRUE를 반환합니다. 실패하면 EGL_FALSE를 반환합니다.

이 호출에는 두 개의 EGL surface가 필요하다는 것을 눈치채셨을 것입니다. 이 접근 방식은 고급 EGL 사용법에 대한 논의에서 활용할 유연성을 허용하지만, 읽기 및 그리기를 모두 동일한 값인 이전에 생성한 창으로 설정했습니다.

참고: EGL 사양에서는 eglMakeCurrent구현을 위해 플러시가 필요하므로 타일 기반 아키텍처에서는 이 호출이 비용이 많이 듭니다.

Putting All Out EGL Knowledge Together

이 장에서는 EGL 초기화부터 EGLContext를 EGL서페이스에 바인딩하기까지의 전체 프로세스를 보여주는 전체 예제를 보여드립니다. 여기서는 네이티브 창이 이미 생성되어 있고 오류가 발생하면 애플리케이션이 종료된다고 가정합니다. 실제로 예제 3-7은 2장 'Hello Triangle'에 표시된 것처럼 필요한 EGL 창 생성 코드를 래핑하는 자체 개발 함수인 esCreateWindow에서 수행되는 작업과 유사합니다: 창 생성과 컨텍스트를 분리하는 루틴을 제외하면(나중에 설명할 이유 때문에) 말입니다.

EGLBoolean initializeWindow ( EGLNativeWindow nativeWindow )
{
 const EGLint configAttribs[] =
 {
EGL_RENDER_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_NONE
 };
 const EGLint contextAttribs[] =
 {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
 };
 EGLDisplay display = eglGetDisplay ( EGL_DEFAULT_DISPLAY )
 if ( display == EGL_NO_DISPLAY )
 {
return EGL_FALSE;
 }
 EGLint major, minor;
 if ( !eglInitialize ( display, &major, &minor ) )
 {
return EGL_FALSE;
 }
 EGLConfig config;
 EGLint numConfigs;
 if ( !eglChooseConfig ( display, configAttribs, &config, 1,
&numConfigs ) )
 {
return EGL_FALSE;
 }
 EGLSurface window = eglCreateWindowSurface ( display, config,
nativeWindow, NULL );
 if (window == EGL_NO_SURFACE)
 {
return EGL_FALSE;
 }
EGLContext context = eglCreateContext ( display, config,
EGL_NO_CONTEXT,
contextAttribs);
 if ( context == EGL_NO_CONTEXT )
 {
return EGL_FALSE;
 }
 if ( !eglMakeCurrent ( display, window, window, context ) )
 {
return EGL_FALSE;
 }
 return EGL_TRUE;
}

이 코드는 애플리케이션이 예제 3-8에서 512 × 512 창을 열기 위해 호출한 경우와 유사합니다.

ESContext esContext;
const char* title = "OpenGL ES Application Window Title";
if (esCreateWindow(&esContext, title, 512, 512,
ES_WINDOW_RGB | ES_WINDOW_DEPTH))
{
 // Window creation failed
}

esCreateWindow의 마지막 파라미터는 창에 원하는 특성을 지정하며, 다음 값의 비트마스크로 지정합니다:

  • ES_WINDOW_RGB - RGB 기반 색상 버퍼를 지정합니다.
  • ES_WINDOW_ALPHA-대상 알파 버퍼를 할당합니다.
  • ES_WINDOW_DEPTH - 깊이 버퍼를 할당합니다.
  • ES_WINDOW_STENCIL - 스텐실 버퍼를 할당합니다.
  • ES_WINDOW_MULTISAMPLE - 다중 샘플 버퍼를 할당합니다.

창 구성 비트마스크에서 이러한 값을 지정하면 적절한 토큰과 값을 적절한 토큰과 값을 EGLConfig 속성 목록에 추가합니다. (즉, 앞의 예제에서 configAttribs)에 적절한 토큰과 값을 추가합니다.

Synchronizing Rendering

여러 그래픽 API의 렌더링을 단일 창으로 조정해야 하는 상황에 직면할 수 있습니다. 예를 들어 OpenGL ES 3.0보다 OpenVG를 사용하는 것이 더 쉽거나 기본 창 시스템의 글꼴 렌더링 기능이 창에 문자를 그리는 데 더 적합하다는 것을 알 수 있습니다. 이러한 경우 애플리케이션에서 다양한 라이브러리를 공유 창에 렌더링할 수 있도록 허용해야 합니다. EGL에는 동기화 작업에 도움이 되는 몇 가지 함수가 있습니다.

애플리케이션이 OpenGL ES 3.0으로만 렌더링하는 경우 glFinish(또는 13장의 "동기화 객체 및 펜스"에서 설명하는 보다 효율적인 동기화 객체 및 펜스)를 호출하기만 하면 모든 렌더링이 이루어졌는지 확인할 수 있습니다. 그러나 렌더링에 둘 이상의 크로노스 API(예: OpenVG)를 사용 중이고 창 시스템의 기본 렌더링 API로 전환하기 전에 어떤 API가 사용되는지 모를 수 있는 경우 이 함수를 호출할 수 있습니다:

💡 **EGLBoolean eglWaitClient()** 크로노스 API(예: OpenGL ES 3.0, OpenGL, OpenVG)를 통한 모든 렌더링이 완료될 때까지 클라이언트 실행을 지연시킵니다. 성공하면 EGL_TRUE를 반환합니다. 실패하면 EGL_FALSE를 반환하고 EGL_BAD_CURRENT_SURFACE 에러가 게시됩니다.

이 함수의 작동 방식은 glFinish와 비슷하지만, 현재 어떤 크로노스 API가 작동 중인지와 상관없이 작동합니다. 마찬가지로 네이티브 윈도우잉 시스템의 렌더링이 완료되었는지 확인해야 하는 경우 이 함수를 호출하세요:

💡 **EGLBoolean eglWaitNative(EGLint engine)** 엔진은 렌더링이 완료될 때까지 기다릴 렌더러를 지정합니다.

EGL_CORE_NATIVE_ENGINE은 항상 허용되며 지원되는 가장 일반적인 엔진을 나타냅니다. 다른 엔진은 구현에 따라 다르며 EGL 확장을 통해 지정됩니다. 성공하면 EGL_TRUE가 반환됩니다. 실패하면 EGL_FALSE가 반환되고 EGL_BAD_PARAMETER 오류가 게시됩니다.

Summary

이 장에서는 OpenGL ES 3.0용 서페이스 및 렌더링 컨텍스트를 생성하기 위한 API인 EGL에 대해 배웠습니다. 이제 EGL을 초기화하고, 다양한 EGL 속성을 쿼리하고, EGL을 사용하여 온스크린, 오프스크린 렌더링 영역과 렌더링 컨텍스트를 생성하는 방법을 배웠습니다. 이제 OpenGL ES 3.0으로 렌더링하는 데 필요한 모든 작업을 수행할 수 있는 충분한 EGL을 배웠습니다. 다음 장에서는 OpenGL ES 셰이더와 프로그램을 만드는 방법을 보여드리겠습니다.