데이터의 흐름

882025년 09월 15일5

비동기 컴퓨트와 다중 큐에 대한 논의가 시작되자, 개발자들은 GPU의 힘을 이전보다 훨씬 더 정교하게 활용할 수 있게 될 것이라는 기대에 부풀었다. 하지만 이 새로운 아키텍처는, 곧바로 또 다른 근본적인 질문을 수면 위로 끌어올렸다.

“그래서, 컴퓨트 큐에서 계산한 데이터는 어떻게 렌더 큐로 가져오죠?”

질문을 던진 것은 한 3D 의료 시각화 스타트업의 엔지니어였다. 그들은 환자의 거대한 볼륨 데이터(Volume Data)를 컴퓨트 셰이더로 실시간으로 처리하여, 렌더링 가능한 단면 이미지로 만드는 작업을 하고 있었다.

그의 질문은 당연했다.
컴퓨트 작업과 렌더링 작업이 별도의 큐에서 비동기적으로 실행된다면, 한쪽 큐에서 생성된 결과물(버퍼나 텍스처)을 다른 쪽 큐가 ‘안전하게’ 읽을 수 있는 시점을 어떻게 알 수 있는가?

드미트리는 이 문제가 GPUFence와 같은 기존의 동기화 프리미티브만으로는 부족하다는 것을 알았다. 그것은 작업의 완료 여부만 알려줄 뿐, 자원 자체의 ‘소유권’과 ‘상태’를 관리해주지는 못했다.

W3C 회의.
“우리는 자원의 ‘소유권 이전(Ownership Transfer)’이라는 개념을 도입해야 합니다.”
드미트리가 제안했다.

“예를 들어, 컴퓨트 큐에서 버퍼 A에 대한 계산을 마쳤다면, 개발자는 queue.submit()을 호출할 때, 이 버퍼 A의 소유권을 컴퓨트 큐에서 렌더 큐로 이전한다고 명시적으로 선언하는 겁니다. 그 순간부터, 컴퓨트 큐는 더 이상 이 버퍼에 접근할 수 없게 됩니다. 그리고 렌더 큐는 이 버퍼를 안전하게 읽어서 렌더링에 사용할 수 있게 되죠.”

이 아이디어는 명쾌했지만, 곧바로 애플의 딘 잭슨이 날카로운 반론을 제기했다.
“소유권 이전 모델은 너무 경직되어 있습니다. 만약 여러 개의 렌더링 작업이 동일한 계산 결과를 동시에 읽어야 한다면 어떻게 하죠? 소유권은 단 하나뿐인데 말입니다. 저희 Metal의 아키텍처는 ‘자원 상태 추적(Resource State Tracking)’이라는, 더 유연한 모델을 사용합니다.”

그가 설명하는 모델은 더 복잡했다.
개발자는 자원의 소유권을 이전하는 대신, 자원의 ‘상태’를 변경하는 명령을 내린다.
예를 들어, 버퍼 A를 ‘컴퓨트 쓰기 가능(Compute-Writable)’ 상태에서, ‘그래픽 읽기 전용(Graphics-Readable)’ 상태로 전환하라는 명령을 내리는 것이다. GPU 드라이버는 이 상태 전환 명령을 보고, 내부적으로 필요한 캐시 플러시나 메모리 배리어 작업을 알아서 처리해준다.

두 개의 다른 철학이 정면으로 충돌했다.
명시적인 소유권 이전이냐, 암시적인 상태 관리냐.

전자는 개발자가 이해하기는 쉽지만, 유연성이 떨어졌다.
후자는 유연하고 강력하지만, 개발자가 자원의 모든 상태를 직접 추적해야 하는 부담을 안겨주었다.

회의는 몇 주간 평행선을 달렸다. 양쪽 모두 자신들의 네이티브 API(Vulkan과 Metal)의 철학을 기반으로, 물러설 수 없는 주장을 펼쳤다.

드미트리는 이 교착 상태를 지켜보며, WebGPU의 근본적인 사명을 다시 한번 생각했다.
WebGPU는 Vulkan도, Metal도 아니었다. 그것은 이 모든 것을 아우르는, 더 높은 수준의 추상화여야 했다. 그리고 웹 API는 무엇보다 개발자에게 ‘안전하고’ ‘예측 가능해야’ 했다.

그는 마침내, 두 세계를 절충하는 제3의 안을 내놓았다.
“우리는 둘 다 가질 수 있습니다. 하지만 개발자에게 선택권을 주되, 그 선택의 결과를 명확하게 만들어야 합니다.”

그의 제안은 이러했다.
WebGPU는 기본적으로 애플이 주장하는 ‘자원 상태 추적’ 모델을 도입하지 않는다. 그것은 웹 개발자들에게 너무 큰 부담을 준다.
대신, Vulkan과 유사한 ‘자원 배리어(Resource Barrier)’ 개념을 더 명시적이고 단순화된 형태로 도입한다.

개발자는 작업 제출(submit) 시, 각 자원에 대한 소유권을 명시적으로 선언한다.

queue.submit(commandBuffers, {
  read: [bufferB, textureC],
  write: [bufferA],
});

이 코드는 브라우저에게 명확한 힌트를 준다.
“이번 제출에서, 나는 bufferA에 쓸 것이고, bufferB와 textureC를 읽을 것이다.”
그러면 Dawn과 같은 구현체는 이 정보를 바탕으로, 내부적으로 필요한 최소한의 동기화 작업(캐시 플러시, 레이아웃 전환 등)을 알아서 처리해준다. 개발자는 더 이상 복잡한 상태 관리에 대해 고민할 필요가 없다.

이 제안은 복잡한 상태 관리를 자동화하면서도, 개발자가 자신의 의도를 명확히 표현할 수 있게 하는, 절묘한 균형점 위에 서 있었다.
오랜 논쟁 끝에, 커뮤니티는 마침내 드미트리의 안에 손을 들어주었다.

이 결정은 WebGPU의 데이터 흐름을 관리하는 방식에 대한 중요한 이정표가 되었다.
WebGPU는 개발자에게 모든 것을 맡기는 완전한 저수준 API의 길을 가는 대신, 적절한 추상화를 통해 개발자의 실수를 방지하고, 복잡성을 숨겨주는 ‘안전한 고성능 API’로서의 정체성을 다시 한번 확립하게 된 것이다.

드미트리는 이제, 단순히 GPU의 힘을 해방시키는 것을 넘어, 그 힘이 만들어내는 거대한 데이터의 강물이 범람하지 않고, 개발자가 의도한 물길을 따라 안전하게 흐를 수 있도록, 보이지 않는 둑을 쌓고 수로를 설계하는, 치수 공학자의 역할을 수행하고 있었다.