OpenGLES Programming Guide for iOS 번역 - 6장. OpenGL ES 응용프로그램 디자인 가이드

OpenGL ES/iOS 2011. 9. 21. 20:09

본 번역은 얼마든지 정확하지 않을 수 있습니다. 참고용으로만 보시길... ^^;;;


OpenGL ES는 변환, 쓰기, 클립, 텍스처, 환경 효과 등 대량의 데이터 세트에 대한 많은 복잡한 연산을 개발자를 대신하여 실행합니다. 데이터의 크기와 수행되는 계산의 복잡도는 성능에 영향을 미칠 수 있고, 선명한 3D 그래픽을 생각한 만큼 아름답게 할 수 있습니다. 응용프로그램이 OpenGL ES를 사용하여 몰입형 실시간 이미지를 사용자에게 제공하는 게임인지, 또는 이미지의 품질에 따라 중점을 둔 이미지 처리 응용프로그램인지의 여부를 불문하고, 이 장에 나타난 정보를 이용하여 응용프로그램의 그래픽 엔진 설계에 도움이 됩니다. 이 장에서는 다음 장에서 다루는 주요 개념을 소개합니다.

OpenGL ES를 시각화하는 방법

OpenGL ES를 시각화할 수있는 방법이 몇 가지 방법에 대해 응용프로그램의 설계 및 모니터링을 수행할 컨텍스트가 약간 다릅니다. OpenGL ES를 시각화하는 가장 일반적인 방법은 그림 6-1 과 같은 그래픽 파이프라인으로 시각화하는 것입니다. 응용프로그램은 그래픽 파이프라인을 설정 후 1개 이상의 그리기 명령을 실행합니다. 그리기 명령은 정점 데이터를 파이프라인에 보냅니다. 파이프라인은 정점 데이터를 처리하고 기본으로 빌드한 조각으로 래스터 화됩니다. 각 조각은 색상과 깊이 값을 계산하고 그 값을 프레임 버퍼에 병합합니다. 멘탈 모델로 파이프라인을 사용하는 것은 새로운 프레임을 생성하는 응용프로그램이 실행하는 작업을 특정하는 데 중요합니다. 전형적인 OpenGL ES 2.0 응용프로그램의 경우 디자인은 파이프라인의 정점과 단편 단계 처리, 사용자 정의 쉐이더를 만듭니다. OpenGL ES 1.1 응용프로그램의 경우 희망하는 계산을 수행하는 고정 기능 파이프라인을 구동하는 상태 머신을 수정합니다.

파이프라인 모델의 또 하나의 장점은 각 단계에서 독립적으로하고 동시에 결과를 계산할 수 있다고 말하는 것입니다. 이것은 중요한 포인트입니다. 그래픽 하드웨어 독립적인 부분이 그것까지 전송되는 지오메 트리에 대해 정점으로 조각 계산을 실행하는 동안 응용프로그램은 새로운 기본을 만들 수 있습니다. 파이프라인 중 하나의 단계 작업량이 너무 많은 경우 또는 작업의 실행 시간이 걸리는 경우 다른 파이프라인은 가장 시간이 걸려있는 단계가 작업을 완료할 때까지 유휴 상태입니다. 설계는 장치에 탑재된 그래픽 하드웨어 능력 계산을 적응하여 파이프라인의 각 단계에서 수행되는 작업의 균형을 잡을 필요가 있습니다

중요 : 응용프로그램의 성능을 튜닝하는 경우 첫 번째 단계는 일반적으로 응용프로그램의 병목 단계와 그 이유를 확인하는 것입니다.

그림 6-1. OpenGL ES 그래픽 파이프라인


OpenGL ES를 시각화하는 또 하나의 방법은 클라이언트 서버 아키텍처로 구상하는 것입니다(그림 6-2 참조). OpenGL ES의 상태 변화 텍스처와 정점 데이터 및 렌더링 명령의 모든 응용프로그램에서 OpenGL ES 클라이언트로 보내지도록 해야합니다. 클라이언트는이 데이터를 그래픽 하드웨어가 해석할 수있는 형식으로 변환하고 GPU로 전송합니다. 이 변환 프로세스는 오버헤드가 증가할 뿐만 아니라 데이터를 그래픽 하드웨어에 전달하는 과정에 시간이 걸립니다.

뛰어난 성능을 달성하기 위해 응용프로그램은 OpenGL ES에게 호출 하는 빈도를 줄이고 변환의 오버헤드를 최소화하고 응용프로그램과 OpenGL ES 사이의 데이터 흐름을 주의깊게 관리해야 합니다.

그림 6-2. OpenGL ES 클라이언트 서버 아키텍처


고성능 OpenGL ES 응용프로그램의 설계

간단히 말해서, 확고한 디자인의 OpenGL ES 응용프로그램은 다음을 수행해야 합니다.

 ● OpenGL ES 파이프라인 병렬 처리를 활용한다.

 ● 응용프로그램과 그래픽 하드웨어 간의 데이터 흐름을 관리한다.

 그림 6-3 화면에 표시하는 애니메이션을 OpenGL ES를 사용하여 실행되는 응용 프로그램의 프로세스 흐름을 보여줍니다.

그림 6-3. 자원 관리를 위한 응용프로그램 모델


응용 프로그램을 시작하고 처음 실행하는 응용 프로그램의 실행 중에 변경하지 말아야 리소스 초기화합니다. 이러한 리소스는 응용 프로그램이 OpenGL ES 개체에 캡슐 화하는 것이 이상적입니다. 목표는 실행중인 응용 프로그램 (또는 게임의 수준에 대한 기간 등 응용 프로그램 실행의 일부 기간)로 변경하지 않은 채에 있는 개체를 만들고 초기화 시간의 증가에 대한 대가로 렌더링 성능 을 향상시킬 것입니다. 복잡한 명령 또는 상태 변경은 단일 함수 호출에서 사용 가능한 OpenGL ES 개체로 대체해야 합니다.
예를 들어, 고정 기능 파이프라인의 설정에는 많은 함수 호출을 걸릴 수 있습니다. 대신 초기화할 때 그래픽 쉐이더 컴파​​일하고 단일 함수 호출을 수행할 때 그래픽 쉐이더로 전환합니다. 작성 및 수정에 부하가 발생 OpenGL ES 개체는 대부분의 경우 정적 개체로 만듭니다.

렌더링 루프는 OpenGL ES 컨텍스트에 렌더링하는 모든 항목을 처리하고 그 결과를 화면에 표시합니다. 애니메이션 장면에서는 일부 데이터가 프레임마다 업데이 트됩니다. 그림 6-3과 내부 렌더링 루프에서 응용 프로그램이 렌더링 리소스 업데이트 (프로세스의 OpenGL ES 개체 만들기 또는 수정)와 렌더링 리소스를 사용하는 그리기 명령을 보내고 번갈아 실행합니다.
 내부 루프의 목표는 CPU와 GPU가 병렬로 처리를 응용 프로그램과 OpenGL ES가 동시에 같은 리소스에 액세스할 수 없도록 작업 부하의 균형을 잡는 것입니다.
 iOS는 OpenGL ES 개체의 수정은 수정이 프레임의 시작과 끝에 실행되지 않을 경우, 부하가 높아질 가능성이 있습니다.

이 내부 루프의 중요한 목표는 OpenGL ES 응용 프로그램으로 다시 데이터가 복사되는 것을 피하는 것입니다. GPU에서 CPU에 대한 결과 사본은 아주 시간이 걸릴 수 있습니다. 중앙 렌더링 루프 같이 복사된 데이터가 현재 프레임의 렌더링 프로세스의 일부로 나중에 사용하는 경우 응용 프로그램은 이전에 보낸 모든 그리기 명령이 완료될 때까지 차단 됩니다.

응용프로그램은 프레임에 필요한 모든 드로잉 명령을 보낸 후 결과를 화면에 표시합니다. 대화형이 아닌 응용프로그램은 다음의 처리를 위해 최종 이미지를 응용프로그램이 보유하고있는 메모리에 복사합니다.
마지막으로 응용프로그램이 종료 준비가있을 때 또는 응용 프로그램이 주요 작업을 완료하면 응용프로그램은 OpenGL ES 개체를 해제하고 응용프로그램 자신 또는 다른 응용프로그램에 추가 리소스 사용할 수 있도록합니다.
이 디자인의 특성의 중요한 점을 요약하면 다음과 같이됩니다.

● 실용적인 경우는 반드시 정적 리소스를 만듭니다.

● 내부 렌더링 루프는 동적 리소스 수정 및 렌더링 명령 전송을 번갈아 실행한다. 프레임의 시작과 끝을 제외하고 동적 리소스의 수정을 피하도록한다.

● 중간 렌더링 결과를 응용 프로그램에서 읽기로 돌아가지 않도록한다.

이 장의 나머지에서는이 렌더링 루프 기능의 구현에 도움이 OpenGL ES 프로그래밍 기법을 소개합니다. 이 후 장에서는 다음 일반적인 기법을 OpenGL ES 프로그래밍의 특정 영역에 적용하는 방법을 설명합니다.

● "동기화 및 플래시 작업을 피한다"

● " OpenGL ES 상태 조회를 피하기"

● "OpenGL ES 에 의한 자원 관리 허용"

● "버퍼의 이중화를 사용하여 리소스 충돌 방지"

● "OpenGL ES 상태 변수주의"

● "OpenGL ES 개체의 상태 변경 대체"

동기화 및 플래시 작업을 피하기

OpenGL ES는 대부분의 명령을 즉시 실행하는 것이 아닙니다. 대부분의 경우 명령은 명령 버퍼 큐에 추가되고 나중에 하드웨어를 실행합니다. 일반적으로 OpenGL ES는 버퍼를 하드웨어로 전송하기 전에 상당수의 명령을 응용프로그램이 대기열에 추가할 때까지 기다립니다. 그래픽 하드웨어 명령을 일괄적으로 실행하는 것은 그렇지 않은 경우보다 효율적인 경우가 적지 않습니다. 그러나 일부 OpenGL ES함수는 버퍼를 즉시 Flush 해야합니다. 다른 함수는 버퍼를 Flush 뿐만 아니라 응용프로그램의 제어를 반환하기 전에 이전에 전송된 명령이 완료될 때까지 차단됩니다. 응용프로그램은 Flush 명령과 동기화 명령의 사용을 그 동작이 필요한 경우에만 제한해야합니다. Flush 명령 및 동기화 명령을 사용 너무 많이 사용하면, 하드웨어 렌더링이 완료될 때까지 기다리고 응용프로그램이 중지될 수 있습니다.

다음 상황에서는 OpenGL ES는 실행에 대비 명령 버퍼를 하드웨어로 전송해야 합니다.

glFlush 함수가 명령 버퍼를 그래픽 하드웨어에 보낼 때. 이 함수는 명령이 하드웨어에 보낼 때까지 차단됩니다. 이 명령의 실행 완료까지 기다리지 않습니다.

glFinish 함수가 이전에 보낸 모든 명령이 그래픽 하드웨어에서 실행을 마친 기다릴 때.

● OpenGL 의 상태를 취득하는 함수 ( glGetError 등) 전송된 명령이 완료될 때까지 기다릴 때.

● 명령 버퍼가 꽉 때.

glFlush 의 효과적인 사용

대부분의 경우, 이미지 데이터를 화면으로 이동하는데 glFlush를 호출할 필요는 없습니다. glFlush 함수 호출이 유용한 경우는 매우 적습니다.

● 응용프로그램이 특정 OpenGL ES 객체를 사용하는 렌더링 명령을 전송하여 나중에 해당 객체를 수정하려고하는 경우(또는 그 반대의 경우). 처리되지 않은 드로잉 명령이 OpenGL ES객체를 수정하려고하면 응용프로그램은 그들의 드로딩 커멘드가 완료될 때까지 정지시킬 수 있습니다. 이 상황에서는 glFlush 를 호출하여 하드웨어가 즉시 명령 처리를 시작합니다. 명령 버퍼를 Flush 후 응용프로그램이 보낸 명령과 병행하여 실행할 수있는 작업을 실행해야합니다.

● 2개의 컨텍스트가 OpenGL ES 객체를 공유하는 경우. 공유 객체를 수정하는 OpenGL ES명령을 보낸 후 다른 컨텍스트로 전환 전에 glFlush 를 호출합니다.

● 여러 스레드가 같은 컨텍스트에 액세스하는 경우, 한번에  개의 스레드만이 OpenGL ES에 명령을 보내도록합니다. 명령을 보낸 후 glFlush 를 호출할 필요가 있습니다.

OpenGL ES상태 조회를 피하기

glGet * () ( glGetError () 포함)를 호출하면 OpenGL ES는 상태 변수를 검색하기 전에 그 이전의 명령이 실행되지 않을 수 있습니다. 이러한 동기화를 위해 그래픽 하드웨어와 CPU가 사이를 두지 않고 실행되어야하기 때문에 병렬 처리 기회가 줄어 듭니다. 이것을 방지하려면 조회해야하는 상태의 복사본을 유지하고 OpenGL ES를 호출하지 않고 복사에 직접 액세스합니다.
오류가 발생하면 OpenGL ES는 glGetError 함수로 구할 수있는 오류 플래그를 설정합니다. 개발 단계에서는 glGetError를 호출 오류 검사 루틴을 코드에 포함시킬 중요합니다. 성능이 중시되는 응용프로그램을 개발하는 경우 오류 정보 검색 응용프로그램 디버깅을 수행하는 동안에만 합니다. 릴리스 빌드시에 glGetError 함수를 지나치게 사용하면 시스템 성능이 저하됩니다.

OpenGL ES에 의한 자원 관리 허용

OpenGL ES는 많은 데이터 형식을 OpenGL ES에 영구적으로 저장할 수 있습니다. 정점, 텍스처, 또는 다른 형태의 데이터를 저장하는 OpenGL ES객체를 만드는 것으로, OpenGL ES는 데이터 변환 및 데이터의 그래픽 프로세서로 전송 오버헤드를 줄일 수 있습니다. 데이터가 수정되는 경우에 비해 더 자주 사용되는 경우 OpenGL ES응용프로그램의 성능을 크게 향상시킬 수 있습니다.

OpenGL ES는 데이터를 어떻게 사용하는지에 대해 응용프로그램에 도움말을 제공합니다. 이러한 힌트는 OpenGL ES는 데이터의 처리 방법에 대한 정보를 바탕으로 선택을 할 수 있습니다. 예를 들어, 정적 데이터는 그래픽 전용 메모리 포함 그래픽 프로세서가 쉽게 가져올 수 메모리에 배치할 수 있습니다.

더블 버퍼링 리소스 충돌 방지

리소스 충돌은 응용프로그램과 OpenGL ES가 있는 OpenGL ES 객체에 동시에 액세스할 때 발생합니다. 일방 당사자(응용프로그램 또는 OpenGL ES)가 사용중인 OpenGL ES 객체를 다른 당사자가 수정하려고 하면 양자는 그 오브젝트가 사용 수 없게 될 때까지 블록될 수 있습니다. 객체의 수정이 시작되면 다른 당사자는 수정이 완료될 때까지 해당 객체에 대한 액세스를 허용하지 않습니다. 또는 OpenGL ES가 암시적으로 객체를 복제하여 두 당사자가 명령의 실행을 계속할 수 있도록하는 경우가 있습니다. 두 방법 모두 안전하지만 모두 응용프로그램의 병목이 될 우려가 있습니다. 그림 6-4 은 이런 문제를 보여줍니다. 이 예제에서는 단일 텍스처 객체가 이 객체를 OpenGL ES 및 응용프로그램 모두 사용하려고하고 있습니다. 응용프로그램이 텍스처를 변경하려고하면 응용프로그램은 그것까지 보낸 그리기 명령이 완료될 때까지, 즉 CPU와 GPU가 동기화될 때까지 기다려야합니다.

그림 6-4. 단일 버퍼에 저장된 텍스처 데이터


이 문제를 해결하기 위해 응용프로그램은 객체 변경 객체와 관련된 그리 화면 사이에서 추가 작업을 수행할 수 있습니다. 그러나 실행 가능한 추가 작업이 응용프로그램에 없으면 응용프로그램은 동일한 크기의 두개의 객체를 명시적으로 만듭니다. 따라서 일방 당사자가 객체를 읽는 동안 다른 하나는 나머지 객체를 수정할 수 있습니다. 그림 6-5 에 이중 버퍼링에 의한 접근을 나타냅니다. GPU가 텍스처를 처리하는 동안 CPU가 다른 텍스처를 수정하는 방법입니다. 처음 시작 후 CPU도 GPU도 유휴 상태입니다. 텍스처를 예로 취했습니다. 이 방법은 거의 모든 종류의 OpenGL ES 객체에서 작동합니다.

그림 6-5. 더블 버퍼링되는 텍스처 데이터


더블 버퍼링은 대부분의 응용프로그램에서 사용할 수 있지만 두 당사자가 거의 동시에 명령 처리를 완료해야 합니다. 블록 상태를 방지하기 위해 또한 버퍼를 추가하는 것이 있습니다. 이 경우 기존의 생산자/소비자 모델(traditional producer-consumer model) 이 구현됩니다. 소비자가 명령 처리를 완료하기 전에 제작자가 작업을 완료하면 제작자는 대기(idle) 버퍼를 사용하여 명령을 계속 처리합니다. 이 상황에서 생산자는 소비자의 처리가 상당히 지연될 경우에만 유휴 상태가됩니다.
더블 버퍼링과 트리플 버퍼링은 파이프라인이 정지하는 것을 막을 대신 메모리의 소비량이 늘어 난다는 단점이 있습니다. 메모리 사용량이 증가하면 응용프로그램의 다른 부분에 부담이 걸릴 수 있습니다. iOS 장치는 메모리는 귀중한 자원입니다. 설계에서 메모리 사용량을 늘리는 것으로, 다른 응용프로그램의 최적화와 균형을 잡을 필요가 있습니다.

OpenGL ES 상태 변수 주의사항

하드웨어는 현재 상태를 한개 가지고있어 그것이 집약되어 캐시됩니다. 상태 전환은 부하가 걸리기 때문에, 상태 전환을 최소화 응용프로그램을 설계하는 것이 가장 권장됩니다.
이미 구성된 상태를 설정하지 마십시오. 있는 기능을 한 번 사용하면 다시 활성화할 필요는 없습니다. 활성화 함수를 두번 이상 호출해도 시간 낭비 될 뿐입니다. 이것은 glEnable 또는 glDisable 를 호출할 때 OpenGL ES가 작동 상태를 확인하지 않기 때문입니다. 예를 들어, glEnable (GL_LIGHTING) 을 두번 이상 호출해도, OpenGL ES는 조명 상태가 이미 활성화되어 있는지 확인하지 않습니다. OpenGL ES는 현재 값과 같은 값이면, 해당 상태 값을 업데이트합니다.

상태를 설정하는 호출을 그리는 루프에 배치하는 것이 아니라 전용 설치 루틴 또는 종료 루틴을 사용하여 불필요한 상태 설정을 피할 수 있습니다. 설치 루틴 및 종료 루틴은 특별한 시각 효과를 달성하는 기능의 켜기 끄기에도 유용합니다. 예를 들어 텍스처화된 다각형의 와이어 프레임의 윤곽을 그릴 경우 유용합니다.

2D 이미지를 그리려면 목록 6-1 에 표시된 것과 유사한하는 관련 상태 변수를 모두 해제하십시오.

목록 6-1. OpenGL ES 1.1 의 상태 변수 비활성화

    glDisable (GL_DITHER);
    glDisable (GL_ALPHA_TEST);
    glDisable (GL_BLEND);
    glDisable (GL_STENCIL_TEST);
    glDisable (GL_FOG);
    glDisable (GL_TEXTURE_2D);
    glDisable (GL_DEPTH_TEST);
    // 다른 상태 변수를 적절하게 비활성화한다.


OpenGL ES 객체의 상태 변경 대체

"OpenGL ES 상태 변수 주의" 섹션은 상태 변경 횟수를 줄일 개와 성능을 향상시킬 수 있음을 시사하고 있습니다. 일부 OpenGL ES 확장 기능은 여러 OpenGL 상태 변경을 수집하는 객체를 한개의 함수 호출에 바인딩할 수있는 객체의 형태로 만들 수 있습니다. 이러한 기법이 가능한 경우 사용하는 것이 좋습니다.
예를 들어, 고정 기능 파이프라인의 설정에는 다양한 연산자의 상태를 변경하기 위해 수많은 함수 호출이 필요합니다. 이것은 호출되는 각 함수에 오버헤드가 될뿐만 아니라 코드가 더 복잡 해지고 관리가​어렵습니다. 대신에 쉐이더를 사용합니다. 쉐이더는 컴파일도 같은 역할을 할 수 있지만 glUseProgram 를 한 번만 호출할 필요가 있습니다.
예를 들어, 정점 배열 객체는 정점 속성을 한 번 설정하면 해당 속성을 정점 배열 객체에 저장할 수 있습니다.

: