셰이더의 재사용

632025년 09월 03일4

다중 GPU 지원과 같은 거대한 미래를 논의하는 동안, 드미트리의 발밑에서는 더 현실적이고 시급한 문제들이 조용히 끓어오르고 있었다. 그중 하나는 W3C의 깃허브 이슈 트래커에 꾸준히 제기되는, 한 가지 패턴의 불만이었다.

“WGSL은 왜 #include나 import 같은 모듈 시스템을 지원하지 않나요?”

이 질문은 주로 대규모 프로젝트를 진행하는 개발자들에게서 나왔다. 그들은 수천, 수만 줄에 달하는 복잡한 셰이더 코드를 작성하고 있었다.

한 게임 스튜디오의 리드 렌더링 엔지니어가 올린 글은 그 고통을 구체적으로 보여주었다.
“저희는 물리 기반 렌더링(PBR)을 위해, 빛의 반사를 계산하는 복잡한 수학 함수들을 라이브러리로 만들어 두었습니다. 그런데 저희가 만드는 모든 재질(Material)의 셰이더 코드에, 이 라이브러리 함수 코드를 전부 복사-붙여넣기 해야만 합니다. 만약 라이브러리 함수 하나를 수정하면, 수십 개의 셰이더 파일을 일일이 찾아다니며 수정해야 하죠. 이건 끔찍한 악몽입니다.”

그의 말은 모든 소프트웨어 엔지니어링의 기본 원칙, ‘코드를 재사용하라(Don't Repeat Yourself, DRY)’가 WGSL에서는 통하지 않는다는 절규였다.

드미트리는 이 문제를 누구보다 잘 이해하고 있었다. WGSL의 초기 설계 당시, 이 모듈 시스템에 대한 논의는 분명히 있었다. 하지만 그때는 다른 우선순위에 밀려 뒤로 미뤄졌었다.

그 이유 중 하나는 ‘보안’이었다.
만약 #include "http://evil.com/malicious.wgsl" 같은 코드가 허용된다면, 셰이더는 외부의 악성 코드에 무방비로 노출될 수 있었다.
또 다른 이유는 ‘복잡성’이었다. 모듈 시스템을 도입하는 것은, WGSL 컴파일러의 구조를 완전히 새로 짜야 하는 대수술이었다. 의존성 해결, 순환 참조 방지, 네임스페이스 관리 등, 프로그래밍 언어 설계의 가장 어려운 문제들이 모두 여기에 담겨 있었다.

하지만 이제는 더 이상 이 문제를 외면할 수 없었다. WebGPU가 성숙해지고, 그 위에서 만들어지는 프로젝트들의 규모가 커지면서, 코드 재사용성의 부재는 개발자들의 생산성을 심각하게 저해하는 가장 큰 장애물이 되고 있었다.

Dawn 팀 미팅.
“때가 된 것 같군.”
드미트리가 말했다. “WGSL에 날개를 달아줄 시간이야.”

팀은 WGSL 모듈 시스템의 설계를 위한 태스크포스(TF)를 구성했다.
그들은 다른 언어들의 사례를 연구했다. C++의 #include, 자바스크립트의 import/export, Rust의 moduse. 각각의 장단점을 분석하며, WGSL에 가장 적합한 모델을 찾아 나섰다.

보안 문제 해결이 최우선 과제였다. 외부 URL을 직접 포함하는 것은 처음부터 논외였다. 모든 모듈은 동일한 출처(Same-Origin) 내에서만 로드될 수 있어야 했다.

가장 큰 논쟁은 문법에 대한 것이었다.
#include "common/lighting.wgsl" 처럼 C++ 스타일의 전처리기(Preprocessor)를 도입하자는 의견이 있었다. 개발자들에게 친숙하다는 장점이 있었다.
하지만 드미트리는 반대했다.
“전처리기는 강력하지만, 코드를 분석하기 어렵게 만듭니다. 우리는 컴파일러가 코드의 모든 구조를 명확하게 파악할 수 있는, 더 현대적인 방식을 원합니다.”

긴 논의 끝에, 팀은 자바스크립트의 ES6 모듈 시스템과 유사한, 명시적인 import 문법을 도입하는 방향으로 가닥을 잡았다. 하지만 자바스크립트와는 달리, ‘컴파일 타임’에 모든 의존성이 해결되는 정적인 모델을 채택하기로 했다.

새로운 WGSL 코드의 모습은 이러했다.

// lighting.wgsl
export fn calculate_phong(
  light_dir: vec3f,
  view_dir: vec3f,
  normal: vec3f
) -> vec4f {
  // ... 복잡한 조명 계산 로직
}
// main.wgsl
import { calculate_phong } from "lighting.wgsl";

@fragment
fn main() -> @location(0) vec4f {
  // ...
  let final_color = calculate_phong(light, view, N);
  return final_color;
}

이 설계안은 W3C 커뮤니티에 제안되었다. 초기에는 컴파일러 구현의 복잡성을 우려하는 목소리도 있었지만, 대다수의 개발자들은 열렬히 환영했다.

이제 남은 것은 구현이었다.
벤과 WGSL 컴파일러 팀에게는 또다시 거대한 도전이 주어졌다.
그들은 이제 단순히 하나의 파일을 파싱하는 것을 넘어, 여러 파일 간의 의존성 그래프를 구축하고, 함수와 구조체의 이름을 관리하며, 모든 것을 하나의 거대한 셰이더 프로그램으로 합치는 정교한 ‘링커(Linker)’를 만들어야 했다.

작업은 더뎠지만, 꾸준히 진척되었다.
몇 달 후, 드미트리는 자신의 컴퓨터에서 새로운 WGSL 코드를 컴파일하는 데 성공했다. 그는 여러 파일에 흩어져 있던 함수들이 하나의 완벽한 프로그램으로 합쳐지는 과정을 디버거를 통해 지켜보았다.

그것은 마치 흩어져 있던 음표들이 모여 하나의 장엄한 악보가 되는 과정과도 같았다.
WGSL은 마침내 단순한 ‘스크립트’에서, 복잡한 소프트웨어를 구축할 수 있는 ‘시스템 언어’로 진화하는 첫걸음을 내디딘 것이다.

드미트리는 이슈를 제기했던 렌더링 엔지니어에게 짧은 답글을 남겼다.
“곧, 당신의 악몽이 끝날 겁니다.”