• GPU 프로그래밍 API
    • 그래픽과 컴퓨팅 모두에 활용 가능
    • A7 이상에서 사용 가능
    • 미리 컴파일된 셰이더
    • 멀티스레딩에 적용 가능
  • 배경
    • 모든 draw call은 각자의 상태 벡터를 필요로 한다.
      • 셰이더, 상태, 텍스쳐, 렌더 타겟 등등
    • 상태 벡터를 바꾸는 것은 CPU 입장에서는 비싼 연산이다
      • 소프트웨어에서 하드웨어쪽으로 연산을 옮길 필요가 있다.
    • 메탈은 더 많은 draw call을 빠르게 처리할 수 있다(약 10배)
      • 더 많은 객체들을 표현할 수 있게 한다.
    • 메탈 이전에는 표준적으로 OpenGL, OpenCL이 사용됨
      • 여러 하드웨어에서 돌아가는 표준이기 때문에 최적화하기 힘듦
      • 그래서 메탈은 OS와 하드웨어와 함께 통합됨
  • 메탈 디자인 철학
    • 가능한한 얇은 API
    • 모던한 GPU 기능들을 사용
    • 비싼 연산은 최대한 덜하는 방법으로
    • 예측 가능한 퍼포먼스
    • 명시적인 커맨드 제출
    • CPU의 동작에서도 최적화
  • 앱에서 사용되는 여러 API들의 포지션
    • 씬 그래프: SceneKit, SpriteKit
    • 2D 그래픽과 이미지 처리: Core Animation, Core Image, Core Graphics
    • 표준 기반 3D 그래픽: OpenGL ES
    • 고효율 GPU 접근: Metal
  • 어떻게 이걸하는가?
    • 많은 게임들이 타겟으로 하는 30FPS의 경우, 대략 프레임당 33.3ms안에 처리가 되어야 한다.
    • CPU의 작업은 다음 프레임에 GPU로 넘어가서 처리된다.
    • 하지만 보통 CPU가 할일이 많고, GPU는 노는 경우가 많다.
    • 설상가상으로 GPU 작업이 늘어나면 CPU타임은 그보더 많이 늘어나버린다 -> Idle 타임은 더 늘어나고, 프레임 시간 제한을 맞추지 못하게 된다.
    • 이는 CPU 타임에 GPU API를 호출하는 비용도 포함되기 떄문이다 -> 메탈은 이를 줄이는 것을 목표로 한다
    • GPU API를 호출하는 비용을 줄이면 CPU에 여유가 생기고, 여기에 물리 엔진이나 AI등의 연산을 끼워넣을 여지도 충분히 생긴다.
  • 왜 GPU 프로그래밍 연산은 무거운가? -> 메탈이 고치려는 부분
    • 상태 검증 비용
      • API 사용이 적합한지 검증
      • API 상태를 하드웨어 상태로 인코딩해야 된다.
    • 셰이더 컴파일
      • 런타임에 셰이더의 기계어 코드를 컴파일 한다.
      • 상태와 셰이더 간의 상호 작용도 있다.
    • GPU로 작업을 전송
      • 리소스의 여유 상태 관리
      • 커맨드 배치 처리
  • 무거운 작업은 덜 자주 하자
    • 작업이 일어나는 빈도를 분류하자
      • 앱 빌드 시간에 일어나는 작업 : 사용자가 경험하지 않는 시간
      • 컨텐츠 로딩: 가끔 일어난다.
      • Draw call: 프레임 당 1000번 이상
    • 기존에는 모든 작업이 Draw Call에서 일어났다. -> 이를 분산하자
      • 앱 빌드 떄 셰이더를 컴파일하자
      • 컨텐츠를 로딩할 때 상태를 검증하자
      • GPU작업만 Draw call 할 때 시키자.
  • API 컨셉
    • Metal 객체
      • Device: GPU
      • Command Queue: command buffer의 시퀀스
      • Command Buffer: GPU 하드웨어 커맨드가 담긴 객체
      • Command Encoder: API 커맨드를 GPU 하드웨어 커맨드로 번역함
      • State: 프레임 버퍼 설정, 블렌드, 뎁스, 샘플러 등…
        • Render Pipeline state는 Descriptor에 Vertex format(descriptor), Vertex function, Fragment Function, Blend(Blend Descriptor)등을 설정한 뒤에 만들어 낸다.
        • Depth Stencil State도 Descriptor를 통해서 만든다.
      • Code: 셰이더들
      • Resources: 텍스쳐와 데이터 버퍼(정점, 상수 등)
        • 텍스처 리소스는 입력으로 받을 수도, 렌더링 패스의 일부로 고정적으로 설정할 수 있다.
    • 멀티스레딩 지원: Command Queue가 thread safe하다.
      • 각 스레드에서 버퍼를 만들어서 제출할 수 있따.
    • command 제출 모델
      • command encoder를 통해서 API 커맨드를 하드웨어 커맨드로 바꿔준다.
      • 하드웨어 커맨드는 command buffer에 저장된다.
      • encoder는 Render, Compute, Blit의 세종류로, 모두 하나의 command buffer 타입으로 바뀐다.
        • 각 encoder는 모든 상태를 다 가지고 있기 때문에 타입을 분리하면 타입간 전환 연산이 많이들기 떄문이다.
      • 앱은 가벼운 커맨드 버퍼 타입을 많이 만들어서 제출한다
      • Metal은 command buffer가 실행이 끝나면 앱에 신호를 준다.
      • Command Encoder는 command를 즉시 만든다.
        • 상태 검증은 지연되지 않는다
        • driver에 대한 직접 호출
      • encoding은 병렬적으로 이루어진다.
        • 앱이 이 실행 순서를 조정할 수 있따.
        • 스케일업 하기 좋도록 효율적으로 구현되어 있다.
    • Resource Update Model
      • A7의 통합 메모리 시스템에 맞게 디자인됨
        • CPU와 GPU가 같은 저장소를 공유
        • 암시적 복사 없음
        • 자동적인 CPU&GPU 통합 모델
          • CPU와 GPU는 command buffer 실행 경계마다 쓰기가 일어나는 것을 Observing한다.
          • 명시적인 CPU 캐시관리는 필요하지 않다.
      • 높은 성능 -> 대신 싱크 맞추는 책임은 개발자에게.
      • 리소스 종류는 2가지
      • 리소스 구조(사이즈, 레벨, 포맷)은 바꿀 수 없다.
      • 데이터 버퍼 업데이트
      • 텍스처 업데이트
      • Blit command Encoder를 통해서 GPU 가속된 파이프라인 업데이트
      • 텍스쳐 저장소는 여러 텍스쳐 간에 공유가 가능
      • 데이타 버퍼와 텍스처 데이터를 공유 가능 -> 행 기반 픽셀 데이터로 취급
    • 커맨드 인코더 타입
    • 셰이딩 언어
  • 개발 툴