에픽게임즈가 던진 ‘CPU 컬링 병목’이라는 화두는, WebGPU 커뮤니티라는 잔잔한 호수에 던져진 거대한 돌과 같았다. 파문은 빠르게 퍼져나갔고, 마침내 W3C ‘GPU for the Web’ 그룹의 공식 회의 안건으로 상정되었다.
가상 회의실에 다시 모인 웹 그래픽의 거인들. 하지만 이번 회의의 분위기는 이전과 사뭇 달랐다. 이전에는 API의 기본적인 뼈대를 세우기 위해 각자의 이해관계를 조율하는 외교 협상과 같았다면, 지금은 이미 검증된 공통의 목표를 향해 함께 미지의 영역을 탐험하는 공동 탐사대와 같았다.
에픽게임즈의 마일스가 직접 발제자로 나섰다. 그는 자신들의 데모에서 발생한 CPU 병목 현상을 다시 한번 상세히 설명하며, 메시 셰이더(Mesh Shader)의 필요성을 역설했다.
그의 발표가 끝나자, 반론은 없었다. 모두가 동의했다. 문제는 ‘무엇을’이 아니라, 언제나처럼 ‘어떻게’였다.
드미트리가 먼저 입을 열었다.
“우리 모두 메시 셰이더가 가야 할 길이라는 데 동의합니다. 하지만 이것은 기존의 렌더 파이프라인에 기능 몇 개를 추가하는 수준이 아닙니다. 우리는 그래픽스 파이프라인의 새로운 ‘문법’을 정의해야 합니다.”
그는 화면에 전통적인 렌더 파이프라인의 흐름을 그렸다.
[정점 입력] -> [버텍스 셰이더] -> [래스터라이저] -> [프래그먼트 셰이더] -> [출력]
“이것이 우리가 지난 수십 년간 익숙해져 온 문법입니다. 고정된 입력과 고정된 순서. 마치 정해진 공정만 따르는 조립 라인과 같죠. 하지만 메시 셰이더는 이 모든 것을 파괴합니다.”
그는 기존의 다이어그램을 지우고, 완전히 새로운 그림을 그렸다.
“새로운 파이프라인은 두 개의 새로운 셰이더 단계로 구성될 겁니다.”
그가 첫 번째 상자를 그렸다. “Task Shader (또는 Amplification Shader).”
“이 셰이더는 일종의 ‘작업 관리자’입니다. CPU로부터 ‘소행성 10만 개를 그려라’는 식의 아주 거대한 명령을 단 한 번만 받습니다. 그리고 이 관리자는 카메라의 위치나 다른 조건들을 보고, ‘좋아, 이 작업을 처리하기 위해 150개의 작업팀이 필요하겠다’고 스스로 결정한 뒤, 그 작업팀들을 호출합니다.”
그는 다음 상자를 그리고 Task Shader와 연결했다. “Mesh Shader.”
“이것이 바로 ‘작업팀’입니다. 각 작업팀은 자신에게 할당된 작은 임무, 예를 들어 ‘소행성 500개 중에서 보이는 것만 골라내서 지오메트리를 만들어라’ 같은 일을 수행합니다. 이 작업팀은 스스로 컬링을 수행하고, 필요한 정점 데이터(Vertices)와 인덱스 데이터(Indices)를 직접 생성해서 래스터라이저로 보냅니다.”
“결론적으로, CPU는 이제 ‘무엇을 그릴지’ 일일이 고민할 필요가 없습니다. 그냥 ‘여기 재료가 있다’고 던져주기만 하면, 나머지는 GPU 안에서 Task Shader와 Mesh Shader가 협력하여 전부 처리하는 겁니다. CPU의 비명은 사라지게 되죠.”
드미트리의 설명에 모두가 고개를 끄덕였다. 개념은 명확했다. 진짜 논쟁은 이 개념을 어떤 API로 구현할 것인가에서 시작되었다.
애플의 딘이 말했다.
“Metal의 메시 셰이더 파이프라인은 기존의 렌더 파이프라인과 명확히 구분됩니다. 우리도 createMeshPipelineAsync
같은 새로운 함수를 만들어야 합니다. 개발자의 혼란을 막기 위해서요.”
모질라의 알렉스가 반박했다.
“함수를 새로 만드는 것보다, 기존의 createRenderPipeline
에 새로운 셰이더 단계(taskStage
, meshStage
)를 추가하는 편이 더 일관성 있지 않을까요? 파이프라인의 종류가 늘어나는 것은 API를 더 복잡하게 만들 뿐입니다.”
WGSL의 문법에 대한 논의는 더욱 치열했다.
새로운 셰이더 타입을 어떻게 정의할 것인가? @task
와 @mesh
라는 새로운 어트리뷰트를 도입하자는 데에는 의견이 모였다.
Task Shader에서 Mesh Shader로 데이터를 어떻게 전달할 것인가? workgroup_storage
라는 공유 메모리 공간을 사용하자는 아이디어가 나왔다.
Mesh Shader는 최종적으로 생성된 지오메트리를 어떻게 출력할 것인가? Vulkan과 Metal, DirectX 12는 이 부분에서 모두 미묘하게 다른 방식을 사용하고 있었다.
회의는 몇 시간 동안 이어졌다. 이것은 정답을 찾는 과정이 아니었다. 수많은 기술적 트레이드오프 속에서, 가장 합리적이고 미래지향적인 ‘합의점’을 찾아가는 고통스러운 과정이었다.
긴 논쟁 끝에, 그들은 마침내 첫 번째 합의에 도달했다.
WGSL에 @task
와 @mesh
라는 새로운 셰이더 스테이지를 도입하고, 이들을 포함하는 새로운 파이프라인 기술을 ‘메시 파이프라인(Mesh Pipeline)’이라 부르기로 한 것이다.
그날 밤, 드미트리는 자신의 컴퓨터에 새로운 WGSL 파일을 하나 만들었다.
// mesh.wgsl - DRAFT
@task
fn task_main() {
// ...
}
@mesh
fn mesh_main() {
// ...
}
파일에는 단 몇 줄의 코드만 적혀 있었지만, 드미트리는 자신이 지금 새로운 언어의 첫 페이지를 쓰고 있음을 느꼈다. WebGPU 1.0이라는 교과서의 마지막 장을 덮고, WebGPU 2.0이라는 새로운 교과서의 첫 장을 펼치는 순간. 그 빈 페이지를 어떤 내용으로 채워나갈 것인지, 길고 흥미진진한 여정이 다시 그의 눈앞에 펼쳐지고 있었다.