핵심 정리
scene data
-> CPU setup
-> vertex shader
-> primitive assembly
-> rasterization
-> fragment shader
-> depth/blend/output
-> framebuffer| 단계 | 하는 일 |
|---|---|
| CPU setup | 메시, 재질, 텍스처, 상수 버퍼, draw call 준비 |
| Vertex shader | 정점을 clip space로 변환 |
| Rasterization | 삼각형을 화면 픽셀 후보로 변환 |
| Fragment shader | 픽셀 후보의 색, normal, material 계산 |
| Output merger | depth test, stencil, blending 후 framebuffer 기록 |
구조
렌더링 파이프라인은 3D 장면을 2D 화면 이미지로 바꾸는 일련의 단계입니다. CPU는 어떤 메시를 어떤 상태로 그릴지 명령을 만들고, GPU는 수많은 정점과 fragment를 병렬로 처리합니다.
CPU setup에서는 draw call, pipeline state, bind group이나 descriptor, vertex/index buffer, uniform 같은 입력을 묶습니다. 이 단계가 정리되어야 GPU가 어떤 shader로 어떤 geometry를 어떤 render target에 그릴지 알 수 있습니다.
vertex shader 이후에는 primitive assembly가 정점들을 삼각형, 선, 점 같은 primitive로 묶습니다. rasterization은 이 primitive가 화면의 어떤 sample을 덮는지 계산하고, fragment shader는 그 sample 후보마다 색이나 material 값을 계산합니다.
output merger는 fragment 결과를 그대로 저장하지 않습니다. depth test와 stencil test로 남길지 버릴지 판단하고, 기존 color attachment와 blending한 뒤 framebuffer에 기록합니다.
중요한 구분은 정점 단계는 기하 정보, fragment 단계는 화면 샘플 후보를 다룬다는 점입니다. 정점이 많으면 vertex shader 비용이 커지고, 화면을 많이 덮으면 fragment shader와 fill-rate 비용이 커집니다.
정점이 많음 -> vertex 처리 비용
큰 면이 겹침 -> rasterization 이후 fragment 비용
투명/후처리 -> blending, bandwidth 비용고정 기능과 프로그래머블 단계
현대 GPU 파이프라인은 일부 단계가 고정 기능이고, 일부 단계는 shader로 작성합니다. vertex shader와 fragment shader는 대표적인 프로그래머블 단계입니다. 반면 rasterization, depth test, blending은 설정은 가능하지만 기본 동작 자체는 고정 기능에 가깝습니다.
병목 읽기
| 증상 | 먼저 의심할 곳 |
|---|---|
| 정점 수 증가에 따라 느림 | vertex processing |
| 해상도 증가에 따라 느림 | fragment, fill-rate, bandwidth |
| draw call이 많을수록 느림 | CPU submission, state change |
| 투명 물체가 많을수록 느림 | overdraw, blending |
| 복잡한 재질에서 느림 | fragment shader, texture sampling |
성능 문제를 볼 때 “GPU가 느리다”로 뭉뚱그리면 원인을 찾기 어렵습니다. 정점 수, 화면 점유율, 텍스처 샘플 수, blending, 메모리 대역폭 중 어떤 축이 늘었는지를 분리해야 합니다.
주의할 점
렌더링 파이프라인은 단순히 위에서 아래로 한 번 흐르는 도식이 아닙니다. 실제 GPU는 병렬 처리, 캐시, 타일 기반 렌더링, 드라이버 최적화가 섞여 동작합니다.
그래도 이 단계 구분은 문제를 추적하는 기준점입니다. 어느 단계의 입력이 어느 단계의 비용으로 바뀌는지 먼저 나누면 그래픽스 버그와 성능 병목을 훨씬 정확하게 읽을 수 있습니다.
참고 링크
2 sources