Unreal Engine 5.8 · Editor tooling · Metal
Shader Cost
An editor plugin that audits every material in a project, then draws each one over the whole screen on this Mac and times it on the GPU. The interesting part is where the engine's own shader stats and the measured cost disagree.
Why
The stats window answers one material at a time, and on a Mac it leaves gaps
Unreal's Material Stats window shows instruction counts for the material you have open. A tech artist trying to find what's expensive in a project needs every material at once, ranked. On a Mac some of those numbers don't exist: the Metal compiler reports instruction counts but no sampler counts, so the engine shows a misleading 0.
Instruction counts also leave out a lot of real cost. They can't see texture bandwidth, overdraw, or pixels that get clipped and skip the rest of the pipeline. So the auditor has two halves:
- Static audit. For every material and instance: base-pass pixel and vertex instruction counts, texture lookups, the textures it references and their sizes, how many shaders it expands into, and the flags that make a material costly whatever its instruction count (translucency, masking, world position offset, pixel depth offset, two-sided). Instances that only change parameters are grouped with the parent whose shaders they reuse.
- Measured cost. Each unique material covers the whole screen and is timed on the GPU against a simple lit baseline.
The finding
Same shader, twice the cost
The canal pack's stone master material and one of its instances compile to nearly the same shader. The stats window ranks them as equals. On the GPU they're not:
| Material | Pixel instructions | Textures | Measured, full 1440p |
|---|---|---|---|
MM_MP_01 (master) | 576 | placeholder 16×16 textures | +0.82 ms |
MI_StoneWall_03 (its instance) | 582 | eight 2048² textures, 33.6 MP | +1.70 ms |
The master only references tiny placeholders that fit entirely in the GPU's texture cache. The instance swaps in eight real 2K textures, and streaming them through the cache doubles the cost. An instruction count can't show that.
Some materials measured cheaper than the lit baseline, and those numbers are real too. The unlit sky skips the lighting the baseline pays for (−0.44 ms). A masked banner clips most of its pixels, and clipped pixels skip the rest of the pipeline (−0.22 ms).
How it measures
Make the signal big and the noise cancel
The benchmark launches the project in standalone game mode on an empty map: a camera looking straight down at a plane that fills the view, and one directional light without shadows. Each material goes onto the plane and alternates with a simple lit baseline, round by round, reading the GPU's frame time. Its cost is the median difference, scaled to one full 1440p screen.
The first run was useless. At 720p the plane was so cheap that the GPU sat nearly idle and changed its clock between frames: the baseline alone swung by 1.5 ms, more than most materials cost. Rendering at 2560×1440 with 200% screen percentage gives each material four times the pixels against the same fixed overhead, and keeps the GPU busy enough to hold its clock. After that change, two separate runs agree within about 0.05 ms per material.
Surprises
What the engine's numbers don't tell you
Undrawn materials look free
The editor compiles shaders on demand, so a material nobody has drawn has almost no compiled shaders and no instruction counts. The audit compiles its own complete shader map for each unique material, the same way the Material Stats window does, and never writes it to the DDC.
Some stats don't exist on Metal
Metal reports instruction counts but not sampler counts, and the engine shows 0. The report says "n/a" instead of repeating a number that looks real.
An idle GPU is a noisy GPU
Apple's GPU changes clock speed when it has little to do. A benchmark that barely loads it measures the clock governor, not the shader.
Headless isn't the same as the editor
Run as a commandlet, the audit compiled fallback shaders for some materials and saw every texture as a 32×32 placeholder. The editor path is the supported one, and the headless mode is marked experimental.
Next
What it doesn't measure yet
- Vertex work, world position offset and shadow-pass cost. The full-screen plane measures pixel cost only.
- Texture compression formats. The value read in the editor isn't reliable yet, so the report leaves it out.
- Translucent overdraw, measured with stacked layers rather than estimated from a flag.