본문 바로가기
OSS

OpenGL ES 3.0 스터디 3주차 공부자료

by mazayong 2023. 8. 7.

3강
(궁금했던 점: *로 표시. 해결된 부분: --로 *옆에 중첩해서 표시.)
(()) : 스터디시 질문에 대한 답변.

    • EGL에 대한 이야기.
    • 2강처럼 자체함수를 사용하면 개념 이해에는 용이할 수 있지만, 실제 opengl es 3.0으로
      작업하기에는 어려울 수 있으므로 EGL을 사용해서 하나의 기능 구현하는 걸 예시로 보여줘서
      EGL에 대해서 알아볼 것.

플랫폼별 다른 방식으로 opengl 윈도우 관리하지 않고 통합적으로 관리하기 위해 만들어진 것.

 

EGL : 장치의 native windowing system과 통신.

  • 사용가능한 drawing surfaces의 유형을 및 구성 보여줌.
  • drawing surfaces 만들어서 그래픽스 컨텍스트 연결.
  • OpenGL ES 3.0과 같은 기타 그래픽 렌더링 API 간의 렌더링 동기화.

(OpenVG((1**--)하드웨어 가속 벡터 그래픽을 위한 것.) 기본 드로잉 명령)
-> egl: 크로노스 렌더링 api와 아래 단의 네이티브 플랫폼 윈도우 시스템 사이의 인터페이스.

(++용어정리
네이티브 플랫폼 윈도우 시스템: 디스플레이 스크린의 각 부분을 관리하는 sw
그래픽스 컨텍스트: 윈도우의 렌더링 데이터 저장하는 구조체.(각 윈도우들 구분 가능. 개별적 렌더링)
서피스: 이미지 렌더링을 위해 생성될 이미지를 저장하려고 할당된 메모리(이미지 버퍼)를 추상화한 것.
-> 추상화되어서 이미지 버퍼 자체를 신경쓰지 않고 관리 가능.
싱크로나이징 렌더링: opengl 렌더링 파이프라인에서 완전히 실행했는지 확인하는 프로세스.
)

 

1) hardware-accelerated vector graphics?
오픈 벡터 그래픽의 파이프라인을 하드웨어로 설계->벡터 그래픽 처리 수행 성능 증가,
소프트웨어 렌더링 처리에 사용하던 openvg 벡터 콘텐츠를 별도 변환 없이 사용하는 하드웨어 방식 벡터 그래픽 가속기.

EGL : 기본 window 시스템 - opengl es 3.0 사이에서 glue 레이어 제공.
(2**--) glue layer?-> 데이터 변환. 해당 디스플레이 형식에 맞게.
EGL이 사용 가능한 drawing surface/ 다른 기능 확인하려면 윈도우와의 통신 채널 열어야 함.

 

 

 

 

코드 돌아가는 과정

  1. 로컬 EGL 디스플레이와의 연결 생성 및 초기화.
    (윈도잉 시스템은 다른 semantics를 가져서 윈도잉 시스템과의 인터페이스를 위해
    모든 시스템 종속성을 캡슐화하는 EGLDisplay 제공.)
    우선 로컬 윈도우 시스템과 연결할 수 있는지 확인하고, EGL 초기화 여부를 확인 후 진행합니다.

1.1 eglGetDisplay
장치 컨텍스트에 대한 핸들-HDC(마소 윈도우 기준)
((다양한 운체 윈도잉시스템 추상화.
default display: 기본 디스플레이.))

 

1.2 eglGetError()
해당 함수를 사용해 호출 실패 여부 및 이유를 리턴해줌.
특정 스레드에서 호출 완료된 가장 최근 EGL 함수의 오류 코드 반환.
(에러가 생길 때만 선택적으로 반환해서 제대로 작동하는 경우에 대해 에러 확인 횟수 줄일 수 있음.)
((언제 nodisplay? 데이터센터 들어가는 경우(CLI가 없어서) 에러냄.))
((매번 호출-runtime상에서 많이 하므로 에러 호출 줄이기. 최근 라이브러리는 다른 개념으로 더 접근.
이건 egl만의 특성.
cpp assault와 같은 것을 사용해 디버거는 에러체크, 릴리즈는 그냥 없애는 방식.
))

 

1.3 eglInitialize
EGL 디스플레이 연결 지정.
반환한 egl implementation 메이저 버전, 마이너 버전 번호 반환.
(3**메이저버전, 마이너 버전을 왜 2개나 반환하는 거지?)
((struct-값 복사, class는 레퍼런스로 감.(자바)
포인터이기 때문에 이렇게 사용함.
))

  1. egl 초기화 후 렌더링 표면의 유형/구성 결정. -> 사용할 메모리 크기 할당받는 과정+해당 메모리에 맞는 configs 가져옴.
    이 방법
    • 모든 surface configuration을 찾아서 최적의 configuration(4**--조건이라고 우당탕탕 해석) 찾기
    • 요구사항 지정하고 egl에서 가장 잘 맞는 것을 찾는다.

 

2번째가 더 쉬움.

  • egl은 특정 surface, 그 특성에 대한 정보 포함된 eglConfig반환.(각 컬러 컴포넌트 비트 수/해당 eglconfig와 연관된 depths buffer 있는 경우(5**-- 문장 이해)

eglGetConfigs
egl display 연결 지정, configs 지정, configs 크기 지정, 반환되는 구성 크기 지정.

호출 방법 2개.
(configs-null : 시스템은 egl-true, numconfigs를 사용가능한 eglconfigs 수로 지정.-> 컴퓨터가 임의로 이만큼 사용할 꺼라고 지정.)
(초기화되지 않은 eglconfig 값의 배열 할당하고 configs 파라미터로 eglGetConfigs에 전달 가능.
=> maxReturnConfigs를 할당된 배열 크기로 설정하면 반환되는 configs 최대 개수 지정가능. 호출하면 됨.
리턴된 값의 목록을 처리하기 시작해 필요에 따라 가장 적합한 config 결정. -> 유저가 임의로 지정해서 할 수 있음)

 

 

왜 이런 2가지 호출 방법이 있지? 그러면 두 번째는 최대개수를 지정한 후 그에 맞는 모든 configs들을 가져와서 내가 정하는건가?(6**--)

((고급과정에 들어가면 specific하게 더 원하는 걸 선택할 수 있는거지 일단 여기서는 디폴트로만 알려줌.
쿼리도 필요없음.
))

 

 

  1. Querying EGLConfig Attributes (eglconfig 설명 + 해당 값 검색)
    eglconfig에서 원하는 attributes 검색하기.
    display: egl connection 점검하기.
    config: 쿼리할 configs들
    attributes : 리턴할 attributes
    value: 특정 값? (7**-- -> 잘 모름. egl 자체가 추상화된 인터페이스여서 크게 중요하지 않음.)

이 함수 옵션이 굉장히 많으므로 eglChooseconfig를 사용해서 가장 맞는 구성 반환.
(8**-- 요청에 가장 적합한 config라는 의미가 뭐지? 해당 윈도우 조건에 맞는/내가 렌더링할
조건에 맞는 버퍼 번호와 같은 것을 렌더링한다는 것)
(9** 적합한 config를 어떻게 알고 알려주는거지?-> 해당 버전+기기 조건 알고 있으니 그냥 조건에 맞게 출력하나?)

3-1. eglChoose ->
((best 조건 뽑아줌.
해당 조건 depts(rgb 565로 꼭 설정되는 건 아님.)
))

 

디스플레이 연결 지정
attribList: configs로 일치시킬 config 목록 지정.
configs: configs 목록 지정.
maxReturnConfigs: Configs 크기 지정.
numConfigs: 반환되는 Configs 크기 지정.

반환값 3가지.

올바른 작동에 중요한 모든 attributes에 대해 기본값, 속성 목록 제공.

(10**--egl_config_caveat : 경고값. 어떤 렌더링 방식인지 알려주는 것
20**-- egl_slow_config와 non-conformant_config 차이? )

성공적으로 반환시 그릴 수 있는 충분한 정보 얻음. 기본적으로 egl_surface_type 지정 안하면
사용자가 온스크린 창을 원한다고 가정.

 

 

  1. on-screen 렌더링할 부분 만들기
    eglcreateWindowSurface 호출.
    egl display 연결 지정
    configuration 설정
    nativewindowtype 설정
    attribList: window attributes list 지정. null일 수도 있음.
    (다른 렌더링 api를 지원해서 일부 속성 적용되지 않음.
    렌더링할 프론트버퍼/백 버퍼 지정할 때 단일 attrib 사용.
    ((11** --왜 단일을 사용하지?
    더블 버퍼링 레이턴시 늘어남.(지금 늘어난 게 다음 버퍼링에 나와서.)
    vr-바로바로 반응성이 중요해서 반응성 하나만 씀. 화면 주사-내가 렌더링하는 거 타이밍이 맞게 나오는 게 중요해서 싱글 버퍼만 씀.
    ))
    하나의 버퍼를 사용해서 더블 버퍼링 기법을 사용하므로 하나의 메모리 주소만 가져오면 되서 싱글을 쓰나 )
    eglconfig + display connection argument로 받음.

해당 함수가 실패할 경우가 여러가지이므로 해당 경우를 확인하기 위해 EglGetError함수 이용.

 

 

  1. off-screen rendering을 위한 egl pbuffers
    눈에 보이지 않는 off-screen surfaces로 렌더링 가능.
    pbuffer: 텍스쳐 맵 생성에 주로 사용.
    텍스쳐에 렌더링만 하는 경우는 다른 것을 써도 좋지만, 프레임버퍼오브젝트를 사용할 수 없는 경우는
    pbuffer 유용.
    (opengl es로 오프스크린 표면 렌더링 후 다른 api에서 텍스쳐로 사용? 이게 무슨 말이지?
    13**이러면 텍스쳐맵 생성된 걸
    다른 api에서 갔다 사용하는 케이스를 말하는건가?)

pbuffer은 egl_surface_type값 추가해야 함.
pbuffer에 맞는 코드 추가(14**--차이: 렌더링 완료되면 버퍼 교체가 아닌 버퍼값을
애플리케이션으로 복사하거나 버퍼의 바인딩 텍스쳐로 수정. 왜 버퍼값 복사하지?
double buffering을 안쓰나?

--> opengl은 싱글 스레드여서)

 

 

  1. Rendering Context 만들기
    rendering context: 작동에 필요한 모든 상태 정보를 포함하는 opengl es 3.0 내부 데이터 구조.

eglcreateContext로 context 생성.
shareContext: 여러 컨텍스트가 특정 유형 데이터 공유 가능.
attribList: egl_context_client_version만 허용됨.
(이거 지정해야하는 이유? 왜 이거해야 올바른 형태를 반환하는거지?
사용중인 es버전과 관련된 유형을 지정해서 버전을 맞추는건가? 14**)
((sharedContext는 지금 알 필요 없음. 하나하나 자세히 용어를 뜯어볼 필요는 없음.))

 

 

  1. Making on eglcontext current
    make current란 프로세스가 목적. (특정 eglContext를 렌더링 표면과 연관시키는 방법.
    app이 다양한 용도로 여러 eglContext생성했을 수도 있으므로.)
    ((surface, context 사용해서 드로우하겠다는 게 목적. 이 함수가 없으면 그릴 수 없음.
    클래스 set함수 느낌.(property 설정 느낌.)

-> 비싼 함수는 맞음. 일반적 코딩은 surface 1, context 1. 모바일 gpu는 비싼데 다른데서는 비싸지 않을 수도 있음.
그냥 필요하면 쓰는 함수의 느낌.
이 context갖고 surface에 그릴 것. 이 context가 바뀌지 않았다면 굳이 이 함수 호출할 필요 없음.
화면 로테이션할 경우도 해당 함수 호출.
))

 

eglMakeCurrent((이게 제일 중요.))
display 커넥션 점검
draw: draw surface
read: read surface
context: surfaces에 첨부할 렌더링 컨텍스트 지정.
여기서는 flush필요. (타일 베이스 아키텍쳐는 비효율적.
타일 베이스: 프레임버퍼 전체 매번 갱신이 아닌 타일마다 렌더링하는 것.
드로우콜 발생 시 즉시 프레임버퍼에 기록하는 게 아닌 칩셋에 내장된 메모리 존재하는 타일만 렌더링.
실제 도형 그려지는 타일만 렌더링.)
https://3dmpengines.tistory.com/2045
++드로우콜-cpu frame에 어떤 거 그릴지 정하고 gpu에 오브젝트 그리라고 명령 호출.

  1. putting all out egl knowledge together
    egl초기화~context를 egl surface에 바인딩까지 과정.
    (창 생성과 컨텍스트 분리하는 부분 제외하면 나머지는 유사.)
  1. Synchronizing Rendering -> 어떤 api가 현재 렌더링되어있는지, 모든 렌더링 이뤄졌는지 확인.
    그래픽 api 렌더링을 싱글 창에 띄워야 할 수 있음.
    다양한 라이브러리를 윈도우에 렌더링하도록 해야 함.

 

opengl es 3.0으로만 하면 glFinish만 호출하면 됨.
둘 이상의 크로노스api(17**--크로노스: 그래픽스 관련 라이브러리 회사.) 사용+ 윈도우 시스템을 기본 렌더링 api로 전환 전 어떤
api 사용하는지 모를 때
eglWaitClient(): 크로노스api를 통한 모든 렌더링 완료될 때까지 클라이언트 실행 지연.
((openvg에서 그림->위에 opengl 시작해서 그리고 싶을 때 동기화필요할 때 쓰고 싶음.
openvg 안쓸꺼 같아서 넘어가도 됨.))

glFinish와 비슷, 어떤 크로노스api가 작동중인지와 별개로 작동.
(18**glFinish는 크로노스api 다른 거 작동중이면 멈추나?--
glfinish-다른 그래픽스 api 다 렌더링될때까지 결과 나오지 않음.
eglWaitClient-해당 함수 전에 만들어진 렌더링 api는 해당 함수 다음에 만들어진 native rendering call 전에 다 호출되는
거 확인+호출해서 실행되게 설정.
)

 

((egl이 여러 윈도우 시스템 관리 위해 만들어진 라이브러리.
surface, context 생성 및 eglmakeCurrent함수 써서 만들어야 함.
구현하는데서 어케했는지 차이지 원래 스펙은 안나와야 하는 게 맞음.

opengl이 윈도우에 렌더링 위해서는 egl context 필요. (윈도우 접근 방법은 Egl만 알기 때문.)
어떤 데이터 갖고있는지는 정확하게 모름. egl context: opengl v, egl v 담음. window와 연결해서 opengl그려지도록
설정, 기타 메모리 자원 등 초기화해놓는 것.
--> 상태 정보 담고있다는 개념 정도로 추상화해서 생각하면 됨.
))

 

 

((모바일은 화면을 잠궈서 어플에 접근 못하게 만들려고 함.
이 상황에서 context/surface 등 데이터 찾아서 가져오려고 하려고 하면 (background task)
디스플레이켜짐+프레임 상태 바뀌는 상태에서 디스플레이 실제로 키면 최종 버전 팝업 되는건지?
--> 계속 렌더링 가능. 디스플레이도 자신만의 큐가 있음 계속 렌더링 데이터가 쌓이면 못받게 하거나 최신 데이터 받게 하거나
등의 작용 가능. app lifecycle 콜백대로 일반적으로 진행됨.
pose/lockscreen되어도 해당 어플리케이션 렌더링 과정이 붕괴되지 않음.

뒤에서 알아서 백그라운드에 도는 거는 현실적으로 쓰이지 않음.(렌더링은 무거운 작업. 그래서 업데이트/렌더링 둘을 나눔.
렌더링 필요없-업데이트, 렌더링은 최신 상태만 갖고 수행함.
-> 상태관리는 따로 해줘야 함. 결국 업데이트와 렌더링을 분리하기 위해 그렇게 만들어둠.
)

듀얼 모니터 렌더링: egl Context 2개일 때 egl way client 해줘도 됨.
듀얼 모니터 써도 윈도우즈 시스템에서 윈도우를 하나만 보냄. 그래서 surface 하나. 두 개 주면 EglmakeCurrent 사용하면 됨.

eglmakeCurrent에서 draw, read에 pbuffer 넣어도 됨?
--> pbuffer: 윈도우 없을 때 무언가를 만드는 것이므로 가능.
read를 이용해 삼각형을 그릴 때 그려지는 장소가 draw.
현재 모니터에 그려져 있는 것을 read 가능. 이 때 read(내가 읽을 데이터)에 데이터 넣음.

readsurface = drawsurface. 동영상 플레이어 위에 자막 넣고 싶음. 동영상 코덱에서 불러오는거=Read,
draw는 자막을 만들어서 새 버퍼에 넣기.
drawbuffer = default frame buffer.
))