작성일 :

모바일 GPU의 대역폭 제약

이전 글에서 데스크톱 GPU의 구조와 렌더링 파이프라인을 다루었습니다.

데스크톱 GPU는 IMR(Immediate Mode Rendering) 방식으로 동작합니다.

CPU가 드로우 콜을 제출하면, GPU는 즉시 렌더링 파이프라인 전체를 실행하여 프레임버퍼에 기록하는 구조입니다. 프레임버퍼는 GPU 외부의 VRAM(Video RAM, 비디오 메모리) 에 위치하며, 삼각형 하나를 처리할 때마다 해당 픽셀 위치의 깊이 값을 읽고, 비교하고, 새 색상을 씁니다.


1
2
3
4
5
6
7
8
9
IMR 방식의 메모리 접근 패턴
══════════════════════════════════════════════════════════

  드로우 콜 A ──▶ VS → 클리핑 → 래스터화 → FS → 테스트/블렌딩 ─┐
  드로우 콜 B ──▶ VS → 클리핑 → 래스터화 → FS → 테스트/블렌딩 ─┼─▶ 프레임버퍼 (VRAM)
  드로우 콜 C ──▶ VS → 클리핑 → 래스터화 → FS → 테스트/블렌딩 ─┘

  → 테스트/블렌딩에서 삼각형마다 프레임버퍼 읽기/쓰기 발생
══════════════════════════════════════════════════════════


이 방식은 삼각형마다 프레임버퍼를 반복적으로 읽고 쓰기 때문에 넓은 메모리 대역폭을 전제로 합니다. 데스크톱 GPU는 전용 VRAM과 128~512bit 폭의 메모리 버스를 갖추고 있어 수백~수천 GB/s의 대역폭을 확보할 수 있고, 전원 콘센트에서 수백 W를 공급받으므로 전력 제약도 적습니다.

모바일 환경은 사정이 전혀 다릅니다. 배터리 전력은 수 W에 불과하고, GPU 전용 메모리도 없습니다.


모바일의 대역폭 문제

모바일 기기에서 GPU는 CPU와 물리적으로 같은 칩, 즉 SoC(System on Chip) 안에 통합되어 있습니다.

GPU 전용 메모리가 따로 없고, CPU와 GPU가 하나의 시스템 메모리인 LPDDR(Low Power Double Data Rate) 을 공유합니다. 이 구조를 통합 메모리 아키텍처(Unified Memory Architecture) 라 부릅니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
데스크톱 vs 모바일 메모리 구조
══════════════════════════════════════════════════════════

데스크톱:
┌───────┐              ┌───────┐  128~512bit 버스  ┌──────────────┐
│  CPU  │─── PCIe ────│  GPU  │◀══════════════▶│  전용 VRAM   │
└───────┘              └───────┘  수백~수천 GB/s  │ (GDDR6/HBM)  │
                        별도 칩                    └──────────────┘
                  별도 전원 공급 (수백 W)

모바일 (SoC):
┌──────────────────────────┐  64~128bit 버스  ┌──────────────┐
│ ┌───────┐   ┌──────────┐ │◀═════════════▶│  공유 LPDDR  │
│ │  CPU  │   │   GPU    │ │  25~50 GB/s   │ (시스템 RAM)  │
│ └───────┘   └──────────┘ │ (대역폭 공유)  └──────────────┘
│        SoC 칩 내부        │
└──────────────────────────┘
      배터리 전원 (수 W)
══════════════════════════════════════════════════════════


이 구조는 두 가지 제약을 만들어 냅니다.

메모리 버스 폭의 차이. 위 다이어그램에서 확인할 수 있듯이, 모바일 SoC의 메모리 버스는 64~128bit으로 데스크톱의 절반 이하입니다. 버스 폭이 좁으면 한 클럭당 전송할 수 있는 데이터양이 적습니다.

여기서 클럭이란 메모리 버스의 동작 단위 시간으로, 메모리와 컨트롤러가 한 번 신호를 주고받는 주기입니다. 128bit 버스는 매 클럭마다 128bit(16바이트)를 전송하고, 64bit 버스는 그 절반인 8바이트만 전송합니다.

결과적으로 모바일의 메모리 대역폭은 25~50 GB/s 수준에 머뭅니다.

메모리 대역폭과 전력의 관계. 외부 메모리에 접근하는 행위 자체가 전력을 소비합니다.

LPDDR 메모리에서 데이터를 읽거나 쓸 때마다 메모리 컨트롤러, 메모리 버스, DRAM 셀이 모두 활성화됩니다. ARM의 기술 문서에 따르면, 외부 메모리 접근은 칩 내부 메모리 접근에 비해 약 10배 이상의 에너지를 소비합니다.

모바일 기기는 배터리로 동작하므로 전력 예산이 제한적이고, 외부 메모리 접근을 줄이는 것이 성능뿐 아니라 전력 효율에도 직접 영향을 미칩니다.

이 두 제약이 실제로 얼마나 빠듯한지, 해상도 1920x1080, 60fps 기준의 프레임버퍼 대역폭을 계산해 보면 구체적으로 드러납니다.


1
2
3
4
5
6
7
8
9
10
11
12
프레임버퍼 대역폭 계산
══════════════════════════════════════════════════════════

픽셀 수:       1920 x 1080 = 2,073,600 픽셀
색상 버퍼:     4바이트/픽셀 (RGBA8)
깊이 버퍼:     4바이트/픽셀 (D24S8)
────────────────────────────────────
픽셀당 합계:   8바이트

프레임당 크기: 2,073,600 x 8 = 약 16 MB
초당 프레임:   16 MB x 60 = 약 960 MB/s  ≒  ~1 GB/s
══════════════════════════════════════════════════════════

이 ~1 GB/s는 최종 결과를 한 번 쓰기만 한 경우입니다. 실제로는 삼각형마다 깊이 버퍼를 읽어서 테스트하고, 통과하면 색상과 깊이를 다시 써야 하므로 읽기와 쓰기가 반복됩니다. 같은 픽셀을 여러 삼각형이 덮어쓰는 현상인 오버드로우가 2x라고 가정하면:

1
~1 GB/s x 2(오버드로우) x 2(읽기+쓰기) = ~4 GB/s

멀티샘플 안티앨리어싱(MSAA)까지 적용하면 이 수치는 더 커집니다.


모바일 GPU의 총 대역폭이 25~50 GB/s인 상황에서, 프레임버퍼 접근만으로 4 GB/s 이상을 소비합니다.

프레임버퍼는 GPU가 처리하는 여러 작업 중 하나일 뿐인데, 텍스처 읽기, 버텍스 데이터 로드, 포스트 프로세싱 등이 모두 같은 대역폭을 나눠 써야 합니다.

데스크톱처럼 IMR 방식으로 삼각형마다 외부 메모리를 왕복하면, 대역폭이 빠르게 바닥납니다.

모바일 GPU는 이 문제를 해결하기 위해 렌더링 방식 자체를 바꿨습니다.


Tile-Based (Deferred) Rendering의 원리

모바일 GPU가 채택한 타일 기반 렌더링(Tile-Based Rendering) 으로, 화면 전체를 한 번에 처리하지 않고 16x16 또는 32x32 픽셀 크기의 작은 타일로 나누어 하나씩 렌더링하는 방식입니다.

GPU 칩 내부에는 레지스터, 캐시 등 여러 종류의 고속 메모리가 있으며, 이를 통칭하여 온칩 메모리(On-Chip Memory) 라 부릅니다. 온칩 메모리는 GPU 실리콘 위에 직접 배치되어 있어, 외부 LPDDR에 비해 접근 속도가 빠르고 전력 소비가 낮습니다.

타일이 충분히 작으면, 그 타일의 색상 버퍼와 깊이 버퍼가 이 온칩 메모리 중 타일 렌더링 전용으로 할당된 영역인 온칩 타일 메모리(On-Chip Tile Memory) 에 들어갑니다.

셰이딩 중 깊이 테스트와 색상 블렌딩이 모두 온칩에서 이루어지므로, 앞서 계산한 프레임버퍼 대역폭 문제가 해소됩니다.


“Deferred”가 붙는 이유는 셰이딩 시점에 있습니다.

드로우 콜이 제출되어도 즉시 셰이딩하지 않고, 먼저 모든 삼각형을 타일별로 분류한 뒤 나중에(deferred) 셰이딩합니다.

이 과정을 TBDR(Tile-Based Deferred Rendering) 이라 부르며, 두 단계로 구성됩니다.

1단계: Binning (타일 분류)

GPU는 제출된 모든 드로우 콜에 대해 버텍스 셰이더를 실행하여, 각 정점을 화면 좌표(Screen Space)로 변환합니다.

변환이 끝나면 삼각형이 화면의 어떤 타일에 걸치는지 계산하고, 걸치는 타일마다 해당 삼각형을 등록합니다. 하나의 삼각형이 여러 타일에 걸칠 수 있으므로, 같은 삼각형이 여러 타일에 동시에 등록되기도 합니다.

이 결과물이 타일 리스트(Tile List) 입니다. 각 타일에 그려져야 할 삼각형 목록이 기록되며, 이 데이터는 공유 LPDDR에 저장됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Binning 단계
══════════════════════════════════════════════════════════

화면을 타일로 분할 (간략화한 예시)

  ┌────┬────┬────┬────┬────┐
  │ T0 │ T1 │ T2 │ T3 │ T4 │
  ├────┼────┼────┼────┼────┤      A = 삼각형 A가 걸치는 타일
  │ T5 │ A  │ A  │    │    │      B = 삼각형 B가 걸치는 타일
  ├────┼────┼────┼────┼────┤
  │T10 │ A  │A,B │ B  │T14 │
  └────┴────┴────┴────┴────┘

타일 리스트 (공유 LPDDR에 저장):
  T6:  [삼각형 A]
  T7:  [삼각형 A]
  T11: [삼각형 A]
  T12: [삼각형 A, 삼각형 B]   ← 두 삼각형이 겹치는 타일
  T13: [삼각형 B]
══════════════════════════════════════════════════════════


Binning 단계에서는 이전 글에서 다룬 7단계 파이프라인 중 래스터화 이전까지, 즉 버텍스 셰이더와 클리핑까지만 수행합니다.

여기에 타일 분류를 추가하여 타일 리스트를 구축하는 것이 이 단계의 전부입니다.

래스터화, 프래그먼트 셰이딩, 테스트/블렌딩은 모두 2단계로 미뤄집니다.


Binning은 렌더링의 준비 단계이지만, 모든 삼각형을 처리하고 타일 리스트를 외부 메모리에 기록해야 하므로 그 자체로 비용이 발생합니다.

이 비용은 삼각형 수에 비례하며, 모바일 최적화에서 폴리곤 수를 줄여야 하는 이유 중 하나입니다.

타일 크기도 성능에 영향을 미칩니다.

타일이 작으면 온칩 타일 메모리 사용량이 줄어들지만, 타일 수가 늘어나 Binning과 타일 전환의 오버헤드가 커집니다.

반대로 타일이 크면 타일당 온칩 타일 메모리 요구량이 늘어납니다.

또한 GPU가 확보한 온칩 타일 메모리 총량은 고정되어 있으므로, 타일이 크면 동시에 처리할 수 있는 타일 수가 줄어들어 병렬 처리 효율이 떨어질 수 있습니다. GPU 제조사마다 자사 아키텍처에 최적인 타일 크기를 선택하며, 대표적으로 Mali는 16x16 픽셀, Apple GPU는 32x32 픽셀을 사용합니다.

2단계: 타일별 렌더링

두 번째 단계에서 GPU는 타일을 하나씩 처리합니다.

먼저 타일 리스트에서 해당 타일에 등록된 삼각형 목록을 읽어온 뒤, GPU 칩 내부에 있는 온칩 타일 메모리 를 작업 공간으로 할당합니다. 온칩 타일 메모리는 외부 LPDDR 메모리와 달리 GPU 실리콘 위에 직접 배치된 고속 SRAM(Static RAM)이므로, 접근 지연이 수십 배 짧습니다.


온칩 타일 메모리 안에 색상 버퍼와 깊이 버퍼를 배치한 뒤, 해당 타일에 속한 삼각형들을 래스터화하고 프래그먼트 셰이더를 실행합니다.

깊이 테스트와 블렌딩까지 모든 픽셀 단위 연산이 온칩에서 이루어지므로, IMR에서 병목이었던 외부 메모리 대역폭 소비가 이 구간에서는 발생하지 않습니다.

타일의 모든 삼각형 처리가 끝나면 최종 색상 결과만 외부 메모리의 프레임버퍼에 기록하고, 다음 타일로 이동하여 같은 과정을 반복합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
타일별 렌더링 단계 (타일 T6)
══════════════════════════════════════════════════════════

  외부 메모리에서 타일 리스트 로드: [삼각형 A, ...]
         │
  ┌──────▼───────── 온칩 타일 메모리 ─────────────┐
  │                                                │
  │   래스터화 → FS → 테스트/블렌딩                 │
  │                        ↕                       │
  │                깊이 버퍼 + 색상 버퍼            │
  │                                                │
  └────────────────────┬───────────────────────────┘
                       │ 타일 완료
                       ▼
          외부 메모리 프레임버퍼 (T6 영역에 최종 색상만 기록)
══════════════════════════════════════════════════════════


IMR에서는 삼각형마다 외부 메모리의 프레임버퍼를 읽고 썼지만, TBDR에서는 타일 내 모든 삼각형 처리가 끝날 때까지 외부 메모리에 접근하지 않습니다.

외부 메모리에 기록하는 것은 타일당 최종 색상 한 번뿐입니다.

IMR vs TBDR 비교

두 방식의 핵심 차이는 외부 메모리 접근 빈도입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
IMR vs TBDR: 외부 메모리 접근 비교
══════════════════════════════════════════════════════════

IMR:
──────────────────────────────────────────────────────────
  드로우 콜 → VS → 클리핑 → 래스터화 → FS → 테스트/블렌딩
                                                ↕ (삼각형마다)
                                        외부 메모리 프레임버퍼

  외부 메모리 접근: ████████████████████████████

TBDR:
──────────────────────────────────────────────────────────
  1단계: 드로우 콜 → VS → 클리핑 → 타일 분류 → 외부 메모리에 저장
  2단계: 타일 리스트 로드 →  래스터화 → FS → 테스트/블렌딩
                             (온칩 타일 메모리 내)
                                    ▼ 타일 완료 시에만
                            외부 메모리에 최종 색상 기록

  외부 메모리 접근: ██
══════════════════════════════════════════════════════════


TBDR에도 비용은 있습니다.

Binning 단계에서 모든 삼각형의 버텍스 셰이더를 먼저 실행해야 하므로, 삼각형 수에 비례하는 부하가 발생합니다.

타일 리스트를 외부 메모리에 기록했다가 2단계에서 다시 읽는 비용도 있고, 하나의 삼각형이 여러 타일에 걸치면 타일 경계에서 중복 처리가 발생하기도 합니다.

그럼에도 프래그먼트 셰이딩 중 외부 메모리 접근을 거의 제거한다는 이점이 이 비용을 충분히 상쇄합니다.

대역폭과 전력이 제한된 모바일 환경에서 TBDR은 사실상 유일한 현실적 선택입니다.


온칩 타일 메모리의 이점

TBDR의 핵심 자원은 온칩 타일 메모리입니다. GPU 실리콘 다이(silicon die) 위에 배치된 SRAM(Static RAM) 으로, 외부 LPDDR과는 크기, 속도, 전력 특성이 근본적으로 다릅니다.


크기. 타일 하나를 처리하는 데 필요한 만큼만 존재합니다.

16x16 픽셀 타일 기준으로, 색상 버퍼(RGBA8, 4바이트 x 256픽셀)에 1,024바이트, 깊이+스텐실 버퍼(D24S8, 4바이트 x 256픽셀)에 1,024바이트이므로 합계 약 2 KB입니다.

MSAA를 적용하면 샘플 수에 비례하여 늘어나지만(4xMSAA일 경우 약 8 KB), 여전히 수 KB ~ 수십 KB 범위에 머뭅니다.

GPU 전체에서는 수백 KB ~ 수 MB 규모의 온칩 타일 메모리를 확보하고 있어, 여러 타일을 병렬로 처리할 수 있습니다. 예를 들어, Adreno GPU의 GMEM은 세대에 따라 1~12 MB에 달합니다.

속도. 온칩 SRAM은 외부 LPDDR 메모리보다 접근 지연이 수십 배 짧습니다.

칩 내부의 배선이 외부 메모리 인터페이스보다 넓고 빠르므로 대역폭 면에서도 유리합니다.

깊이 테스트나 블렌딩처럼 같은 픽셀 위치를 반복적으로 읽고 쓰는 연산에서 이 차이가 극대화됩니다.

전력. ARM의 공개 자료에 따르면, 외부 메모리 접근은 칩 내부 메모리 접근에 비해 에너지 소비가 약 10배 이상 높습니다.

배터리로 동작하는 모바일 기기에서 이 차이는 결정적입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
메모리 계층별 특성
══════════════════════════════════════════════════════════

                     접근 속도      전력 소비      용량
─────────────────────────────────────────────────────────
 온칩 타일 메모리     ~1 사이클      낮음          타일당 수 KB
 (GPU SRAM)                                      (전체 수백 KB ~ 수 MB)

 L2 캐시             ~10 사이클      중간          수백 KB
 (GPU 내부)

 외부 메모리          ~100 사이클     높음          수 GB
 (LPDDR)

─────────────────────────────────────────────────────────
* 사이클 = GPU 클럭 1회. 1 GHz GPU 기준 1사이클 ≒ 1 ns
* 외부 메모리 접근의 에너지 소비는 온칩 SRAM 대비 약 10배 이상
══════════════════════════════════════════════════════════


앞에서 살펴본 크기, 속도, 전력 특성이 TBDR의 구체적인 성능 이점으로 이어집니다.

깊이 테스트가 온칩에서 수행됩니다. 불투명 오브젝트를 그릴 때, 각 프래그먼트의 깊이 값을 기존 깊이 버퍼와 비교해야 합니다. IMR에서는 이 비교가 외부 메모리에서 이루어지지만, TBDR에서는 온칩 타일 메모리 안에서 이루어집니다.

깊이 테스트를 통과하지 못한 프래그먼트는 온칩에서 즉시 폐기되므로, 외부 메모리 접근 없이 처리가 완료됩니다.

블렌딩이 온칩에서 수행됩니다. 반투명 오브젝트를 그릴 때, 기존 색상 값을 읽어 새 색상과 혼합(blend)해야 합니다. 이 읽기-수정-쓰기 과정이 온칩에서 이루어지므로 외부 메모리 대역폭을 소비하지 않습니다.

MSAA가 효율적입니다. MSAA는 픽셀당 여러 샘플을 유지해야 하므로 메모리 사용량이 샘플 수에 비례하여 늘어납니다. IMR에서는 이 증가된 데이터가 모두 외부 메모리에 상주하여 대역폭 부하가 커집니다.

TBDR에서는 MSAA 샘플이 온칩 타일 메모리에만 존재합니다. 타일이 끝날 때 리졸브(Resolve) — 여러 샘플을 하나의 최종 색상으로 합치는 과정 — 를 수행하여 결과만 외부 메모리에 기록합니다. 4xMSAA를 적용해도 외부 메모리에 기록되는 데이터량은 MSAA 없이 렌더링할 때와 동일하므로, MSAA의 대역폭 비용이 사실상 무료에 가깝습니다.

타일 완료 후 최종 색상만 외부에 기록됩니다. 깊이 버퍼, 스텐실 버퍼, MSAA 샘플 데이터는 타일 렌더링 중에만 필요하고 이후에는 사용되지 않습니다.

다음 타일이나 다음 프레임에서 온칩 타일 메모리가 초기화되므로, 이 데이터를 외부 메모리에 기록하지 않아도 됩니다.


Unity에서는 이 특성을 활용하는 두 가지 기능을 제공합니다. FrameBufferFetch는 온칩 타일 메모리에 남아 있는 현재 타일의 색상 값을 셰이더에서 직접 읽는 기능이고, Memoryless 렌더 텍스처 설정은 깊이/스텐실 버퍼처럼 프레임 간에 유지할 필요가 없는 데이터를 외부 메모리에 할당하지 않도록 합니다.


Mali / Adreno / Apple GPU 비교

앞 절까지 TBDR의 공통 원리를 다루었습니다. 실제 모바일 GPU 시장에서는 ARM(Mali), Qualcomm(Adreno), Apple 세 제조사가 주요 점유율을 차지합니다. 세 계열 모두 타일 기반 아키텍처를 사용하지만, 타일 내에서 불필요한 작업을 줄이는 전략이 각각 다릅니다.

Mali (ARM)

Mali GPU는 ARM이 설계하여 라이센스하는 GPU IP(Intellectual Property) 로, Samsung Exynos, MediaTek Dimensity 등 다양한 SoC에 탑재됩니다. 타일 크기는 16x16 픽셀이 기본입니다. 세대별 아키텍처 차이가 커서 Android 기기의 GPU 다양성이 넓고, Unity에서 셰이더 복잡도를 낮게 유지해야 하는 배경이기도 합니다.


Forward Pixel Kill (FPK)

Mali의 고유 최적화 기술입니다. 프래그먼트 셰이딩이 진행되는 도중에, 같은 픽셀 위치에 더 가까운 불투명 프래그먼트가 도착하면, 이미 진행 중이던 먼 쪽 프래그먼트의 셰이딩을 즉시 중단합니다.

이전 글에서 다룬 Early-Z 테스트가 셰이딩 시작 전에 깊이를 비교하여 거부하는 것과 달리, FPK는 셰이딩이 이미 시작된 프래그먼트도 취소할 수 있습니다.

FPK 자체는 드로우 순서와 무관하게 동작합니다.

하지만 FPK만으로 모든 불필요한 셰이딩을 제거할 수는 없으므로, Early-Z 테스트의 역할도 여전히 중요합니다.

불투명 오브젝트를 앞에서 뒤(front-to-back) 순서로 그리면 Early-Z가 더 많은 프래그먼트를 셰이딩 전에 거부하여 전체 효율이 높아집니다.

Unity의 Opaque 패스가 Mali와 Adreno 같은 GPU에서 front-to-back 정렬을 적용하는 이유이기도 합니다.

Adreno (Qualcomm)

Adreno GPU는 Qualcomm Snapdragon SoC에 탑재되며, Android 기기에서 높은 점유율을 가진 모바일 GPU입니다.


FlexRender

Adreno는 순수 TBDR 방식만 사용하지 않고, 상황에 따라 IMR과 TBDR을 전환하는 하이브리드 방식을 지원합니다.

장면의 삼각형이 적어서 Binning 오버헤드가 타일링 이점보다 클 때 IMR 경로를 선택하고, 삼각형이 많고 오버드로우가 발생하는 복잡한 장면에서는 TBDR 경로를 사용합니다.

이 전환은 드라이버가 장면 특성을 분석하여 자동으로 판단합니다.


LRZ (Low Resolution Z-test)

Adreno의 대역폭 절약 기술입니다.

Binning 단계에서 각 타일에 대한 저해상도 깊이 맵을 생성하고, 타일별 렌더링 단계에서 이 깊이 맵을 먼저 참조하여 확실히 가려질 프래그먼트를 래스터화 전에 제거합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
LRZ 동작 원리
══════════════════════════════════════════════════════════

1단계: Binning — 저해상도 깊이 맵 생성

  삼각형들의 깊이 정보를 저해상도로 기록
  ┌─────────┬─────────┬─────────┐
  │  z=10   │  z=30   │  z=5    │  ← 영역별 최소 깊이값
  ├─────────┼─────────┼─────────┤     (LRZ 버퍼)
  │  z=25   │  z=15   │  z=40   │
  └─────────┴─────────┴─────────┘

2단계: 타일별 렌더링 — LRZ 버퍼로 조기 판정

  예: LRZ 깊이값 = 10 인 영역

  프래그먼트 (z=90)  →  10보다 멀다   → 가려짐 확실 → 래스터화 생략
  프래그먼트 (z=5)   →  10보다 가깝다 → 보일 가능성 → 정밀 깊이 테스트

══════════════════════════════════════════════════════════

LRZ는 저해상도로 비교하므로 100% 정확하지는 않습니다. 확실히 가려지는 프래그먼트만 제거하고, 경계에 있는 프래그먼트는 보수적으로 통과시킵니다.

그럼에도 대부분의 장면에서 프래그먼트 셰이더에 도달하는 불필요한 프래그먼트를 크게 줄여줍니다.

Apple GPU

Apple GPU는 Apple이 자체 설계한 GPU로, A-시리즈(iPhone) 및 M-시리즈(iPad, Mac) 칩에 탑재됩니다.

초기에는 Imagination Technologies의 PowerVR 타일 기반 기술을 라이센스하여 사용했으나, A11 Bionic(2017년) 이후부터 자체 설계 GPU 아키텍처로 전환했습니다.


HSR (Hidden Surface Removal)

Apple GPU의 핵심 차별점입니다.

타일 내의 모든 삼각형에 대해 래스터화까지만 먼저 수행하고, 각 픽셀 위치에서 실제로 보이는(가장 앞에 있는) 프래그먼트만 식별한 뒤, 그 프래그먼트에 대해서만 프래그먼트 셰이더를 실행합니다. “Deferred”라는 이름이 가장 정확히 적용되는 동작입니다.

Mali의 FPK가 이미 시작된 셰이딩을 나중에 취소하는 방식이라면, Apple의 HSR은 보이는 프래그먼트만 셰이딩을 시작하는 방식입니다.

불투명 지오메트리에 한해, 가려진 프래그먼트에 대한 셰이딩이 하드웨어 수준에서 완전히 제거됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Apple GPU의 HSR 동작 흐름
══════════════════════════════════════════════════════════

타일 T6의 픽셀 P에 삼각형 3개가 겹침:
  삼각형 A (z=50),  삼각형 B (z=10),  삼각형 C (z=80)

1단계: 래스터화 — 모든 삼각형의 위치만 처리 (셰이딩 없음)

  픽셀 P의 깊이 버퍼 변화:
    삼각형 A  →  z=50 기록
    삼각형 B  →  z=10 갱신  (50보다 가까움)
    삼각형 C  →  z=80 무시  (10보다 멀음)

2단계: 셰이딩 — 최종적으로 보이는 프래그먼트만 실행

  픽셀 P  →  삼각형 B만 셰이딩 (1회)
  삼각형 A, C  →  셰이딩 없음

══════════════════════════════════════════════════════════

이 구조 덕분에 Apple GPU에서는 불투명 오브젝트의 그리기 순서(draw order)가 성능에 미치는 영향이 적습니다. 앞에서 뒤로 정렬하든, 뒤에서 앞으로 정렬하든, HSR이 보이는 프래그먼트만 셰이딩하기 때문입니다.

다만 이는 불투명 오브젝트에 한정됩니다.

반투명 오브젝트는 뒤에 있는 색상과 블렌딩해야 하므로 “가려진다”는 판정 자체가 성립하지 않습니다.

이 한계는 Mali의 FPK나 Adreno의 LRZ도 동일합니다.

세 아키텍처 비교 요약

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Mali / Adreno / Apple GPU 비교
══════════════════════════════════════════════════════════

                Mali            Adreno           Apple
─────────────────────────────────────────────────────────
렌더링 방식     TBDR            TBDR + IMR 혼합   TBDR
                                (FlexRender)

타일 크기       16×16           가변              32×32

오버드로우      FPK             LRZ               HSR
최적화          셰이딩 중 취소   래스터화 전 제거   보이는 것만 셰이딩

대표 SoC        Exynos          Snapdragon        A-시리즈
                Dimensity                         M-시리즈
══════════════════════════════════════════════════════════


세 아키텍처 모두 타일 기반입니다.

차이는 타일 내에서 불필요한 작업을 어떻게 줄이느냐에 있습니다.

Mali는 이미 진행 중인 셰이딩을 취소(FPK)하고, Adreno는 저해상도 깊이로 조기에 걸러내며(LRZ), Apple은 보이는 것만 셰이딩을 시작(HSR)합니다.

Unity의 Opaque 패스는 Mali와 Adreno에서 불투명 오브젝트를 앞에서 뒤(front-to-back)로 정렬하여 Early-Z가 가려진 프래그먼트를 더 많이 거부하도록 합니다.

반면 Apple GPU는 HSR이 보이는 프래그먼트만 셰이딩하므로 정렬 효과가 크지 않고, Unity도 HSR을 지원하는 GPU에서는 이 정렬을 생략합니다.


오버드로우가 TBDR에서 특히 비싼 이유

오버드로우(Overdraw) 는 화면의 같은 픽셀 위치에 여러 오브젝트가 겹쳐 그려지는 현상입니다.

배경 위에 건물이 그려지고, 건물 위에 캐릭터가 그려지면, 해당 픽셀은 세 번 셰이딩됩니다.

최종적으로 보이는 것은 가장 앞의 캐릭터뿐이므로, 배경과 건물에 대한 셰이딩은 낭비입니다.

앞에서 살펴본 FPK, LRZ, HSR은 모두 이 낭비를 줄이기 위한 기술이었습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
오버드로우 예시 (한 픽셀 위치)
══════════════════════════════════════════════════════════

  (카메라)    [캐릭터]    [건물]      [배경]
     ──▶       z=10       z=50       z=100

  뒤에서 앞으로 그릴 경우:
  1) 배경 셰이딩     → 결과 기록    ← 낭비
  2) 건물 셰이딩     → 덮어쓰기     ← 낭비
  3) 캐릭터 셰이딩   → 덮어쓰기     ← 최종 결과

  오버드로우 = 3x (한 픽셀에 3번 셰이딩)
══════════════════════════════════════════════════════════


오버드로우는 IMR에서도 낭비이지만, 모바일 TBDR에서는 비용이 더 크게 작용합니다.

제한된 셰이딩 예산

TBDR은 온칩 타일 메모리로 대역폭을 절약하지만, 프래그먼트 셰이더 실행 비용은 줄이지 않습니다. 오버드로우 2x는 프래그먼트 셰이더가 두 배 실행된다는 뜻입니다.

모바일 GPU의 ALU(Arithmetic Logic Unit, 연산 유닛) 수는 데스크톱 GPU의 수천~수만 개에 비해 약 1,000~2,000개 수준이고, 필레이트(Fill Rate) — 초당 처리 가능한 프래그먼트 수 — 도 데스크톱의 수백억~수천억 픽셀/초에 비해 약 150억~500억 픽셀/초로 제한적입니다.

이 한정된 연산 예산에서 보이지 않는 픽셀까지 셰이딩하면 예산이 빠르게 고갈됩니다.


반투명 오브젝트와 오버드로우

FPK, LRZ, HSR 같은 하드웨어 최적화는 모두 깊이 버퍼를 기반으로 동작합니다. 불투명 오브젝트는 깊이 버퍼에 값을 기록하므로 가려진 프래그먼트를 판별하고 제거할 수 있습니다.

반면 반투명(알파 블렌딩) 오브젝트는 깊이 버퍼에 값을 기록하지 않습니다. 깊이 기반 판별이 불가능하므로, 겹치는 만큼 반드시 셰이딩이 실행됩니다.

깊이를 기록하지 않는 이유는 블렌딩 때문입니다. 반투명 오브젝트는 뒤에 있는 오브젝트의 색상이 비쳐 보여야 하는데, 깊이 버퍼에 값을 기록하면 뒤의 오브젝트가 깊이 테스트에서 폐기되어 블렌딩할 색상 자체가 사라집니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
같은 픽셀에 3개 오브젝트가 겹칠 때
══════════════════════════════════════════════════════════

불투명 (앞에서 뒤 순서):
  캐릭터(z=10)  → 셰이딩 ✓  깊이 10 기록
  건물(z=50)    → 폐기   ✗  10보다 멀다
  배경(z=100)   → 폐기   ✗  10보다 멀다
  → 셰이딩 1회

반투명 (뒤에서 앞 순서):
  연기 A(z=50)  → 셰이딩 + 블렌딩
  연기 B(z=40)  → 셰이딩 + 블렌딩
  연기 C(z=30)  → 셰이딩 + 블렌딩
  → 셰이딩 3회 (깊이 판별 불가)

══════════════════════════════════════════════════════════


모바일 게임에서 오버드로우가 빈번하게 발생하는 대표적인 요소는 파티클 시스템, UI, 반투명 머티리얼입니다.

파티클 시스템은 연기, 불꽃, 폭발 등의 이펙트를 표현할 때 반투명 빌보드(Billboard, 항상 카메라를 향하는 사각형) 수십 ~ 수백 장을 겹칩니다. 각 파티클이 화면의 넓은 영역을 덮으면 오버드로우가 급격히 증가합니다.

UI 역시 배경 패널, 그림자, 테두리, 아이콘, 텍스트 등 여러 반투명 레이어가 겹쳐 구성되며, 화면의 상당 부분을 차지합니다.

유리, 물, 홀로그램 등 반투명 머티리얼을 사용하는 오브젝트가 서로 겹치는 경우에도 그만큼 오버드로우가 누적됩니다.

Unity 에디터의 Scene View에서 Overdraw 시각화 모드(상단 드롭다운에서 선택)를 활성화하면, 현재 장면에서 오버드로우가 어디서 발생하는지 색상 강도로 확인할 수 있습니다. 밝은 색일수록 오버드로우가 심한 영역입니다.

오버드로우와 셰이더 복잡도

오버드로우는 프래그먼트 셰이더 실행 횟수를 직접 배가시킵니다.

1
2
3
4
5
6
7
8
오버드로우에 따른 셰이딩 횟수 (1920×1080, 60fps 기준)
══════════════════════════════════════════════════════════

  1.0x   약 1.24억 프래그먼트/초
  2.0x   약 2.49억 프래그먼트/초   ← 2배
  3.0x   약 3.73억 프래그먼트/초   ← 3배

══════════════════════════════════════════════════════════

초당 셰이딩 횟수만 보면 모바일 GPU의 필레이트(150~500억 픽셀/초) 대비 작아 보입니다.

하지만 필레이트는 프래그먼트 셰이더 이후 단계인 ROP(Render Output Unit)의 처리 속도 — 깊이/스텐실 테스트, 블렌딩, 프레임 버퍼 기록 — 만 측정한 값입니다. 프래그먼트 셰이더가 색상을 계산하는 비용은 포함되지 않습니다.

Unlit 셰이더처럼 텍스처 하나만 읽는 경우에는 계산이 가벼워서 필레이트에 가까운 속도로 처리할 수 있고, 오버드로우 2~3x도 견딜 수 있습니다.

하지만 노멀 맵, 스페큘러, 조명 계산이 포함된 셰이더는 텍스처를 여러 장 읽고 수학 연산을 수십 번 수행하므로, 셰이더 실행 자체가 병목이 됩니다.

오버드로우 3x는 이 비싼 셰이더 실행을 3번 반복한다는 뜻이므로, 셰이더가 복잡할수록 프레임 레이트가 급격히 떨어집니다.


TBDR에서 주의해야 할 추가 사항

앞에서 TBDR이 대역폭을 절약하는 원리와, FPK/LRZ/HSR이 오버드로우를 줄이는 방식을 살펴봤습니다. 하지만 TBDR에는 데스크톱 IMR에서는 신경 쓰지 않아도 되는 추가 비용이 있습니다.

타일 메모리와 Resolve

타일의 렌더링이 끝나면 온칩 타일 메모리의 내용을 외부 메모리에 기록하는데, 이 과정을 Store 또는 Resolve 라 부릅니다.

반대로, 이전 프레임의 내용을 다시 사용해야 할 때 외부 메모리에서 온칩 타일 메모리로 불러오는 과정은 Load 라 부릅니다.

이 Load/Store 과정이 타일마다 발생하므로, 불필요한 Load나 Store가 늘어나면 TBDR의 대역폭 이점이 상쇄됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
타일 하나의 렌더링 사이클
══════════════════════════════════════════════════════════
1. Load    외부 메모리 → 온칩 타일 메모리
           (이전 내용이 필요하면 로드, Clear면 생략)

2. Render  래스터화 → 프래그먼트 셰이더 → 테스트/블렌딩
           (모든 연산이 온칩 타일 메모리 안에서 수행)

3. Store   온칩 타일 메모리 → 외부 메모리
           (최종 색상 저장, 깊이/스텐실은 선택적)
══════════════════════════════════════════════════════════
  - Load 생략 = 대역폭 절약
  - 깊이/스텐실 Store 생략 = 대역폭 절약
══════════════════════════════════════════════════════════


Unity에서 카메라의 Clear Flags를 “Solid Color”나 “Skybox”로 설정하면, 어차피 화면 전체를 덮어쓰므로 이전 프레임의 내용이 필요 없습니다.

GPU는 외부 메모리에서 읽는 대신 온칩 타일 메모리를 해당 색상으로 직접 초기화하므로, Load가 생략되고 대역폭이 절약됩니다.

반면 “Don’t Clear”로 설정하면, 새 프레임에서 그려지지 않는 픽셀은 이전 프레임의 색상을 유지해야 합니다.

이전 내용을 보존하려면 외부 메모리에서 Load할 수밖에 없으므로, 그만큼 대역폭을 소비합니다.

렌더 타깃 전환 비용

GPU가 렌더링 결과를 기록하는 대상 버퍼를 렌더 타깃(Render Target) 이라 합니다.

기본 렌더 타깃은 화면에 표시되는 프레임 버퍼이지만, 포스트 프로세싱처럼 중간 결과가 필요하면 별도의 렌더 텍스처(Render Texture) 를 만들어 렌더링합니다.

GPU의 출력 대상을 프레임 버퍼에서 렌더 텍스처로, 또는 그 반대로 바꾸는 것이 렌더 타깃 전환입니다.


데스크톱 IMR은 렌더링 데이터가 항상 외부 메모리(VRAM)에 있으므로, 렌더 타깃을 전환해도 GPU의 쓰기 대상만 바꾸면 됩니다.

반면 TBDR은 렌더링 데이터가 온칩 타일 메모리에 있으므로, 전환할 때마다 현재 타일의 내용을 외부 메모리에 Store한 뒤 새 렌더 타깃의 내용을 Load해야 합니다.

전환 횟수만큼 Store + Load가 반복되므로, 전환이 잦을수록 대역폭 소비가 늘어납니다.


블룸 하나만 해도 밝은 부분 추출 → 블러 → 원본과 합성 과정에서 최소 3번의 렌더 타깃 전환이 발생합니다.

색 보정, 톤 매핑까지 추가하면 효과마다 전환이 더해지므로, 모바일에서 포스트 프로세싱이 많을수록 Load/Store 대역폭 비용이 급격히 늘어납니다.

Alpha Test(Discard)와 TBDR

프래그먼트 셰이더에서 discard(또는 clip) 명령어를 사용하여 특정 프래그먼트를 폐기하는 기법을 알파 테스트(Alpha Test) 라 부릅니다.

나뭇잎, 풀, 철조망 등 복잡한 형태를 사각형 폴리곤에 알파 텍스처로 표현할 때 흔히 사용됩니다.


TBDR에서 알파 테스트는 특별한 문제를 일으킵니다.

앞에서 다룬 LRZ와 HSR은 프래그먼트 셰이더 실행 전에 깊이를 비교하여 불필요한 프래그먼트를 걸러내고, FPK는 셰이딩 도중에도 가려진 프래그먼트의 실행을 취소합니다.

그런데 discard를 사용하면, 프래그먼트 셰이더 실행 결과에 따라 해당 프래그먼트가 폐기될 수 있으므로, 깊이 값이 기록될지 여부도 셰이더 실행이 끝나야 결정됩니다.

즉, discard가 포함된 셰이더에서는 GPU가 프래그먼트 셰이더 실행 전에 깊이를 확정할 수 없습니다.


discard는 렌더링 결과의 정확성을 위해 반드시 실행되어야 하는 셰이더 로직입니다. 나뭇잎 텍스처의 투명한 부분을 폐기하지 않으면 사각형 폴리곤이 그대로 보이기 때문입니다.

반면 깊이 기반 최적화는 성능을 높이기 위한 것이므로, 비활성화해도 렌더링 결과는 동일합니다.

GPU는 정확한 이미지를 먼저 보장해야 하므로, discard가 포함된 셰이더를 반드시 실행하는 대신 해당 드로우 콜에 대해 깊이 기반 최적화를 비활성화하거나 효과를 크게 줄입니다.


따라서 모바일에서는 가능하면, 프래그먼트를 폐기하지 않고 알파 값으로 배경과 색상을 혼합하는 알파 블렌딩(Alpha Blending) 을 사용하는 편이 유리합니다.

discard를 사용하지 않으므로 깊이 기반 최적화에 영향을 주지 않기 때문입니다.

오버드로우 비용은 고려해야 하지만, 깊이 기반 최적화는 드로우 콜 단위로 적용되므로 discard를 사용하지 않는 불투명 오브젝트들은 FPK, LRZ, HSR의 이점을 그대로 유지합니다.

알파 테스트가 반드시 필요한 경우에는 해당 오브젝트의 수를 최소화하는 것이 좋습니다.

버텍스 수와 Binning 부하

데스크톱 IMR에서는 각 삼각형이 버텍스 셰이딩을 마치면 곧바로 프래그먼트 셰이딩으로 넘어갑니다. 여러 삼각형이 파이프라인의 서로 다른 단계에 동시에 있으므로, 버텍스 처리와 프래그먼트 처리가 겹쳐 진행됩니다.

반면 TBDR에서는 먼저 모든 삼각형의 버텍스 셰이더를 실행하고, 각 삼각형이 어느 타일에 속하는지 분류하는 Binning 단계가 완료되어야 타일별 렌더링이 시작됩니다.

삼각형 수가 많으면 Binning 단계가 길어지고, 그동안 프래그먼트 처리 유닛은 유휴 상태로 대기합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TBDR에서 삼각형 수에 따른 부하 분포
══════════════════════════════════════════════════════════

삼각형이 적은 장면:
  Binning:         ████
  프래그먼트 처리:       ████████████████████████
  → 프래그먼트 셰이딩이 병목

삼각형이 많은 장면:
  Binning:         ████████████████████
  프래그먼트 처리:                       ██████████
  → Binning(버텍스 셰이딩)이 병목

──────────────────────────────────────────────────────────
  삼각형 수 → Binning 시간
  셰이더 복잡도 → 프래그먼트 처리 시간
══════════════════════════════════════════════════════════


Binning과 프래그먼트 처리 중 어느 쪽이 병목인지 확인하려면 벤더별 GPU 프로파일링 도구가 필요합니다.

Arm Mobile Studio(Mali), Snapdragon Profiler(Adreno), Xcode GPU Profiler(Apple)에서 두 단계의 시간을 분리하여 볼 수 있습니다.


마무리

  • 모바일 GPU는 CPU와 시스템 메모리(LPDDR)를 공유하며, 메모리 대역폭이 25~50 GB/s로 데스크톱의 약 1/10 수준입니다.
  • TBDR은 화면을 타일로 분할하여 온칩 타일 메모리에서 래스터화, 깊이 테스트, 블렌딩, MSAA를 처리하고, 최종 색상만 외부 메모리에 기록합니다.
  • Mali(FPK), Adreno(LRZ), Apple(HSR)은 각각 다른 방식으로 가려진 프래그먼트의 셰이딩을 줄이지만, 깊이 정보를 기반으로 동작하므로 불투명 오브젝트에서 효과적입니다.
  • 반투명 오브젝트는 깊이 순서대로 뒤에서 앞으로 그려야 하므로 이 최적화의 도움을 받지 못하고, 오버드로우가 프래그먼트 처리 비용을 직접 증가시킵니다.
  • 렌더 타깃 전환의 Load/Store 비용, discard에 의한 깊이 기반 최적화 비활성화, Binning 단계의 삼각형 수 비례 비용은 TBDR 고유의 고려 사항입니다.

모바일 GPU 아키텍처의 핵심은 외부 메모리 접근을 최소화하는 것입니다.

TBDR, 온칩 타일 메모리, 각 제조사의 깊이 기반 최적화는 모두 이 원칙 위에 서 있습니다.

Unity의 렌더 파이프라인도 이 하드웨어 위에서 동작하므로, Built-in Render Pipeline과 URP의 렌더 타깃 전환 횟수, 깊이/스텐실 Store 정책, 포스트 프로세싱 처리 방식 차이가 모바일 성능에 직접적인 영향을 줍니다.



관련 글

시리즈

Tags: GPU, TBDR, Unity, 모바일, 최적화

Categories: ,