Skip to content

perf: replace pow(x, 5.0) with multiplies in Schlick Fresnel#8774

Open
willeastcott wants to merge 6 commits into
mainfrom
perf/schlick-fresnel-no-pow
Open

perf: replace pow(x, 5.0) with multiplies in Schlick Fresnel#8774
willeastcott wants to merge 6 commits into
mainfrom
perf/schlick-fresnel-no-pow

Conversation

@willeastcott
Copy link
Copy Markdown
Contributor

@willeastcott willeastcott commented May 24, 2026

Summary

  • Rewrites pow(1.0 - saturate(cosTheta), 5.0) as a manual chain of multiplies (x2 = x*x; fresnel = x2*x2*x) in both getFresnel and getFresnelCC.
  • Applied to both the GLSL and WGSL versions of the fresnelSchlick chunk.

pow(x, 5.0) compiles to exp2(5.0 * log2(x)) on most hardware — two transcendental ops. The four multiplies that compute x^5 directly are bit-exact and consistently cheaper, especially on mobile GPUs where transcendentals run at a lower throughput. With clearcoat enabled both Fresnel functions are invoked per light per fragment, so the saving compounds in lit scenes.

This is a standard industry optimization — Unreal, Unity, and Filament all use the same form for Schlick.

Test plan

  • No visual change in lit examples (Fresnel output is mathematically identical to the prior code).
  • Spot-check graphics_clustered-lighting and a clearcoat material — pixel diff should be zero modulo last-bit FP noise.
  • npm test and npm run lint pass (only pre-existing unrelated lint error in utils/esbuild-build-target.mjs).
  • Optional: use WEBGL_debug_shaders / Mali Offline Compiler to confirm pow is gone from the translated source / cycle count drops.

🤖 Generated with Claude Code

pow(x, 5.0) compiles to log2 + mul + exp2 on most hardware (two
transcendentals); the four multiplies that compute x^5 directly are
bit-exact and consistently cheaper, especially on mobile GPUs where
transcendentals are slower. Applied to both the main getFresnel and
clearcoat getFresnelCC, in both GLSL and WGSL chunks.

Output is mathematically identical to the prior implementation; no
visual change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add an inline comment at each call site so a future reader does not
"clean up" the manual multiply chain back into pow(x, 5.0).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the pre-existing square() helper. Collapses the four duplicated
x*x*x*x*x expansions in fresnelSchlick into single pow5() calls and
keeps the rationale comment in one place. Same idea as Filament's
common_math.glsl pow5 helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Optimizes the Schlick Fresnel term in the lit fragment shader chunks by replacing pow(x, 5) with an x^5 multiply chain, reducing reliance on transcendental ops in both GLSL and WGSL shader paths.

Changes:

  • Added a pow5 helper to the lit fragment base chunk (GLSL + WGSL).
  • Updated getFresnel and getFresnelCC in fresnelSchlick chunks to use pow5(...) (GLSL + WGSL).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/scene/shader-lib/wgsl/chunks/lit/frag/fresnelSchlick.js Switch Fresnel exponent from pow(..., 5) to pow5(...).
src/scene/shader-lib/wgsl/chunks/lit/frag/base.js Introduce pow5 helper in WGSL base chunk.
src/scene/shader-lib/glsl/chunks/lit/frag/fresnelSchlick.js Switch Fresnel exponent from pow(..., 5) to pow5(...).
src/scene/shader-lib/glsl/chunks/lit/frag/base.js Introduce pow5 helper in GLSL base chunk.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/scene/shader-lib/glsl/chunks/lit/frag/base.js Outdated
Comment thread src/scene/shader-lib/wgsl/chunks/lit/frag/base.js Outdated
Per review feedback: basePS is user-overridable, so adding a required
symbol to it would silently break any project that overrides basePS
when fresnelSchlickPS calls into it. pow5 has only two callers and they
are both in fresnelSchlickPS, so it belongs there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 4 changed files in this pull request and generated no new comments.

pow5 lives in fresnelSchlickPS now, so base.js no longer needs to be
touched. Reverting the operator-spacing tidy keeps this PR scoped to
the Fresnel perf change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: graphics Graphics related issue performance Relating to load times or frame rate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants