Quick Reference
| 단계 | 실행 단위 | 입력과 출력 | 주로 맡기는 일 |
|---|---|---|---|
| Vertex shader | vertex invocation | vertex attribute -> clip position, varying | transform, skinning, per-vertex data |
| Fragment shader | covered fragment 또는 sample | interpolated varying -> color/depth output | material, lighting, texture sampling |
| Compute shader | workgroup 안의 invocation | buffer/texture -> buffer/texture | culling, simulation, image processing |
| Tessellation / geometry | patch 또는 primitive | 추가 geometry data | 지원 범위와 비용을 확인한 특수 기하 처리 |
vertex attributes -> vertex shader -> varyings
-> rasterizer -> fragment shader -> attachment
storage resources -> compute workgroups -> storage resources데이터와 실행 단위
셰이더는 GPU에서 대량의 데이터를 병렬 처리하는 작은 프로그램입니다. vertex shader는 정점마다 실행되고, fragment shader는 rasterization 이후 fragment마다 실행됩니다. fragment의 UV와 normal은 vertex output을 보간해 들어오므로, vertex와 fragment 사이의 location/type/interpolation 규약이 맞아야 합니다.
vertex count -> vertex shader invocation
covered samples -> fragment shader invocation
workgroup/thread -> compute shader invocationGPU는 많은 스레드를 동시에 실행해 처리량을 얻습니다. 하지만 모든 계산이 빠른 것은 아닙니다. 메모리 접근 패턴, 분기, texture sampling, register pressure, bandwidth가 성능에 큰 영향을 줍니다.
geometry shader나 tessellation shader는 API와 플랫폼에 따라 제공되는 추가 기하 단계입니다. tessellation은 patch를 더 작은 primitive로 세분화하고, geometry shader는 primitive 단위로 새 geometry를 만들거나 버릴 수 있지만 현대 실시간 파이프라인에서는 비용과 지원 범위를 먼저 확인해야 합니다. GPU-driven geometry를 원한다면 mesh shader 지원 여부와 fallback도 별도로 정합니다.
fragment shader는 화면 sample 수에 비용이 묶입니다. 같은 shader라도 작은 물체에 쓰면 가볍고, 전체 화면 후처리에 쓰면 모든 픽셀을 훑기 때문에 texture read/write와 bandwidth가 병목이 될 수 있습니다.
Compute shader
compute shader는 화면에 직접 그리는 파이프라인과 분리된 범용 병렬 계산 단계입니다. particle simulation, culling, image processing, prefix sum, tiled lighting 같은 작업에 쓰입니다. workgroup_size(x, y, z)는 한 group 안에서 협력할 invocation 수를 정합니다. 모든 GPU에서 같은 크기가 빠른 값은 아니므로 register 사용량, shared memory, dispatch 크기를 profiler로 확인합니다.
workgroup barrier는 같은 workgroup 안의 shared/storage 접근 순서만 맞춥니다. 서로 다른 workgroup이 쓴 결과를 현재 dispatch 안에서 완전한 순서로 읽는 용도로 쓰면 안 됩니다. 전역 순서가 필요하면 여러 dispatch/pass로 분리하고 resource barrier를 둡니다.
성능을 바꾸는 선택
| 증상 | 의심할 축 |
|---|---|
| shader 명령이 많음 | ALU 비용 |
| texture sample이 많음 | texture unit, cache |
| 큰 render target | bandwidth |
| 분기가 심함 | 같은 wave의 서로 다른 경로 실행, divergence |
| 임시 값이 많음 | register pressure |
| CPU는 한가하고 GPU가 바쁨 | GPU-bound |
성능을 볼 때 셰이더 코드 줄 수보다 실제 instruction, sample 수, 메모리 접근, 화면 점유율을 봐야 합니다. texture lookup은 location이 인접하고 mip가 적절할수록 cache가 잘 맞을 가능성이 크고, 랜덤 storage access와 높은 register pressure는 occupancy를 낮출 수 있습니다.
stage 경계에서 생기는 오류
GPU는 병렬 처리에 강하지만 순차 의존성이 강한 작업에는 맞지 않을 수 있습니다. 이전 결과가 다음 결과에 계속 필요하면 병렬화 이점이 줄어듭니다. fragment shader의 비용도 화면에서 얼마나 많이 실행되는지에 따라 달라지므로, 같은 material이 작은 prop에서는 가벼워도 fullscreen pass에서는 무거울 수 있습니다.
| 증상 | 확인할 계약 또는 제약 |
|---|---|
| mesh가 찌그러지거나 화면 밖으로 감 | vertex layout, matrix convention, clip-space position의 w |
| material 값이 뒤섞임 | varying location/type, flat vs interpolated qualifier |
| compute 결과가 일부만 갱신됨 | dispatch count, bounds check, workgroup size |
| compute 안에서 가끔 이전 값이 읽힘 | workgroup barrier 범위와 pass 간 resource barrier |
| 특정 material만 GPU 시간이 큼 | fragment invocation, texture sample, branch divergence, register count |
참고 링크
1 sources