11 / 12 · Integrate, measure, ship集成、测量、交付
Measure first, then design a compute rasterizer先测量,再设计 compute 光栅器
Separate bottlenecks and connect today’s quad renderer to a future tile-based backend.区分性能瓶颈,并把当前 quad 渲染器连接到未来的 tile 后端。
Two splats teach correctness. A hundred thousand reveal costs. The first optimization is to find out which cost you actually have: transfer, CPU sorting, vertex work, pixel overdraw or synchronization.两个 splat 教你正确性,十万个 splat 暴露开销。优化的第一步是确认瓶颈:传输、CPU 排序、顶点计算、像素重复覆盖,还是同步。
1. Separate the clocks1. 分开计时范围
Measure CPU preparation, CPU upload/submission, GPU pass duration and presentation separately. The time around queue.submit is not the time the GPU spent rendering. GPU timestamp queries are optional features; report their absence instead of filling the field with a CPU estimate. Our lab’s frame counter deliberately does not claim GPU milliseconds.分别测 CPU 准备、CPU 上传与提交、GPU pass 耗时及显示。queue.submit 前后的时间不等于 GPU 渲染时间。GPU timestamp query 是可选能力,缺失时应明确标注,不要用 CPU 估计填进 GPU 指标。本实验的帧计数特意不冒充 GPU 毫秒数。
2. Work scales with coverage, not just point count2. 工作量也取决于覆盖面积
Two scenes with equal Gaussian count can have different costs. Huge overlapping quads generate many fragment invocations. A tiny alpha can still require enough work to decide to discard it. Reduce resolution as an experiment: a large speedup suggests pixel cost, while unchanged time suggests another bottleneck. Keep camera and model time fixed during comparisons.相同 Gaussian 数量的场景,耗时可能不同。巨大重叠 quad 会产生很多片元执行;很小的 alpha 也可能要先计算才能决定丢弃。可以降低分辨率做实验:明显加速提示像素成本,时间不变则提示其他瓶颈。比较时固定相机和模型时间。
The current real pass sorts visible centers on the CPU and repeats geometric evaluation in the shader. Its center-margin culling and maximum footprint clamp are baseline heuristics, not conservative production solutions. Document these choices before interpreting image quality or speed differences.当前真实 pass 在 CPU 对可见中心排序,并在 shader 重复几何求值。中心边距剔除与最大 footprint 限制都是基线启发式,不是生产级保守方案。解释画质或速度差异前,先记录这些选择。
3. Build the compute path as explicit stages3. 把 compute 路径拆成明确阶段
A typical tile path projects splats, computes bounds, counts tile overlaps, prefix-sums counts into offsets, writes (tile, depth, splat) entries, sorts entries, finds per-tile ranges and composites pixels. Each stage has buffer capacities and overflow behavior. “Move it to compute” does not remove sorting, memory traffic or synchronization.典型 tile 路径依次投影 splat、求边界、统计 tile 重叠数、前缀和生成偏移、写出 (tile, depth, splat) 条目、排序、找出每个 tile 的范围,最后合成像素。每步都需要 buffer 容量与溢出处理。“搬到 compute”并不会消除排序、内存流量或同步。
Our existing mini_compute example is a deliberately smaller reference: a few CPU-ordered splats, one compute invocation per pixel, front-to-back transmittance, GPU readback and comparison with a CPU implementation. It does not implement tile binning or GPU sorting. Run that first before designing the larger pipeline.已有 mini_compute 是特意缩小的参考:少量 CPU 排序 splat、每像素一个 compute 执行、从前往后累计透射率、GPU 回读并与 CPU 实现比较。它没有实现 tile 分桶或 GPU 排序。先跑通它,再设计更大的管线。
4. Port algorithms, not platform accidents4. 移植算法,不移植平台偶然行为
CUDA, Metal compute and WGSL compute differ in execution and memory capabilities. Keep a CPU reference, fixed input fixture, documented sort tie-breaks and pixel tolerances. A backend must preserve the model and composition contract, even if its workgroup sizes, scan or sort implementation differ.CUDA、Metal compute 与 WGSL compute 在执行和内存能力上不同。保留 CPU 参考、固定输入、明确的排序同值规则与像素容差。即使工作组大小、扫描或排序实现不同,后端也必须保持模型与合成约定。
RUN → OBSERVE运行 → 观察
Make this step concrete把这一步运行起来
CPU/GPU pixel checks pass for the small compute rasterizer; reversing order changes the output. Readback latency is not a throughput benchmark.小型 compute 光栅器通过 CPU/GPU 像素检查,反转顺序改变输出。回读延迟不是吞吐量基准。
Open GPU milestone 7 →打开 GPU 里程碑 7 →cargo run --locked --example mini_compute
# Inspect artifacts/mini-compute/ and compare the correct/reversed-order PNGs.
cargo run --locked --example native_smokeCommands use the reference repository. Rust/WGSL changes need a native rebuild or npm run build:wasm, followed by a browser reload.命令以参考仓库为基础。修改 Rust/WGSL 后需重新编译桌面程序,或运行 npm run build:wasm 后刷新浏览器。
IDEA → IMPLEMENTATION想法 → 实现
Follow the running code沿着真实代码看
Excerpts are extracted from the repository at site build time. Expand only the part you are studying; full files include context and line numbers.片段在网站构建时直接从仓库提取。只展开正在研究的部分,完整文件包含上下文与行号。
src/workshop.rs · L254–306
Inspect the current CPU-to-GPU boundary before moving work across it.移动工作到 GPU 前,先看清现有 CPU/GPU 边界。
pub fn prepare(
&mut self,
queue: &wgpu::Queue,
frame: Frame,
size: [u32; 2],
) -> Result<u32, String> {
frame.validate()?;
if size.contains(&0) {
return Err("Cannot prepare a zero-sized target".into());
}
let (view, projection) = camera(frame, size[0], size[1]);
self.stage = frame.stage;
if frame.stage == 7 {
let pass = self.real.as_mut().ok_or("Load the STG-Lite model first")?;
self.count = pass
.prepare(
queue,
frame.time,
view.to_cols_array_2d(),
projection.to_cols_array_2d(),
size,
)?
.visible_count as u32;
} else {
let data = instances(frame, view);
self.count = data.len() as u32;
queue.write_buffer(&self.instances, 0, bytemuck::cast_slice(&data));
queue.write_buffer(
&self.uniforms,
0,
bytemuck::bytes_of(&Uniforms {
view_projection: (projection * view).to_cols_array_2d(),
viewport_stage: [size[0] as f32, size[1] as f32, frame.stage as f32, 0.0],
}),
);
}
Ok(if frame.stage == 0 { 0 } else { self.count })
}
pub fn draw(&self, pass: &mut wgpu::RenderPass<'_>) {
if self.stage == 0 {
return;
}
if self.stage == 7 {
if let Some(real) = &self.real {
real.draw(pass);
}
return;
}
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &self.bindings, &[]);
pass.draw(0..if self.stage == 1 { 3 } else { 6 }, 0..self.count);
}Open full source with line numbers ↗打开带行号的完整源码 ↗Real-model render pass ↗真实模型渲染 pass ↗ · Real-model shader ↗真实模型 shader ↗ · Compute reference shader ↗Compute 参考 shader ↗
Predict it, change it, explain it先预测,再修改,再解释
Why can a renderer get slower when its splat count stays constant but the camera moves closer?Splat 数量不变,相机靠近后为什么可能变慢?
Check your reasoning核对推理
Projected footprints become larger and overlap more pixels, increasing fragment work and blending. Counting splats alone misses that cost. A tile implementation also creates more tile-overlap entries.投影 footprint 变大、覆盖和重叠更多像素,增加片元及混合工作。只数 splat 看不到这种成本;tile 实现也会产生更多重叠条目。
Keep the theory beside the code把原理放在代码旁边
Open the related derivation or lab ↗打开对应推导或实验 ↗
3D Gaussian Splatting · original research ↗