# Skill — Unity Shader Review

**Platform:** Unity (HLSL / ShaderLab)  
**Category:** Game Development / Performance  
**Composable With:** agents/unity/shader-generator/agent.md

---

## Purpose

Reviews Unity shader code for mobile performance budget violations, API correctness for the target render pipeline, and common authoring mistakes. Use before shipping a shader to a mobile build.

## When to Use

- Before committing a new shader to a project targeting mobile (iOS/Android)
- When reviewing a shader generated by an LLM or written by a non-graphics developer
- When profiling shows GPU overdraw or high fragment shader cost

---

## Skill Prompt

```
When reviewing a Unity HLSL/ShaderLab shader, check for:

MOBILE PERFORMANCE BUDGET
- Texture samples: flag if >2 texture samples per fragment on a mobile-targeted shader.
  Each sample adds GPU latency; 3+ samples per fragment will cause frame budget overruns
  on Mali/Adreno GPUs at 1080p.
- Precision: all fragment shader calculations should use half (16-bit) unless the precision
  is mathematically required to be float (32-bit). Flag any float declaration in the fragment
  shader that could be half.
- Dynamic branching: flag any if/else or switch in the fragment shader that depends on a
  uniform or interpolated value. Dynamic branches cause warp divergence on mobile GPUs.
  Exception: compile-time branches (#if / #ifdef / shader_feature) are free.
- pow() and sin(): these are expensive on some GPUs. In the fragment shader, prefer
  approximations or precomputed lookup textures for non-interactive effects.
- Discard / clip(): transparent shaders that use discard prevent early-Z culling on mobile.
  Use alpha blending instead of alpha test where possible.

RENDER PIPELINE CORRECTNESS
- Built-in Pipeline: must use CGPROGRAM / ENDCG and #include "UnityCG.cginc".
  Flag any HLSLPROGRAM / URP/HDRP includes in a Built-in shader.
- URP: must use HLSLPROGRAM / ENDHLSL and include the correct URP ShaderLibrary headers.
  Flag any CGPROGRAM in a URP shader — it will not compile correctly.
- HDRP: must use HDRP ShaderLibrary headers and the HDRP attribute struct conventions.
- Cross-pipeline: if the shader is intended to be pipeline-agnostic, it must use
  shader_feature or multi_compile to select the correct includes.

COMMON MISTAKES
- CBUFFER_START(UnityPerMaterial): all material properties that are modified per-material
  must be inside this constant buffer (URP). Properties outside CBUFFER are GPU-uploaded
  per-draw-call, which is expensive for batching.
- Missing FallBack: for Built-in shaders, a FallBack ensures low-end devices render something
  reasonable. Flag shaders with no FallBack "Diffuse" or FallBack Off without justification.
- _Time usage: use _Time.y (seconds) for animation. Never use Unity C# Time.deltaTime or
  Time.time from a shader — these values cannot be passed without a script.
- ZWrite Off for transparent shaders: additive or alpha-blended shaders must have ZWrite Off
  to avoid writing to the depth buffer and causing incorrect sorting.

For each issue: identify the line or block, explain the GPU cost or correctness problem,
and provide the corrected HLSL/ShaderLab snippet.
```

---

## Example

**Input:**
```hlsl
half4 frag(Varyings IN) : SV_Target
{
    float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
    float4 mask = SAMPLE_TEXTURE2D(_MaskTex, sampler_MaskTex, IN.uv);
    float4 detail = SAMPLE_TEXTURE2D(_DetailTex, sampler_DetailTex, IN.uv);
    float brightness = 1.0;
    if (col.a > 0.5) brightness = 2.0;
    return col * mask * detail * brightness;
}
```

**Output:**
```
3 texture samples per fragment — WARNING for mobile
  Problem: _MainTex, _MaskTex, and _DetailTex are all sampled in the same fragment shader.
           This hits the recommended mobile budget of ≤2 samples. On Adreno 6xx/Mali-G7x,
           the third sample adds ~0.5ms per frame at 1080p.
  Fix: Pack the mask into the alpha channel of _MainTex (RGBA), or bake the detail
       multiplicatively into _MainTex at import time to eliminate one sample.

Dynamic branch on interpolated value — CRITICAL for mobile
  Problem: `if (col.a > 0.5)` branches on a sampled texture value. This causes warp
           divergence on mobile GPUs — both branches execute for every fragment in a warp,
           wasting half the GPU work.
  Fix: Use a smooth step or multiply instead:
  half brightness = 1.0 + step(0.5, col.a);  // 1.0 or 2.0, no branch
  // Or: half brightness = lerp(1.0, 2.0, step(0.5, col.a));

float4 used for col, mask, detail — WARNING
  Problem: All three sampled values are float4 (32-bit). For a color/mask that doesn't
           need full float precision, use half4 to halve ALU register pressure on mobile.
  Fix: half4 col = SAMPLE_TEXTURE2D(...); half4 mask = ...; half4 detail = ...;
```

---

## Notes

- GPU profiling (Mali Offline Compiler, Adreno Profiler, Xcode GPU Timeline) is the ground truth — use this skill for pre-submission review, not as a replacement for measuring.
- For desktop/console shaders, the mobile budget rules (texture count, half precision) do not apply — focus on correctness and pipeline checks instead.
