Babylon.js의 성공적인 데모와 쏟아지는 긍정적인 피드백 속에서, Dawn 팀은 축제 분위기에 젖어 있었다. 그들이 수년간 흘린 땀이 마침내 달콤한 열매를 맺는 듯했다. 그러나 그 단꿈은 한 독립 개발자가 올린 블로그 포스트 하나로 산산조각 났다.
포스트의 제목은 도발적이었다.
“WebGPU는 정말로 더 빠른가? 어떤 상황에서는 아니다.”
글의 내용은 충격적이었다.
그 개발자는 간단한 2D 스프라이트 수천 개를 화면에 그리는 테스트를 진행했다. 하나는 전통적인 WebGL 방식으로, 다른 하나는 최신 WebGPU 방식으로 구현했다. 모두의 예상과 달리, 결과는 WebGL의 압승이었다. 특정 조건에서 WebGPU 버전은 WebGL보다 오히려 2배 가까이 느렸다.
이 포스트는 순식간에 개발자 커뮤니티로 퍼져나갔다.
“이게 어떻게 된 일이지? 차세대 API라면서?”
“역시 복잡하기만 하고 실속은 없는 건가.”
의심의 목소리가 여기저기서 터져 나왔다. 팀 내부의 분위기도 심각하게 가라앉았다. 지금까지의 모든 노력이 부정당하는 듯한 기분이었다.
드미트리는 즉시 팀을 소집했다. 그의 얼굴은 굳어 있었지만, 눈빛은 침착했다.
“패닉에 빠질 필요 없습니다. 이건 공격이 아니라, 우리가 풀어야 할 문제입니다. 저 개발자의 코드를 가져와서 정확히 무엇이 문제인지 분석합시다.”
팀은 해당 개발자의 코드를 클론하여 정밀 분석에 들어갔다. 코드는 단순했다. 수천 개의 스프라이트를 그리기 위해, 매 프레임마다 반복문을 돌며 draw
명령을 수천 번 호출하는 구조였다.
벤이 성능 프로파일러를 돌려보며 중얼거렸다.
“이상합니다. CPU 오버헤드는 분명 WebGPU 쪽이 훨씬 낮습니다. 그런데도 전체 렌더링 시간이 더 오래 걸려요.”
그것은 논리적으로 맞지 않았다. CPU 부담이 적다면 당연히 더 빨라야 했다.
드미트리는 프로파일러의 세부 데이터를 확대하며 문제의 실마리를 찾아 나섰다. 그리고 마침내, 그는 병목의 진짜 원인을 찾아냈다. 문제는 CPU가 아니라, CPU와 GPU 사이의 ‘통신 과정’에 있었다.
“이걸 보게.”
드미트리가 화면의 특정 부분을 가리켰다.
“이 개발자는 매번 draw
를 호출할 때마다 새로운 Bind Group을 생성하고 있습니다. 스프라이트마다 위치 정보가 다르니 어쩔 수 없는 선택이었겠죠.”
“그게 왜 문제가 되죠? Bind Group은 원래 동적으로 생성해서 사용하는 것 아닙니까?”
카이가 물었다.
“맞습니다. 하지만 Bind Group을 생성하고 설정하는 과정은, 비록 WebGL보다 가볍다 해도, 여전히 비용이 드는 작업입니다. 수천 개의 작은 draw
호출과 수천 개의 Bind Group 생성이 매 프레임마다 반복되자, 그 작은 비용들이 눈덩이처럼 불어나 GPU에 명령을 전달하는 파이프라인 자체를 막아버린 겁니다.”
드미트리는 화이트보드에 그림을 그렸다.
“WebGL은 마치 1차선 국도와 같습니다. 느리고 좁지만, 작은 차(명령) 한두 대가 지나가는 데는 큰 무리가 없죠. 반면 WebGPU는 8차선 고속도로입니다. 하지만 이 고속도로에 진입하려면 복잡한 톨게이트(초기 설정 및 객체 생성)를 통과해야 합니다. 거대한 트럭(대규모 렌더링 작업) 한 대를 보내는 데는 압도적으로 효율적이지만, 자전거(작은 렌더링 작업) 수천 대를 한 대씩 톨게이트에 통과시키려 하니, 오히려 국도보다 더 느려진 겁니다.”
모두가 그제야 문제의 본질을 깨달았다. WebGPU는 ‘만병통치약’이 아니었다. 그것은 특정 종류의 작업, 즉 대규모 데이터를 일괄적으로 처리하는 데 최적화된 도구였다. 사용자가 그 철학을 이해하지 못하고 WebGL 스타일로 API를 사용하면, 오히려 성능이 저하되는 ‘역설’이 발생할 수 있었다.
“그렇다면 해결책은?”
벤이 물었다.
“두 가지입니다. 첫째, 우리는 개발자들에게 WebGPU의 올바른 사용법을 적극적으로 알려야 합니다. 이런 경우, 모든 스프라이트의 위치 정보를 하나의 거대한 버퍼에 미리 담아두고, 단 한 번의 drawInstanced
호출로 모든 것을 그리는 것이 올바른 접근법이라는 것을요. 가이드와 문서를 보강해야 합니다.”
“둘째, API 자체도 개선의 여지가 있습니다. 작은 draw
호출이 반복되는 시나리오에서도 성능 저하를 최소화할 수 있도록, 드라이버 레벨에서 Bind Group을 더 효율적으로 캐싱하고 업데이트하는 방법을 연구해야 합니다. 이건 Dawn 내부의 최적화 과제입니다.”
의심의 그림자는 팀에게 중요한 교훈을 남겼다. 최고의 도구라도 사용법을 모르면 흉기가 될 수 있다는 것. 그리고 API 설계자는 사용자가 어떤 실수를 저지를지 항상 예측하고, 그에 대한 대비책을 마련해야 한다는 것.
드미트리는 그날 밤, 해당 블로그 포스트에 직접 장문의 댓글을 남겼다. 그는 문제의 원인을 기술적으로 상세히 설명하고, 더 나은 접근법을 제시하며, API 개선을 약속했다. 그의 진솔하고 겸손한 답변에 커뮤니티의 의심은 점차 이해와 기대로 바뀌어갔다.
이 사건을 계기로 WebGPU는 한 단계 더 성숙해졌다. 단순히 빠르기만 한 API가 아니라, 개발자들이 그 성능을 온전히 이끌어낼 수 있도록 돕는, 더 친절하고 예측 가능한 API로 진화하기 시작한 것이다.