Files
2nd/10_Wiki/Topics/Frontend/Draw Call Optimization.md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

6.7 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-draw-call-optimization Draw Call Optimization 10_Wiki/Topics verified self
Batching
Instancing
GPU Draw Reduction
none A 0.9 applied
graphics
gpu
performance
webgl
webgpu
2026-05-10 pending
language framework
TypeScript WebGPU

Draw Call Optimization

매 한 줄

"매 CPU→GPU command submission 의 의 minimize 의 — 의 frame budget 의 dominant cost". 의 each draw call 의 의 driver overhead (state validation, command translation) 의 incur. 2026 의 WebGPU 의 매 explicit 의 design 의 의 batching 의 essential 의.

매 핵심

매 cost 의 source

  • 의 driver state validation (shader, buffer, texture binding).
  • 의 command buffer 의 translation 의 GPU-specific ISA.
  • 의 GPU 의 의 pipeline switch (cache miss, warp reorganize).

매 reduction 의 strategy

  • Batching: 의 same-state object 의 single draw 의 의 merge.
  • Instancing: 의 same mesh 의 N copy 의 single 의 draw call 의 issue.
  • Texture atlas: 의 multiple texture 의 의 single 의 의 — 의 binding 의 reduce.
  • Indirect draw: 의 GPU 의 의 self-issue 의 의 — CPU 의 의 idle.
  • Bindless / large bind group: 의 binding 의 의 amortize.

매 응용

  1. UI rendering (의 button 의 thousand 의 single draw).
  2. Particle system (의 instancing 의 의 millions).
  3. Tilemap (atlas + instancing).
  4. Foliage / crowd (의 GPU instancing).
  5. Game world chunk (의 batching 의 의 static mesh).

💻 패턴

Three.js BatchedMesh

import * as THREE from "three";

const batched = new THREE.BatchedMesh(1024, 60_000, 90_000, material);
const cubeGeom = new THREE.BoxGeometry();
const id = batched.addGeometry(cubeGeom);

for (let i = 0; i < 1024; i++) {
  const inst = batched.addInstance(id);
  const m = new THREE.Matrix4().setPosition(Math.random() * 100, 0, Math.random() * 100);
  batched.setMatrixAt(inst, m);
}
scene.add(batched);
// 매 single draw call 의 1024 cube

InstancedMesh

const geom = new THREE.SphereGeometry(0.5);
const mesh = new THREE.InstancedMesh(geom, material, 10_000);
const m = new THREE.Matrix4();
for (let i = 0; i < 10_000; i++) {
  m.setPosition(Math.random() * 200 - 100, 0, Math.random() * 200 - 100);
  mesh.setMatrixAt(i, m);
}
mesh.instanceMatrix.needsUpdate = true;

WebGPU instanced draw

const pass = encoder.beginRenderPass(passDesc);
pass.setPipeline(pipeline);
pass.setBindGroup(0, sceneBindGroup);
pass.setVertexBuffer(0, vertexBuffer);
pass.setVertexBuffer(1, instanceBuffer);  // per-instance data
pass.setIndexBuffer(indexBuffer, "uint32");
pass.drawIndexed(indexCount, instanceCount);
pass.end();

WebGPU indirect draw (GPU 의 self-issue)

const indirectBuffer = device.createBuffer({
  size: 16,  // [vertexCount, instanceCount, firstVertex, firstInstance]
  usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.STORAGE,
});

// 의 compute shader 의 의 indirectBuffer 의 의 fill (e.g. frustum cull)
pass.drawIndirect(indirectBuffer, 0);

Texture atlas (UV 의 sub-region)

// fragment shader
uniform sampler2D atlas;
uniform vec4 uvRect;  // x, y, w, h

void main() {
  vec2 uv = uvRect.xy + v_uv * uvRect.zw;
  outColor = texture(atlas, uv);
}

Sort 의 의 state-change minimize

// 의 draw 의 의 material 의 의 sort
drawables.sort((a, b) => {
  if (a.materialId !== b.materialId) return a.materialId - b.materialId;
  if (a.meshId     !== b.meshId)     return a.meshId - b.meshId;
  return a.depth - b.depth;
});

UI batching (single quad mesh)

// 의 each UI element 의 의 quad 의 의 single VBO 의 의 append
class UIBatcher {
  vertices = new Float32Array(4096 * 4 * 5);  // x, y, u, v, color
  count = 0;

  pushQuad(x: number, y: number, w: number, h: number, uv: UVRect, color: number) {
    const v = this.vertices;
    const o = this.count * 20;
    v[o+0]=x;     v[o+1]=y;     v[o+2]=uv.x;       v[o+3]=uv.y;       v[o+4]=color;
    v[o+5]=x+w;   v[o+6]=y;     v[o+7]=uv.x+uv.w;  v[o+8]=uv.y;       v[o+9]=color;
    v[o+10]=x+w;  v[o+11]=y+h;  v[o+12]=uv.x+uv.w; v[o+13]=uv.y+uv.h; v[o+14]=color;
    v[o+15]=x;    v[o+16]=y+h;  v[o+17]=uv.x;      v[o+18]=uv.y+uv.h; v[o+19]=color;
    this.count++;
  }

  flush(pass: GPURenderPassEncoder) {
    device.queue.writeBuffer(this.vbo, 0, this.vertices, 0, this.count * 20);
    pass.setVertexBuffer(0, this.vbo);
    pass.draw(6 * this.count);  // 매 single call
    this.count = 0;
  }
}

Frustum cull (CPU)

function cull(objects: Drawable[], camera: Camera): Drawable[] {
  const frustum = camera.frustum;
  return objects.filter((o) => frustum.intersects(o.worldBounds));
}

GPU-driven cull (compute)

@compute @workgroup_size(64)
fn cullCS(@builtin(global_invocation_id) gid: vec3u) {
  let i = gid.x;
  if (i >= arrayLength(&instances)) { return; }
  let inst = instances[i];
  if (frustumIntersects(inst.bounds, frustum)) {
    let slot = atomicAdd(&drawCount, 1u);
    visibleInstances[slot] = inst;
  }
}

매 결정 기준

상황 Approach
Same mesh, many copy InstancedMesh (instancing)
Different mesh, same material BatchedMesh (geometry merge)
UI / 2D Sprite batcher + atlas
Static scene Pre-merge geometry at build time
Dynamic LOD / cull GPU indirect draw + compute cull
Mobile / tile Reduce binding, atlas, instancing

기본값: instancing 의 first, batching 의 second, indirect/compute 의 last.

🔗 Graph

🤖 LLM 활용

언제: 의 frame time 의 의 CPU-bound 의 (draw call > 1000), GPU-driven culling, atlas 설계. 언제 X: 의 매 GPU-bound 의 (fragment-heavy) — 의 다른 의 axis 의 (overdraw, shader complexity) 의 attack.

안티패턴

  • One mesh per object: 의 10,000 entity 의 = 의 10,000 draw — 매 disaster.
  • Per-frame buffer recreate: 의 GC pressure + 의 driver overhead.
  • Random material switch: state thrash — 매 sort 의 의 by material first.
  • Premature GPU-driven: 의 CPU 의 매 not bottleneck 의 시 의 — 매 added complexity.

🧪 검증 / 중복

  • Verified (WebGPU spec, Three.js BatchedMesh r167+, Unreal/Unity rendering docs, GPU Gems, RenderDoc analysis).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — instancing + indirect + UI batcher 추가