Pure Go에서 GPU Compute Shaders: gogpu/gg v0.15.0

발행: (2025년 12월 26일 오전 11:13 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

GPU Compute Shaders in Pure Go: gogpu/gg v0.15.0

Two days ago we shipped gogpu/gg v0.14.0 with alpha masks and a fluent PathBuilder. Yes—two days, we’re moving fast.

두 날 전에 우리는 알파 마스크와 유연한 PathBuilder를 갖춘 gogpu/gg v0.14.0을 배포했습니다. 네—이틀 만에 우리는 빠르게 움직이고 있습니다.

Looking at performance profiles, we saw a problem:

The CPU was the bottleneck.

성능 프로파일을 살펴보니 문제가 있었습니다:

CPU가 병목 현상이었습니다.

Our GPU could render millions of pixels in milliseconds, but the CPU spent a lot of time tessellating paths. This is the classic 2‑D graphics problem: CPU tessellation doesn’t scale.

우리 GPU는 밀리초 안에 수백만 픽셀을 렌더링할 수 있었지만, CPU는 경로를 테셀레이션하는 데 많은 시간을 소비했습니다. 이것은 고전적인 2‑D 그래픽 문제이며, CPU 테셀레이션은 확장되지 않습니다.

So we moved the entire rasterisation pipeline to GPU compute shaders.

그래서 전체 래스터화 파이프라인을 GPU 컴퓨트 셰이더로 옮겼습니다.

Today, gogpu/gg v0.15.0 is here: 2 280 lines of WGSL compute shaders, vello‑style pipeline, dramatic speed‑ups for complex scenes. All in Pure Go.

오늘, gogpu/gg v0.15.0이 출시되었습니다: 2 280줄의 WGSL 컴퓨트 셰이더, vello‑스타일 파이프라인, 복잡한 장면에 대한 극적인 속도 향상. 모두 Pure Go로 구현되었습니다.

성능 도전

// Drawing 10 000 circles
ctx := gg.NewContext(800, 600)
for i := 0; i 
) {
    let curve = curves[id.x];

    // Adaptive subdivision based on curvature
    let segments = subdivide_bezier(curve);

    // Write to global buffer (thousands in parallel!)
    for (var i = 0u; i 
) {
    let segment = segments[id.x];
    let bounds   = segment_bounds(segment);

    // Find overlapping tiles
    for (var y = tile_min.y; y 
) {
    let tile_id = (pixel.y / TILE_SIZE) * tile_width + (pixel.x / TILE_SIZE);

    var coverage = 0.0;
    for (var i = 0u; i (color.rgb, saturate(coverage));
}

어떤 규모에서도 완벽한 안티앨리어싱—계단 현상 없이, MSAA 오버헤드 없이.

기대되는 성능 향상

| Workload | Expected Behaviour |
|----------|---------------------|
| Simple paths (= h.segmentThreshold {        |
|        // GPU path: dispatch compute shaders |
|        h.gpu.Rasterize(coarse, segments, backdrop, scene.FillNonZero) |
|    } else { |
|        // CPU path: software rasterization |
|        h.cpu.RasterizeSegments(segments, backdrop) |
|    } |
if h.segmentCount < h.segmentThreshold {
    // GPU path: dispatch compute shaders
    h.gpu.Rasterize(coarse, segments, backdrop, scene.FillNonZero)
} else {
    // CPU path: software rasterization
    h.cpu.RasterizeSegments(segments, backdrop)
}

왜? 작은 경로(...atomic이 지원됩니다).

  • 특정 메모리 순서가 필요합니다.
  • 버퍼 레이아웃이 중요합니다.
// This works
@group(0) @binding(1) var counts: array>;
atomicAdd(&counts[i], 1u);

// This doesn't
var counts: array; // Not atomic!

디버깅이 까다로웠습니다 – WGSL 검증 오류가… 난해합니다.

What We Shipped

Statistics

  • 2 280 LOC WGSL shaders (8 shader files) → 2 280 LOC WGSL 셰이더 (8개 셰이더 파일)
  • ~20 K LOC Go in backend/wgpu/~20 K LOC Go backend/wgpu/
  • 74 % test coverage overall → 전체 74 % 테스트 커버리지
  • 0 linter issues린터 이슈 0개

Shader Files

backend/wgpu/shaders/
├── flatten.wgsl     # 589 LOC — Bezier curve flattening
├── coarse.wgsl      # 335 LOC — Tile binning with atomics
├── fine.wgsl        # 290 LOC — Per‑pixel coverage
├── blend.wgsl       # 424 LOC — 29 blend modes on GPU
├── composite.wgsl   # 235 LOC — Layer compositing
├── strip.wgsl       # 155 LOC — Sparse strip rendering
├── blit.wgsl        #  43 LOC — Final output blit
└── msdf_text.wgsl   # 209 LOC — MSDF text rendering

Go Implementation

backend/wgpu/
├── gpu_flatten.go       # 809 LOC — Flatten pipeline
├── gpu_coarse.go        # 698 LOC — Coarse rasterization
├── gpu_fine.go          # 752 LOC — Fine rasterization
├── sparse_strips_gpu.go # 837 LOC — Hybrid CPU/GPU selection
├── renderer.go          # 822 LOC — Main renderer
├── pipeline.go          # 369 LOC — Pipeline orchestration
├── memory.go            # 413 LOC — GPU memory management
└── ... (40+ files total)

직접 해보세요

설치

go get github.com/gogpu/gg@v0.15.0

빠른 예제

package main

import "github.com/gogpu/gg"

func main() {
    ctx := gg.NewContext(512, 512)
    ctx.ClearWithColor(gg.White)

    // 1 000 circles — GPU backend handles complex scenes efficiently
    ctx.SetColor(gg.Hex("#e74c3c"))
    for i := 0; i   
}

CPU 병목 현상에서 GPU 병렬 처리로. 순차적 테셀레이션에서 대규모 병렬 컴퓨트 셰이더로.

이것이 Pure Go가 할 수 있는 일입니다.

go get github.com/gogpu/gg@v0.15.0

⭐ 유용하다고 생각되면 레포에 ⭐ 별을 눌러 주세요!

GoGPU 여정 시리즈의 일부

Pure Go의 GPU 컴퓨트 셰이더 ← 여기 있습니다

Back to Blog

관련 글

더 보기 »