跳到主要内容
shader-performance · 代码
查看代码目录
Graphics Learning Lab Shader Performance / Shader source

Observe · Change · Recompute · Explain

每个优化词都对应一段可观察的工作

Read-only · exact runtime source

代码直接来自交互视口,按 instancing、LOD、overdraw、branches、timeline、debug 的顺序阅读。

这一课怎么学

  1. 观察
  2. 预测
  3. 改一个量
  4. 看证据
  5. 再读代码
01 · vertex

Instancing

复用一份 geometry,减少 draw submission。

attribute float aInstanceId;
uniform float uLodMode;
uniform float uLodBias;
varying float vInstanceId;
varying float vLod;
void main() {
  // SECTION:instancing - instance data changes transforms without multiplying draw submissions
  vInstanceId = aInstanceId;
  vec4 localPosition = vec4(position, 1.0);
  #ifdef USE_INSTANCING
    localPosition = instanceMatrix * localPosition;
  #endif
  float distanceToCamera = length((modelViewMatrix * localPosition).xyz);
02 · vertex

LOD

按距离减少远处顶点工作。

// SECTION:lod - distance uses world depth; screen-size asks how large this
  // object projects through the current camera. They intentionally differ.
  float objectRadius = 0.2;
  #ifdef USE_INSTANCING
    objectRadius *= length(instanceMatrix[0].xyz);
  #endif
  float projectedRadius = projectionMatrix[1][1] * objectRadius / max(distanceToCamera, 0.001);
  float screenSizeMetric = 0.25 / max(projectedRadius, 0.001);
  float lodMetric = uLodMode < 1.5 ? distanceToCamera : screenSizeMetric;
  vLod = uLodMode < 0.5 ? 0.0 : clamp(floor(log2(max(lodMetric, 1.0)) + uLodBias), 0.0, 4.0);
  gl_Position = projectionMatrix * modelViewMatrix * localPosition;
}
03 · fragment

Overdraw

同一像素多层 fragment 的重复成本。

precision highp float;
uniform int uDebugMode;
uniform int uBranchMode;
uniform float uBranchRatio;
uniform float uShaderWork;
uniform float uTextureReads;
uniform float uOverdrawLayers;
uniform float uTargetMs;
uniform float uEstimatedMs;
uniform bool uEarlyZEffective;
varying float vInstanceId;
varying float vLod;

// SECTION:overdraw - transparent layers repeat fragment work for one screen pixel
float overdrawHeat() { return clamp(uOverdrawLayers / 16.0 * (uEarlyZEffective ? 0.55 : 1.0), 0.0, 1.0); }
04 · fragment

Branch divergence

wave 内分支一致性决定有效执行率。

// SECTION:branches - divergence makes the same wave serialize different paths
float branchMask() { float coherentMask = step(0.5, fract(vInstanceId * 0.13)); float divergentMask = step(uBranchRatio, fract(vInstanceId * 0.37)); return uBranchMode == 0 ? coherentMask : uBranchMode == 1 ? divergentMask : 1.0; }
05 · fragment

GPU timeline

把估算时间与 target frame budget 对齐。

// SECTION:timeline - show a budget ratio instead of pretending a single knob is performance
vec3 timelineColor() { float ratio = clamp(uEstimatedMs / max(uTargetMs, 0.01), 0.0, 1.8); return mix(vec3(0.17, 0.8, 0.48), vec3(0.94, 0.24, 0.16), clamp(ratio, 0.0, 1.0)); }
06 · fragment

Debug views

直接输出 overdraw、branch、instance、LOD 与 heatmap。

// SECTION:debug - expose work, instance identity, LOD and budget evidence
void main() {
  float work = 0.0;
  for (int index = 0; index < 120; index += 1) { if (float(index) >= uShaderWork) break; work += sin(float(index) * 0.17 + vInstanceId * 0.01); }
  float branch = branchMask();
  vec3 color = mix(vec3(0.12, 0.32, 0.5), vec3(0.92, 0.52, 0.2), fract(vInstanceId * 0.071));
  color *= 0.72 + 0.28 * abs(sin(work * 0.02));
  if (uDebugMode == 1) color = mix(vec3(0.12, 0.8, 0.48), vec3(0.94, 0.18, 0.12), overdrawHeat());
  if (uDebugMode == 2) color = vec3(branch, 1.0 - branch, 0.16);
  if (uDebugMode == 3) color = vec3(fract(vInstanceId * 0.071), fract(vInstanceId * 0.17), fract(vInstanceId * 0.31));
  if (uDebugMode == 4) color = vec3(vLod / 4.0, 0.65, 1.0 - vLod / 4.0);
  if (uDebugMode == 5) color = timelineColor();
  if (uDebugMode == 6) color = mix(vec3(0.08, 0.13, 0.2), vec3(0.94, 0.2, 0.12), clamp((uShaderWork + uTextureReads * 8.0) / 120.0, 0.0, 1.0));
  gl_FragColor = vec4(color, 0.82);
  #include <tonemapping_fragment>
  #include <colorspace_fragment>
}