분석 대상: zed/crates/gpui/ · metal_renderer.rs · wgpu_renderer.rs · scene.rs · atlas.rs
Zed의 그래픽 파이프라인은 크게 세 계층으로 나뉜다. 가장 위에 플랫폼을 전혀 모르는 Element 트리, 중간에 공통 draw 명령을 담는 Scene, 가장 아래에 실제 GPU 명령으로 변환하는 PlatformRenderer.
UI 코드(버튼, 텍스트박스 등)가 Metal API를 직접 호출하면, Linux나 Web 버전을 만들 때 UI 코드를 전부 다시 작성해야 한다. Scene은 "무엇을 그릴지"만 담고, "어떻게 그릴지"는 플랫폼 렌더러에 맡긴다. UI 코드는 플랫폼을 전혀 몰라도 된다.
Scene에는 여덟 종류의 primitive가 있다. 모두 #[repr(C)]로 선언되어 있어
Rust 구조체를 그대로 GPU 버퍼에 복사(memcpy)할 수 있다.
그려지는 순서는 DrawOrder(u32) 값으로 결정하고,
같은 타입끼리 묶어서(batch) draw call 횟수를 최소화한다.
| Primitive | 용도 | 렌더링 방식 |
|---|---|---|
Quad |
사각형 배경, 버튼, 패널, 테두리 | SDF(Signed Distance Field) — 셰이더가 각 픽셀에서 거리 함수를 계산해 둥근 모서리와 AA를 GPU에서 처리. 텍스처 없이 계산만으로 렌더링. |
Shadow |
드롭 섀도, 인셋 섀도 | Gaussian blur를 SDF로 근사. 역시 텍스처 불필요. 셰이더 계산만으로 부드러운 그림자 표현. |
Path |
베지어 커브, SVG 경로, 삼각형 | MSAA 4× — 별도 임시 텍스처에 4배 해상도로 래스터라이즈 후 resolve. Quad와 달리 임의 모양이라 SDF로 표현 불가. |
Underline |
텍스트 밑줄, 빨간 물결(오류 표시) | wavy 플래그로 직선/물결 구분. 셰이더에서 물결 곡선을 수학적으로 계산. |
MonochromeSprite |
일반 텍스트 글리프 | 글리프 Atlas의 회색조(1채널) 타일 참조. 텍스트 색상은 셰이더에서 덧씌움. |
PolychromeSprite |
이모지, 컬러 아이콘, 이미지 | Atlas의 RGBA(4채널) 타일 참조. 이모지는 OS 텍스트 엔진이 래스터라이즈 후 Atlas에 업로드. |
SubpixelSprite |
서브픽셀 AA 텍스트 | Linux/Windows 전용. macOS에서는 이 분기가 unreachable!(). |
PaintSurface |
비디오/카메라 프레임 | macOS 전용. CVPixelBuffer → CVMetalTextureCache 경로로 GPU 복사 없이 직접 표시. |
화면을 한 번 그리는 데 아래 단계를 거친다. 초록색 단계는 CPU(Rust), 보라색 단계는 GPU(Metal/Vulkan/DX)에서 실행된다.
각 UI 요소(Div, Text, Button 등)가 cx.paint_quad(), cx.paint_glyph() 등을 호출한다. BoundsTree로 레이어 Z-순서를 결정하고, 각 draw 명령을 Scene 버퍼에 적재한다.
모든 primitive를 DrawOrder(u32) 기준으로 정렬한다. 같은 타입 + 같은 텍스처 ID끼리 묶어 배치 이터레이터를 만든다. draw call 횟수를 최소화하는 핵심 단계.
완성된 Scene을 플랫폼 렌더러에 넘긴다. 이 경계에서 CPU → GPU 영역으로 전환된다.
Metal command_queue.new_command_buffer()로 이번 프레임의 GPU 명령 기록 시작. InstanceBufferPool에서 이전 프레임 버퍼를 재사용(재할당 없음).
Shadows → Quads → Path MSAA 중간 패스 → Path resolve → Underlines → MonochromeSprites → PolychromeSprites 순으로 draw call을 발행한다. 각 배치는 GPU에서 병렬로 인스턴스 렌더링된다.
command_buffer.present_drawable() + commit()으로 화면에 표시. CAMetalLayer가 vertical sync에 맞춰 swap한다.
InstanceBufferPool로 버퍼를 풀링해 같은 크기면 재사용한다.
Apple Silicon에서는 CPU/GPU가 메모리를 공유(StorageModeShared)해 복사 자체가 없다.
외장 GPU에서는 StorageModeManaged로 명시적 동기화.
화면에 글자를 그릴 때마다 OS 텍스트 엔진에 "이 글자를 픽셀로 그려줘"를 요청하면 너무 느리다. 한 번 래스터라이즈한 글리프를 큰 GPU 텍스처(Atlas)에 저장해두고, 이후에는 "Atlas의 이 좌표에 있는 타일을 거기에 붙여라"만 전달한다. 이것이 글리프 아틀라스 패턴이다.
각 플랫폼이 다른 API를 쓰는 이유는 단순하다 — Metal은 Apple이 macOS/iOS 전용으로 만든 API라 Linux에서 쓸 수 없고, DirectX는 Microsoft가 Windows 전용으로 만든 API라 macOS에서 쓸 수 없다. Linux/Web은 크로스 플랫폼 추상화 레이어인 wgpu를 통해 Vulkan 또는 WebGPU를 사용한다.
| 항목 | macOS | Linux | Windows | Web/WASM |
|---|---|---|---|---|
| GPU API | Metal (Apple 네이티브) | wgpu → Vulkan OpenGL fallback |
DirectX 11 (MS 네이티브) | wgpu → WebGPU WebGL fallback |
| 셰이더 언어 | .metal (MSL) | .wgsl (WGSL) | .hlsl (HLSL) | .wgsl (WGSL) |
| 윈도우 시스템 | AppKit (NSWindow) | Wayland / X11 | Win32 (HWND) | wasm-bindgen Canvas |
| 텍스트 엔진 | CoreText | cosmic-text | DirectWrite | cosmic-text |
| 서브픽셀 AA | 비활성화 Retina에서 불필요 |
지원 | 지원 (ClearType) | 비활성화 |
| 글리프 Atlas | MetalAtlas | WgpuAtlas | DirectXAtlas | WgpuAtlas |
| 비디오 서피스 | CVPixelBuffer → CVMetalTextureCache (zero-copy) |
미지원 | 미지원 | 미지원 |
| Path AA | MSAA 4× (Metal) | wgpu MSAA | MSAA 4× (DX11) | wgpu MSAA |
Apple이 OpenGL을 deprecated하고 밀어붙인 차세대 API. CPU 오버헤드가 적고 Apple Silicon에서 CPU/GPU 통합 메모리를 활용해 복사 없이 데이터를 공유할 수 있다. CoreText가 OS에서 제공하는 고품질 텍스트 렌더링을 담당한다.
Vulkan은 강력하지만 코드가 매우 장황하다. wgpu가 Vulkan 위에 WebGPU 표준 API를 제공해 코드를 단순화한다. cosmic-text는 순수 Rust로 작성된 텍스트 레이아웃 엔진으로 Wayland/X11 모두에서 동작한다.
DirectX 12보다 구형이지만 더 넓은 호환성을 위해 DX11을 선택했다. DirectWrite는 ClearType 힌팅을 포함한 Windows 네이티브 텍스트 렌더링 API다.
브라우저에서 동작하는 버전. wgpu가 WebGPU 백엔드를 지원하므로 Linux와 거의 같은 렌더러 코드를 재사용한다. WebGPU가 없는 브라우저에서는 WebGL로 fallback한다.
SubpixelSprite 분기가 unreachable!()인 이유가 바로 이것이다.