Cost

Measured, not claimed. Fort → Verify Contract re-checks all of it.

Where it lands

MasterEverything offEverything onTapsSamplers
M_FortCharacter12427763
M_FortWeapon12420522
M_FortBase12420033

Pixel shader instructions, from the Material Editor's own estimate. It over-reports, so treat these as baselines to hold steady rather than ground truth.

What each switch gives back

On the character, from everything on:

Turning offSavesAlso
bFriendFoeRim56a gradient tap
bUseSpecular38the panorama tap and its sampler
bUseDiffuse19two gradient taps
bUnderglow14a gradient tap
bSkin7a gradient tap

These are real, not nominal. Feature gating is static bools on material function inputs, and a discarded function input is never compiled - so a switched-off feature takes its texture taps with it, not just its arithmetic.

The floor

124 instructions with every switch off. That is the shared surface block: the albedo and normal taps, the shading vectors, the pack unpack, and the clamped indirect and distance responses.

It does not shrink, because a Custom node emits its whole body whether or not an output is read. That is the trade for having the maths in a .ush instead of the graph, and it is the obvious place to look first if the budget gets tight.

Samplers

Three at most, on any master.

BaseColor, Normal, CRMshare one - they are all on the world wrap group
The gradient atlasone, however many rows are read
The panoramaone, because a Custom node brings its own

Nowhere near the 16 the platform allows, and the verify pass asserts it stays that way.

Vertex

153 on the environment master, 313 on the character and weapon. The difference is the skeletal mesh vertex factory, not anything in the shading - nothing here writes world position offset.

Verifying

Fort → Verify Contract. It builds an instance per feature and asserts what this page claims, one at a time, so a failure names the claim that stopped being true.

A permutation nobody has compiled before reports zero instructions and fails rather than being skipped, because that is indistinguishable from one that failed to compile - which is exactly where a default-off branch hides a broken sampler type. On a cold derived data cache, run it a second time before believing it.