Elective E1

Performance fundamentals

Math is basically free. Memory isn't.

Here's the intuition almost nobody hands you until you hit a real slowdown: a multiply, an add, a sin() — on modern GPUs these are extremely cheap, executed in bulk across thousands of pixels at once. A texture lookup is a different story — it means reaching out to memory, which is slow compared to arithmetic sitting right there in the processor. This is why a shader that does a lot of math but only samples a texture once or twice is usually fine, while one that samples several textures per pixel starts costing real time.

Branching isn't free either

GPUs don't run one pixel at a time — they run pixels in small groups (often 32 or 64 at once), lockstep, executing the exact same instruction across the whole group simultaneously. If an if statement sends some pixels in that group down one path and others down another, the hardware can't actually skip work for either side — it runs both branches for the whole group and just discards the results that don't apply to each pixel. A branch that's the same for every pixel (like a slider-controlled toggle) costs nothing extra. A branch whose outcome varies pixel-to-pixel — like an edge test — can quietly double your cost.

A loop's bound is the real cost lever

You've already used this without naming it: Module 05's fbm() loop runs octaves times, and every single octave repeats the same handful of noise-lookups and multiplies. Double the octave count and you've roughly doubled that function's cost — not because any one step got slower, but because you're now doing the whole thing twice. When a shader has to run fast (60 times a second, on someone's phone, not your desktop), the octave count — or blur radius, or shadow-ray step count — is usually the first thing worth turning down before anything else.

Predict the output

Which of these is generally the most expensive per-pixel operation in a typical shader?

frame time