@vitreajs/vitrea 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -226,6 +226,15 @@ function occlusionLiftForPolicy(policy, profile = DEFAULT_MATERIAL_PROFILE) {
226
226
  const levels = profile.increasedOcclusionLiftByPolicy;
227
227
  return policy.ambientTint === "reduced" ? levels?.increaseContrast ?? profile.increasedOcclusionLift : levels?.reduceTransparency ?? profile.increasedOcclusionLift;
228
228
  }
229
+ function bodyChromaRetentionUnderPolicy(retention, policy) {
230
+ switch (policy.occlusion) {
231
+ case "nominal":
232
+ return retention;
233
+ case "increased":
234
+ case "opaque":
235
+ return 0;
236
+ }
237
+ }
229
238
  var DEFAULT_MATERIAL_PROFILE = {
230
239
  optics: {
231
240
  // σ = 8 for the regular variant, which keeps this package's blur and
@@ -735,6 +744,18 @@ var DEFAULT_MATERIAL_PROFILE = {
735
744
  // chain's level-1 width, and the tracker carries the floor that would close it.
736
745
  sizeHeavyTapSigma: 9,
737
746
  sizeHeavyTapSigma2x: 9,
747
+ // W30 G2's spanning set for the scale-selective scatter, at the values that
748
+ // make every term of it exactly zero (claims §5.156 §3, §5.158). The share is
749
+ // the single gate: at 0 no second heavy texture is allocated, no pass is
750
+ // encoded and no sample is taken, so the two widths are unread and the
751
+ // reference is unreachable behind its own zero gain. The two signed amounts
752
+ // are the scheme-conditioned leaves §5.159 fits; the widths and the reference
753
+ // are one raster's property and are not.
754
+ sizeHeavySecondSigma: 0,
755
+ sizeHeavySecondSigma2x: 0,
756
+ sizeHeavySecondShare: 0,
757
+ sizeScatterScaleGain: 0,
758
+ sizeScatterScaleRef: 0,
738
759
  sizeOcclusionGain: 0.05,
739
760
  sizeShadowGainMax: 1,
740
761
  /*
@@ -906,6 +927,17 @@ var DEFAULT_MATERIAL_PROFILE = {
906
927
  */
907
928
  collapseTransmission: 0.017,
908
929
  collapseTransmission2x: 0.07,
930
+ /*
931
+ * **SHIPS AT 0** (W31 G3; claims §5.164). The identity, and a post-seal leaf's
932
+ * default IS its identity, forever: `MATERIAL_IDENTITY_TABLE` is append-only
933
+ * and the fingerprint drops this leaf while it holds this value, so a default
934
+ * that moved off 0 would move every shipped document's digest at once.
935
+ *
936
+ * The macOS 27 documents carry their fitted values as patches; the two frozen
937
+ * macOS 26.5 documents carry none, which is what makes their recorded digests
938
+ * reproduce under the rule (claims §5.161 §7b).
939
+ */
940
+ bodyChromaRetention: 0,
909
941
  /*
910
942
  * FITTED 0.038 (W23 G1; claims §5.100 §3, W23 Decision Log 2 (b)) — and it is
911
943
  * NOT the dark material's rim, which is the one thing the charter thought it
@@ -1079,6 +1111,14 @@ var DEFAULT_MATERIAL_PROFILE = {
1079
1111
  outerShadow: {
1080
1112
  offsetPx: 7.95,
1081
1113
  sigmaPx: 15.55,
1114
+ // W30 G2's σ law, at the three zeros that make it the identity (claims
1115
+ // §5.156 §2, §5.158): σ(span) = sigmaPx + max(0, 0 · (span − 0)) = sigmaPx,
1116
+ // at every span and every scale. The macOS 26.5 material's σ genuinely is
1117
+ // span-invariant — 15.4…15.9 CSS px across spans 32…160 — so these zeros are
1118
+ // that measurement and not a placeholder for it.
1119
+ sigmaSlopePerSpan: 0,
1120
+ sigmaSpanRefPx: 0,
1121
+ sigmaThinOffsetPx: 0,
1082
1122
  spreadPx: 3.1,
1083
1123
  thinOcclusionDark: 0,
1084
1124
  thinOcclusionMid: 0.33,
@@ -1100,6 +1140,90 @@ var DEFAULT_MATERIAL_PROFILE = {
1100
1140
  glowGain: 0.6,
1101
1141
  sweepGain: 0.85
1102
1142
  };
1143
+ var MATERIAL_DIGEST_RULE_VERSION = 2;
1144
+ var MATERIAL_IDENTITY_TABLE = [
1145
+ {
1146
+ wave: "W30",
1147
+ /*
1148
+ * A TWO-LEAF gate, and the second leaf is in it rather than gated by it.
1149
+ * `sigmaThinOffsetPx` is not gated by the slope — at slope 0 a non-zero
1150
+ * offset moves σ at every span — and it is not inert on its own at a
1151
+ * non-zero slope either, where it is a real floor on the line. What the
1152
+ * committed case proves is the pair TOGETHER, and the grouping that would
1153
+ * put the offset under the slope's gate merges a macOS 26.5 light material
1154
+ * carrying `sigmaThinOffsetPx` 5 — five CSS px of extra blur on every outer
1155
+ * shadow, on both tiers — into the frozen digest itself (claims §5.161 §11,
1156
+ * finding B4 (b)).
1157
+ */
1158
+ gate: { "outerShadow.sigmaSlopePerSpan": 0, "outerShadow.sigmaThinOffsetPx": 0 },
1159
+ gated: ["outerShadow.sigmaSpanRefPx"],
1160
+ law: "\u03C3(span) = sigmaPx + max(sigmaThinOffsetPx, sigmaSlopePerSpan\xB7(span \u2212 sigmaSpanRefPx))",
1161
+ inertLawCase: 'packages/renderer-webgpu/test/w31-gate-groups.test.ts \u2014 "gate-group 1 \u2014 {sigmaSlopePerSpan 0, sigmaThinOffsetPx 0} gates sigmaSpanRefPx"; with the shipped-value half in w30-inert-laws.test.ts',
1162
+ whyGated: "The pivot is multiplied by the slope, so at slope 0 no value of it can reach \u03C3. The offset is NOT gated by the slope and is in the gate for that reason.",
1163
+ claims: "c9a \xA75.156 \xA72, \xA75.158, \xA75.161 \xA77b"
1164
+ },
1165
+ {
1166
+ wave: "W30",
1167
+ gate: { sizeHeavySecondShare: 0 },
1168
+ gated: ["sizeHeavySecondSigma", "sizeHeavySecondSigma2x"],
1169
+ law: "deep = heavy + sizeHeavySecondShare\xB7(heavy2 \u2212 heavy), heavy2 at sizeHeavySecondSigma{,2x}",
1170
+ inertLawCase: 'packages/renderer-webgpu/test/w31-gate-groups.test.ts \u2014 "gate-group 2 \u2014 {sizeHeavySecondShare 0} gates the two second-heavy widths"; with the shipped-value half in w30-inert-laws.test.ts',
1171
+ whyGated: "At share 0 the second heavy texture is not allocated, no pass is encoded and the optics pass never samples one, so the widths are unread whatever they hold.",
1172
+ claims: "c9a \xA75.156 \xA73, \xA75.158, \xA75.161 \xA77b"
1173
+ },
1174
+ {
1175
+ wave: "W30",
1176
+ gate: { sizeScatterScaleGain: 0 },
1177
+ gated: ["sizeScatterScaleRef"],
1178
+ law: "kScatter += sizeScatterScaleGain\xB7(stat \u2212 sizeScatterScaleRef)",
1179
+ inertLawCase: 'packages/renderer-webgpu/test/w31-gate-groups.test.ts \u2014 "gate-group 3 \u2014 {sizeScatterScaleGain 0} gates sizeScatterScaleRef"; with the shipped-value half in w30-inert-laws.test.ts',
1180
+ whyGated: "The reference is only ever read as a difference the gain multiplies, so at gain 0 no value of it can reach the mix. The macOS 27 LIGHT document exercises this entry: it declines the scatter with the gain at 0 and ships the reference at 0.03.",
1181
+ claims: "c9a \xA75.156 \xA73, \xA75.158, \xA75.161 \xA77b"
1182
+ },
1183
+ {
1184
+ wave: "W31",
1185
+ /*
1186
+ * A plain value drop, and injective for free. The retention multiplies the
1187
+ * mix's second term, so at 0 the composite is bit-identical to the one W30
1188
+ * left — which is what the goldens and the composite-identity case below
1189
+ * assert, and what lets this leaf land without an exemption.
1190
+ */
1191
+ gate: { bodyChromaRetention: 0 },
1192
+ gated: [],
1193
+ law: "colour = gamutAtLuma(mix(colour, backdrop\xB7(Y/Y_backdrop), bodyChromaRetention), Y)",
1194
+ inertLawCase: `packages/renderer-webgpu/test/w31-body-chroma.test.ts \u2014 "the composite is bit-identical at retention 0", "linear luma is held at every retention" and "is the shader's expression, term for term"; the drawn half in packages/renderer-webgpu/e2e/gpu/w31-body-chroma.spec.ts \u2014 "is bit-identical at the shipped identity, named explicitly" and "ON draws differently from OFF"; the CSS tier's half in packages/platform-web/test/w30-css-declaration-identity.test.ts, which compares every declaration against bytes recorded before any leaf existed`,
1195
+ whyGated: "Not gated \u2014 a plain value drop. At retention 0 the mix's second term is multiplied by zero and `colour` leaves the composite exactly as mix(backdrop, adapted, presentAlpha) produced it.",
1196
+ claims: "c9a \xA75.161 \xA75, \xA75.164",
1197
+ declaredFirstAs: {
1198
+ law: "colour = renormaliseToLinearLuma(mix(colour, chromaticityOf(backdrop) at colour's luma, bodyChromaRetention))",
1199
+ inertLawCase: "TO BE COMMITTED BY W31 G3 \u2014 the leaf does not exist yet. G0 names the shape and its identity (W31 X2); the case that proves the composite is bit-identical at 0, and the `test:gpu` case that the on state draws differently from off, land with the leaf."
1200
+ }
1201
+ }
1202
+ ];
1203
+ function materialLeafAt(resolved, path) {
1204
+ return path.split(".").reduce((node, key) => node !== null && typeof node === "object" ? node[key] : void 0, resolved);
1205
+ }
1206
+ function materialDigestDroppedLeaves(resolved) {
1207
+ const dropped = [];
1208
+ for (const entry of MATERIAL_IDENTITY_TABLE) {
1209
+ const held = Object.entries(entry.gate).every(([path, identity]) => {
1210
+ const value = materialLeafAt(resolved, path);
1211
+ return value !== void 0 && value === identity;
1212
+ });
1213
+ if (held)
1214
+ dropped.push(...Object.keys(entry.gate), ...entry.gated);
1215
+ }
1216
+ return dropped.sort();
1217
+ }
1218
+ function materialDigestInput(resolved) {
1219
+ const dropped = materialDigestDroppedLeaves(resolved);
1220
+ const strip = (value, prefix = "") => {
1221
+ if (value === null || typeof value !== "object" || Array.isArray(value))
1222
+ return value;
1223
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [prefix === "" ? key : `${prefix}.${key}`, key, child]).filter(([path]) => !dropped.includes(path)).map(([path, key, child]) => [key, strip(child, path)]));
1224
+ };
1225
+ return strip(resolved);
1226
+ }
1103
1227
  var REFRACTION_SCALE = DEFAULT_MATERIAL_PROFILE.refractionScale;
1104
1228
  var MATERIAL_OPTICS = DEFAULT_MATERIAL_PROFILE.optics;
1105
1229
  var ADAPTIVE_TINT_DARK = DEFAULT_MATERIAL_PROFILE.adaptiveTintDark;
@@ -1220,6 +1344,14 @@ function withMaterialOverrides(base, patch) {
1220
1344
  sizeToneLevelFar: patch.sizeToneLevelFar ?? base.sizeToneLevelFar,
1221
1345
  sizeHeavyTapSigma: patch.sizeHeavyTapSigma ?? base.sizeHeavyTapSigma,
1222
1346
  sizeHeavyTapSigma2x: patch.sizeHeavyTapSigma2x ?? base.sizeHeavyTapSigma2x,
1347
+ // W30 G2's spanning set. One line each, because this merge is explicit per
1348
+ // leaf and a leaf without its own line resolves silently to the base —
1349
+ // which for a leaf a document is meant to fit is a fit that does nothing.
1350
+ sizeHeavySecondSigma: patch.sizeHeavySecondSigma ?? base.sizeHeavySecondSigma,
1351
+ sizeHeavySecondSigma2x: patch.sizeHeavySecondSigma2x ?? base.sizeHeavySecondSigma2x,
1352
+ sizeHeavySecondShare: patch.sizeHeavySecondShare ?? base.sizeHeavySecondShare,
1353
+ sizeScatterScaleGain: patch.sizeScatterScaleGain ?? base.sizeScatterScaleGain,
1354
+ sizeScatterScaleRef: patch.sizeScatterScaleRef ?? base.sizeScatterScaleRef,
1223
1355
  sizeOcclusionGain: patch.sizeOcclusionGain ?? base.sizeOcclusionGain,
1224
1356
  sizeShadowGainMax: patch.sizeShadowGainMax ?? base.sizeShadowGainMax,
1225
1357
  lensRefractionGain: patch.lensRefractionGain ?? base.lensRefractionGain,
@@ -1256,6 +1388,7 @@ function withMaterialOverrides(base, patch) {
1256
1388
  backdropToneSizeBias: patch.backdropToneSizeBias ?? base.backdropToneSizeBias,
1257
1389
  collapseTransmission: patch.collapseTransmission ?? base.collapseTransmission,
1258
1390
  collapseTransmission2x: patch.collapseTransmission2x ?? patch.collapseTransmission ?? base.collapseTransmission2x,
1391
+ bodyChromaRetention: patch.bodyChromaRetention ?? base.bodyChromaRetention,
1259
1392
  rimCollapsed: patch.rimCollapsed ?? base.rimCollapsed,
1260
1393
  rimCollapsedTinted: patch.rimCollapsedTinted ?? base.rimCollapsedTinted,
1261
1394
  rimTintChroma: patch.rimTintChroma ?? base.rimTintChroma,
@@ -1554,6 +1687,11 @@ function scatterHeavyShareThickAtScale(profile = DEFAULT_MATERIAL_PROFILE, devic
1554
1687
  function heavyTapSigmaAtScale(profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
1555
1688
  return rampAtScale(profile.sizeHeavyTapSigma, profile.sizeHeavyTapSigma2x, devicePixelRatio);
1556
1689
  }
1690
+ function heavySecondTapSigmaAtScale(profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
1691
+ if (profile.sizeHeavySecondShare === 0)
1692
+ return 0;
1693
+ return rampAtScale(profile.sizeHeavySecondSigma, profile.sizeHeavySecondSigma2x, devicePixelRatio);
1694
+ }
1557
1695
  function sizeToneLevelFar(spanPx, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, fold = 1) {
1558
1696
  return profile.sizeToneLevelFar * smoothstep(profile.sizeSpanMax, scatterSpanMaxAtScale(profile, devicePixelRatio), spanPx) * fold;
1559
1697
  }
@@ -1695,20 +1833,24 @@ function flatOuterShadow(shadow, amplitude) {
1695
1833
  var SRGB_ENCODING_EXPONENT = 2.4;
1696
1834
  function outerShadowFalloff(signedDistancePx, sigmaPx) {
1697
1835
  const x = -signedDistancePx / Math.max(sigmaPx, 1e-4);
1698
- return 0.5 * (1 + Math.tanh(0.7978845608028654 * (x + 0.044715 * x * x * x)));
1836
+ const t = Math.min(20, Math.max(-20, 0.7978845608028654 * (x + 0.044715 * x * x * x)));
1837
+ return 0.5 * (1 + Math.tanh(t));
1838
+ }
1839
+ function outerShadowSigmaPx(shadow, spanPx) {
1840
+ return shadow.sigmaPx + Math.max(shadow.sigmaThinOffsetPx, shadow.sigmaSlopePerSpan * (spanPx - shadow.sigmaSpanRefPx));
1699
1841
  }
1700
1842
  function outerShadowAlpha(occlusion) {
1701
1843
  const occ = Math.min(1, Math.max(0, occlusion));
1702
1844
  return 1 - Math.pow(1 - occ, 1 / SRGB_ENCODING_EXPONENT);
1703
1845
  }
1704
- function outerShadowReachPx(shadow, occlusion) {
1846
+ function outerShadowReachPx(shadow, occlusion, spanPx) {
1705
1847
  const alpha = outerShadowAlpha(occlusion);
1706
1848
  if (!(alpha > 0))
1707
1849
  return 0;
1708
1850
  const cutoff = 1 / 255 / alpha;
1709
1851
  if (cutoff >= 1)
1710
1852
  return 0;
1711
- const sigma = Math.max(shadow.sigmaPx, 1e-4);
1853
+ const sigma = Math.max(outerShadowSigmaPx(shadow, spanPx), 1e-4);
1712
1854
  let lo = -(8 * sigma + Math.abs(shadow.offsetPx) + Math.abs(shadow.spreadPx));
1713
1855
  let hi = 8 * sigma;
1714
1856
  for (let i = 0; i < 48; i += 1) {
@@ -2570,6 +2712,10 @@ var poolKey = {
2570
2712
  /** The heavy blur and its scratch (W26) — the body's pair, one width deeper. */
2571
2713
  backdropHeavy: (sourceId) => `backdrop:${sourceId}:heavy`,
2572
2714
  backdropHeavyScratch: (sourceId) => `backdrop:${sourceId}:heavy-scratch`,
2715
+ /** The SECOND heavy blur and its scratch (W30 G2) — the same pair, one more
2716
+ * width along, acquired only where `sizeHeavySecondShare` is non-zero. */
2717
+ backdropHeavy2: (sourceId) => `backdrop:${sourceId}:heavy2`,
2718
+ backdropHeavy2Scratch: (sourceId) => `backdrop:${sourceId}:heavy2-scratch`,
2573
2719
  backdropUpload: (sourceId) => `backdrop:${sourceId}:upload`,
2574
2720
  /*
2575
2721
  * The four field targets. Their key is a group's RESOURCE identity — the id
@@ -3481,9 +3627,26 @@ const TAU = 6.283185307179586;
3481
3627
 
3482
3628
  /// Shortest angular distance, so the band wraps continuously past the seam
3483
3629
  /// instead of stalling there for one revolution.
3630
+ ///
3631
+ /// THE RESULT IS FLOORED AT ZERO, AND THE FLOOR IS THE IDENTITY (W31 G2; claims
3632
+ /// 5.163). A distance is non-negative, and this expression is only non-negative
3633
+ /// while 'raw' stays inside one revolution: past 'TAU' the second branch goes
3634
+ /// negative and 'min' takes it. The single caller squares the quotient through
3635
+ /// 'pow(x, 2.0)', whose base WGSL leaves UNDEFINED below zero \u2014 the same shape
3636
+ /// of defect as 5.159b's 'tanh', one function along. Both arguments are angles
3637
+ /// only while the sweep phase is inside [0, 1], and the phase is read off a CSS
3638
+ /// custom property that nothing clamps (platform-web's 'readHostChannels'
3639
+ /// clamps 'materialization' and not 'sweep'), so '--vitrea-sweep: 3' puts the
3640
+ /// centre at 3 TAU and this function at -12.6 radians. 'max(., 0)' returns the
3641
+ /// same bits at every input the unfloored form was defined at, so no pixel any
3642
+ /// material or any bed reaches moves; what it removes is the undefined one.
3643
+ /// Where the phase IS out of range the band is now centred on the pixel rather
3644
+ /// than NaN, which is a defensible reading of nothing in particular \u2014 wrapping
3645
+ /// the phase where it is read is the fix that would make it mean something, and
3646
+ /// it is outside this gate's contract.
3484
3647
  fn angle_delta(a : f32, b : f32) -> f32 {
3485
3648
  let raw = abs(a - b);
3486
- return min(raw, TAU - raw);
3649
+ return max(min(raw, TAU - raw), 0.0);
3487
3650
  }
3488
3651
 
3489
3652
  @fragment
@@ -3736,6 +3899,36 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
3736
3899
  toneExtra : vec4f,
3737
3900
  /// W28: whether the per-surface reference field is present (x).
3738
3901
  localTone : vec4f,
3902
+ /// W30's span-graded shadow sigma (claims 5.156 section 2): the law's slope
3903
+ /// in CSS px of sigma per CSS px of casting span (x), the reference span it
3904
+ /// pivots about (y) and the signed floor it is clamped below at, as an offset
3905
+ /// from 'shadow.y' (z). (w) free. At the shipped zeros this evaluates to
3906
+ /// 'shadow.y' at every span, identically, which is why the 34 goldens do not
3907
+ /// move. No device ratio: the cut rejected the device-px reading of the thin
3908
+ /// regime, so the law is one function of CSS span on both tiers.
3909
+ shadowSigma : vec4f,
3910
+ /// W30's scale-selective scatter, candidate (ii) (claims 5.156 section 3): the
3911
+ /// gain on 'kScatter' per unit of the source's measured scale statistic (x),
3912
+ /// the reference that statistic is measured about (y), and the statistic
3913
+ /// itself for THIS group's source (z) \u2014 the analysis pass's edge density,
3914
+ /// resolved on the CPU because it arrives by readback. (w) free.
3915
+ scatterScale : vec4f,
3916
+ /// W30's scale-selective scatter, candidate (i) (claims 5.156 section 3): the
3917
+ /// second heavy sample's SIGNED share in the deep mix (x), and whether this
3918
+ /// group's source carries a second heavy texture at all (y) \u2014 1 where the
3919
+ /// pyramid built one, 0 where the share declined it. A vec4 of its own rather
3920
+ /// than two lanes in the layout above, on 'heavyTap's precedent: a width's
3921
+ /// switch living in another facet's spare lane is a layout nobody could read
3922
+ /// back. (z) and (w) free.
3923
+ scatterHeavy2 : vec4f,
3924
+ /// W31's body chroma retention (claims 5.161 section 5, 5.164): how much of
3925
+ /// the blurred backdrop's CHROMATICITY the body restores, at the luma the
3926
+ /// tone solve produced (x). A vec4 of its own on W30's rule \u2014 132 is the next
3927
+ /// vec4 boundary and an operator packed into the block above would read two
3928
+ /// of its neighbour's lanes. (y), (z) and (w) free. At the shipped 0 the mix
3929
+ /// below is multiplied by zero and the composite is bit-identical to the one
3930
+ /// W30 left, which is why the 34 goldens do not move.
3931
+ bodyChroma : vec4f,
3739
3932
  };
3740
3933
 
3741
3934
  @group(0) @binding(0) var<uniform> ou : OpticsUniforms;
@@ -3760,6 +3953,15 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
3760
3953
  @group(0) @binding(9) var presenceTexture : texture_2d<f32>;
3761
3954
  @group(0) @binding(10) var localToneTexture : texture_2d<f32>;
3762
3955
 
3956
+ /// The SECOND heavy blur (W30 G2) \u2014 the same construction one width along, and
3957
+ /// read only where 'scatterHeavy2.y' says the pyramid built one. A bind group's
3958
+ /// layout is one layout, so the slot exists at every draw and the placeholder
3959
+ /// view stands in it where the material declined the operator; what costs
3960
+ /// nothing at the inert share is the allocation and the two separable passes,
3961
+ /// which is where the price of this mechanism actually is (W26 Decision Log
3962
+ /// 2 (b)).
3963
+ @group(0) @binding(11) var backdropHeavy2 : texture_2d<f32>;
3964
+
3763
3965
  /// One encoded sRGB channel from a linear one \u2014 the space the backdrop tone
3764
3966
  /// response's anchors live in (W9). Mirrors material.ts's 'linearToSrgbChannel'.
3765
3967
  fn srgb_encode(c : f32) -> f32 {
@@ -3846,9 +4048,47 @@ fn rim_weight(d : f32, width : f32) -> f32 {
3846
4048
  /// normal CDF \u2014 WGSL has no erf, and this is within 1.8e-4 of it everywhere,
3847
4049
  /// which is 0.015 of one 8-bit code at the shipped occlusion. Mirrors
3848
4050
  /// material.ts's 'outerShadowFalloff' term for term.
4051
+ ///
4052
+ /// THE ARGUMENT IS CLAMPED, AND THE CLAMP IS THE IDENTITY (W30 G3b; claims
4053
+ /// 5.159b, corrected at 5.159b section 10). 'tanh' saturates to exactly 1.0 in
4054
+ /// f32 by |t| = 9.011 and in f64 by |t| = 19.061547465398498 \u2014 measured, not
4055
+ /// 18.2, which the first recording of this comment said and which is the last
4056
+ /// magnitude that still returns 1 - 1e-16. Either way 20 is above both, so
4057
+ /// replacing every |t| > 20 with 20 returns the same bits in both precisions at
4058
+ /// every input the unclamped form evaluates finitely; the bound has 0.94 of
4059
+ /// margin over f64 rather than 1.8, which is what the corrected reading
4060
+ /// changes. What it
4061
+ /// removes is a NaN: a backend that lowers 'tanh' to (exp(2t) - 1)/(exp(2t) + 1)
4062
+ /// \u2014 which Metal's fast-math path does \u2014 overflows f32's 'exp' at 2t > 88.72 and
4063
+ /// hands back Inf/Inf. The cubic makes that threshold reachable at a modest
4064
+ /// distance: t passes 44.36 at x ~ 10.06, so any pixel more than about 10 sigma
4065
+ /// inside the shadow's silhouette returned NaN, and the NaN travelled into the
4066
+ /// composite's alpha through 'shadowAlpha * (1 - coverage)', where a coverage of
4067
+ /// exactly 1 does not stop it (NaN times 0 is NaN). At every sigma the project
4068
+ /// had shipped, 10 sigma was further than any caster is deep and nothing reached
4069
+ /// it; macOS 27's thin regime draws sigma 2.13 at a span-44 caster, where 10
4070
+ /// sigma is 21.4 CSS px and a 44 px capsule's own centre line is 25 CSS px
4071
+ /// inside its silhouette \u2014 which is the strip 5.159 section 6 measured.
3849
4072
  fn outer_shadow_falloff(signedDistance : f32, sigma : f32) -> f32 {
3850
4073
  let x = -signedDistance / max(sigma, 1e-4);
3851
- return 0.5 * (1.0 + tanh(0.7978845608028654 * (x + 0.044715 * x * x * x)));
4074
+ let t = clamp(0.7978845608028654 * (x + 0.044715 * x * x * x), -20.0, 20.0);
4075
+ return 0.5 * (1.0 + tanh(t));
4076
+ }
4077
+
4078
+ /// The outer shadow's sigma at a casting span, CSS px (W30 G2; claims 5.156
4079
+ /// section 2). Mirrors material.ts's 'outerShadowSigmaPx' term for term:
4080
+ ///
4081
+ /// sigma(span) = shadow.y + max(shadowSigma.z, shadowSigma.x * (span - shadowSigma.y))
4082
+ ///
4083
+ /// Read PER PIXEL from the CASTING surface's own span, which the field pass has
4084
+ /// already unioned into the aux target \u2014 the span this shader has been reading
4085
+ /// the thick regime's amplitude from since W14 G1, now read twice. At the
4086
+ /// shipped zeros both arms of the max are zero and this is 'ou.shadow.y'
4087
+ /// exactly, for every span, which makes the law's landing a no-op on every
4088
+ /// existing pixel by construction rather than by measurement.
4089
+ fn outer_shadow_sigma(spanCss : f32) -> f32 {
4090
+ return ou.shadow.y
4091
+ + max(ou.shadowSigma.z, ou.shadowSigma.x * (spanCss - ou.shadowSigma.y));
3852
4092
  }
3853
4093
 
3854
4094
  /// What one pixel's outer shadow is made of: the compositing-space alpha the
@@ -3971,7 +4211,10 @@ fn outer_shadow(uv : vec2f, upsampled : f32, fieldSize : vec2f) -> ShadowSample
3971
4211
  // Times the casting surface's presence (W27d): a material that is not there
3972
4212
  // occludes nothing. At presence 1 this is the alpha the bed measures.
3973
4213
  out.alpha = (1.0 - pow(1.0 - occ, 1.0 / 2.4)) * castMat;
3974
- out.falloff = outer_shadow_falloff(shadowField.x + clampedOffCss - ou.shadow.z, ou.shadow.y);
4214
+ out.falloff = outer_shadow_falloff(
4215
+ shadowField.x + clampedOffCss - ou.shadow.z,
4216
+ outer_shadow_sigma(shadowAux.z),
4217
+ );
3975
4218
  out.castSpanCss = shadowAux.z;
3976
4219
  out.castMat = castMat;
3977
4220
  return out;
@@ -4059,6 +4302,54 @@ fn dom_material_output(encodedComposite : vec3f, encodedBackdrop : vec3f, alpha
4059
4302
  return vec4f(clamp(c - b * (1.0 - a), vec3f(0.0), vec3f(a)), a);
4060
4303
  }
4061
4304
 
4305
+ /// W31 \u2014 the body's chroma retention, at a held linear luma (claims 5.161
4306
+ /// section 5, fitted in 5.164).
4307
+ ///
4308
+ /// The body is a neutral plate over the blurred backdrop, so what a
4309
+ /// photograph's hues survive the composite at is '1 - sizedAlpha'. This
4310
+ /// restores the colour's CHROMATICITY toward the blurred backdrop's, carried to
4311
+ /// the colour's OWN linear luma, by the material's retention.
4312
+ ///
4313
+ /// **Luma is held by construction rather than by correction.** Both endpoints
4314
+ /// of the mix have linear luma exactly 'Y' \u2014 'colour' by definition and
4315
+ /// 'target' because it is the backdrop scaled to 'Y' \u2014 and linear luma is a
4316
+ /// linear functional, so the mix has luma 'Y' in exact arithmetic. The
4317
+ /// renormalisation afterwards is an f32 rounding guard and nothing else.
4318
+ ///
4319
+ /// Gamut by scaling chroma toward the neutral AT that luma, never by clipping
4320
+ /// per channel: a per-channel clamp moves the level, which is the one thing
4321
+ /// this operator may not do.
4322
+ ///
4323
+ /// The retention is the identity outside 'Y' in (1e-6, 1], which is a
4324
+ /// finiteness guard rather than a restriction: 'backdrop' and 'adapted' are both
4325
+ /// in [0, 1] and 'presentAlpha' is in [0, 1], so their mix is too. Writing it as
4326
+ /// a branch on the retention as well makes the identity at 0 EXACT \u2014 no
4327
+ /// division, no rounding, the composite left as it arrived.
4328
+ fn gamut_at_luma(c : vec3f, Y : f32) -> vec3f {
4329
+ var t = 1.0;
4330
+ for (var i = 0u; i < 3u; i = i + 1u) {
4331
+ let d = c[i] - Y;
4332
+ if (d > 1e-7) { t = min(t, (1.0 - Y) / d); }
4333
+ else if (d < -1e-7) { t = min(t, -Y / d); }
4334
+ }
4335
+ return mix(vec3f(Y), c, clamp(t, 0.0, 1.0));
4336
+ }
4337
+
4338
+ fn body_chroma_retention(colour : vec3f, backdrop : vec3f, retention : f32) -> vec3f {
4339
+ if (retention <= 0.0) { return colour; }
4340
+ let W = vec3f(0.2126, 0.7152, 0.0722);
4341
+ let Y = dot(colour, W);
4342
+ if (!(Y > 1e-6 && Y <= 1.0)) { return colour; }
4343
+ let Yb = dot(backdrop, W);
4344
+ if (Yb <= 1e-6) { return colour; }
4345
+ // 'toward' and not 'target': WGSL reserves the latter.
4346
+ let toward = backdrop * (Y / Yb);
4347
+ var restored = mix(colour, toward, clamp(retention, 0.0, 1.0));
4348
+ let Yr = dot(restored, W);
4349
+ if (Yr > 1e-6) { restored = restored * (Y / Yr); }
4350
+ return gamut_at_luma(restored, Y);
4351
+ }
4352
+
4062
4353
  @fragment
4063
4354
  fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
4064
4355
  // Nominally the field is one texel per device pixel, so the read is an exact
@@ -4296,7 +4587,24 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
4296
4587
  let rampStart = ou.scatter.y + (ou.shadowSize.z - ou.scatter.y) * sizeThick
4297
4588
  + (ou.shadowThick.w - ou.shadowSize.z) * farS;
4298
4589
  let sharpShare = clamp(sDeep + max(rampStart - sDeep, 0.0) * rampT, 0.0, 1.0);
4299
- let kScatter = clamp(scatterFloor + ((1.0 - sharpShare) - scatterFloor) * fold, 0.0, 1.0);
4590
+ let kScatterSpan = clamp(scatterFloor + ((1.0 - sharpShare) - scatterFloor) * fold, 0.0, 1.0);
4591
+ /*
4592
+ * W30's scale conditioning, candidate (ii) (claims 5.156 section 3): the deep
4593
+ * component's share moves with the BACKDROP's own spatial scale and not only
4594
+ * with the surface's span, which is what a residual that changes sign between
4595
+ * a 4 px pitch and a 16 px one needs. The statistic is the analysis pass's
4596
+ * per-source edge density, a reciprocal length; it arrives by readback, so the
4597
+ * CPU puts it in 'scatterScale.z' and the law is evaluated here.
4598
+ *
4599
+ * At the shipped gain of 0 the added term is a multiplied zero and the clamp
4600
+ * is the identity on a value already clamped to the same interval, so this is
4601
+ * 'kScatterSpan' to the bit.
4602
+ */
4603
+ let kScatter = clamp(
4604
+ kScatterSpan + ou.scatterScale.x * (ou.scatterScale.z - ou.scatterScale.y),
4605
+ 0.0,
4606
+ 1.0,
4607
+ );
4300
4608
  // The inner shadow's depth and profile: W2's law, byte for byte \u2014 the
4301
4609
  // thickness times the size gain (folded through 'sizeK'), clamped to the
4302
4610
  // shorter half extent, and a square on it. The lens ran on this until W12 G2;
@@ -4406,7 +4714,25 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
4406
4714
  // Premultiplied linear in, straight colour out: the material composites over
4407
4715
  // whatever is behind it, so a partially transparent backdrop must not darken
4408
4716
  // the glass.
4409
- let scatterColour = scatterSample.rgb / max(scatterSample.a, 1e-6);
4717
+ var scatterColour = scatterSample.rgb / max(scatterSample.a, 1e-6);
4718
+ /*
4719
+ * W30's second heavy tap, candidate (i) (claims 5.156 section 3): a second
4720
+ * texture at its own width, mixed into the deep sample by a SIGNED share.
4721
+ * A positive share widens the deep component toward that width; a negative
4722
+ * one subtracts it, which is an unsharp mask on the backdrop and the only
4723
+ * shape on offer that passes the middle pitch LESS than both ends \u2014 a mix of
4724
+ * two positive Gaussians is monotone in frequency and cannot.
4725
+ *
4726
+ * Gated on whether the pyramid built one, which it does only where
4727
+ * 'sizeHeavySecondShare' is non-zero. At the inert share the branch is not
4728
+ * taken, nothing is allocated and nothing is sampled, which is the state the
4729
+ * 34 goldens render.
4730
+ */
4731
+ if (ou.scatterHeavy2.y > 0.5) {
4732
+ let second = textureSampleLevel(backdropHeavy2, backdropSampler, refractedUv, 0.0);
4733
+ let secondColour = second.rgb / max(second.a, 1e-6);
4734
+ scatterColour = scatterColour + ou.scatterHeavy2.x * (secondColour - scatterColour);
4735
+ }
4410
4736
  backdrop = mix(bodySample.rgb / max(bodySample.a, 1e-6), scatterColour, kScatter);
4411
4737
  /*
4412
4738
  * The body's own half of the presence (W27d): a material at half presence
@@ -4605,6 +4931,56 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
4605
4931
  */
4606
4932
  let presentAlpha = adaptedAlpha * mat;
4607
4933
  var colour = mix(backdrop, adapted, presentAlpha);
4934
+ /*
4935
+ * W31's chroma retention, HERE and not later (claims 5.161 section 5).
4936
+ *
4937
+ * Immediately after the composite, because the composite is where the
4938
+ * chroma is lost \u2014 the plate is neutral and the backdrop's chromaticity
4939
+ * survives it scaled by '1 - presentAlpha'. Before the tint composition
4940
+ * below, so an author's tint still displaces the result per the composition
4941
+ * contract; the tint's shade law reads the untinted material's LUMINANCE,
4942
+ * which this preserves exactly, so 'shade', 'layer' and 'rimTintColour' are
4943
+ * bit-identical whatever the retention holds.
4944
+ *
4945
+ * Before the DOM branch as well, so the secant that solves an unsampled DOM
4946
+ * group's layer alpha solves it from the material the page will actually
4947
+ * show. That branch is NOT luma-transparent \u2014 'dom_material_alpha' clamps per
4948
+ * channel inside a luma computation \u2014 so an unsampled DOM GROUP'S layer alpha
4949
+ * may move with the retention where a sampled composite does not (claims
4950
+ * 5.161 section 11, the second reading carried forward).
4951
+ *
4952
+ * That sentence said "the 'dom' tier", which is a different thing and is the
4953
+ * thing a reader will think of first (W31 G3c review closure; claims 5.164
4954
+ * section 13, finding N7). The 'dom' tier is the CSS tier, the one the
4955
+ * calibration matrix keys under that name, and it does not run this shader at
4956
+ * all; what is meant here is THIS shader's unsampled-material path, where a
4957
+ * group with no sampled backdrop writes a layer for the browser to composite.
4958
+ *
4959
+ * And on that path what the operator restores TOWARD is not the page's
4960
+ * backdrop, because nothing sampled it. 'dom_material_backdrop()' fabricates
4961
+ * one, and it has two modes. Mode 1, which a group with no declared
4962
+ * 'backdropTone' takes, returns 'vec3f(ou.heavyTap.z)' \u2014 a neutral at the
4963
+ * declared reference luminance \u2014 so the mix target is
4964
+ * 'backdrop * (Y / Yb)' = 'vec3f(Y)', the neutral at the colour's own luma.
4965
+ * That reads as a desaturation and is the IDENTITY in effect: on that path
4966
+ * 'adapted' is 'solvedNeutral' and the fabricated backdrop is a grey, so the
4967
+ * composite has no chromaticity for the target to differ from. Measured at
4968
+ * three retentions up to 1: zero bytes moved
4969
+ * ('e2e/gpu/w31-unsampled-dom-chroma.spec.ts').
4970
+ *
4971
+ * Mode 2 hands the DECLARED tone colour, which can be chromatic, and there
4972
+ * the operator runs \u2014 the body takes the hue the page STATED is behind it
4973
+ * rather than the hue that is. That is the residual, recorded in claims 5.164
4974
+ * section 10, and it is a statement about the target rather than about the
4975
+ * amount: the same spec reads the interior's chroma from 0.066 to 0.334 and
4976
+ * finds the gamut clamp binding at the shipped retention already.
4977
+ *
4978
+ * On the unsampled LAYER path ('flags.x <= 0.5' and not 'domMaterial')
4979
+ * 'colour' is overwritten with 'adapted' a few lines below: there is no
4980
+ * backdrop in hand and no chromaticity to restore toward, and the retention is
4981
+ * silently the identity. That is a declared residual, not an oversight.
4982
+ */
4983
+ colour = body_chroma_retention(colour, backdrop, ou.bodyChroma.x);
4608
4984
  /*
4609
4985
  * How much of this pixel the SURFACE owns, as the canvas will composite it.
4610
4986
  *
@@ -4978,7 +5354,13 @@ var WGSL_PRELUDE = `// vitrea:wgsl-marker
4978
5354
 
4979
5355
  fn srgb_to_linear(c : vec3f) -> vec3f {
4980
5356
  let lo = c / 12.92;
4981
- let hi = pow((c + vec3f(0.055)) / 1.055, vec3f(2.4));
5357
+ // The floor is the identity at every component the select below KEEPS: it
5358
+ // keeps hi only where c > 0.04045, and there the base is already above 0.0905.
5359
+ // Below zero the base is a pow WGSL leaves undefined, and a clamp in the
5360
+ // source is preferred to a proof of the select's discard (W31 G2 review
5361
+ // closure, claims \xA75.163 \xA78, finding N8). Same shape as linear_to_srgb's own
5362
+ // max, one line down.
5363
+ let hi = pow(max((c + vec3f(0.055)) / 1.055, vec3f(0.0)), vec3f(2.4));
4982
5364
  return select(hi, lo, c <= vec3f(0.04045));
4983
5365
  }
4984
5366
 
@@ -5359,7 +5741,7 @@ function createPassRunner(context) {
5359
5741
  };
5360
5742
  },
5361
5743
  opticsPass(encoder, args) {
5362
- const slot = uniformSlot(`optics:${args.resourceId}`, 120);
5744
+ const slot = uniformSlot(`optics:${args.resourceId}`, 136);
5363
5745
  const d = slot.data;
5364
5746
  d[0] = args.viewportDevice[0];
5365
5747
  d[1] = args.viewportDevice[1];
@@ -5478,10 +5860,27 @@ function createPassRunner(context) {
5478
5860
  d[114] = args.backdropToneResponseThick[3] ?? args.backdropToneResponseThick[2];
5479
5861
  d[115] = args.backdropToneAnchorX.length === 4 ? 1 : 0;
5480
5862
  d[116] = args.localTone === void 0 ? 0 : 1;
5863
+ d[120] = args.outerShadowSigmaLaw[0];
5864
+ d[121] = args.outerShadowSigmaLaw[1];
5865
+ d[122] = args.outerShadowSigmaLaw[2];
5866
+ d[123] = 0;
5867
+ d[124] = args.sizeScatterScaleGain;
5868
+ d[125] = args.sizeScatterScaleRef;
5869
+ d[126] = args.backdropScaleStatistic;
5870
+ d[127] = 0;
5871
+ d[128] = args.sizeHeavySecondShare;
5872
+ d[129] = args.heavySecondEnabled ? 1 : 0;
5873
+ d[130] = 0;
5874
+ d[131] = 0;
5875
+ d[132] = args.bodyChromaRetention;
5876
+ d[133] = 0;
5877
+ d[134] = 0;
5878
+ d[135] = 0;
5481
5879
  slot.write();
5482
5880
  const chain = args.backdrop?.chain ?? placeholderView;
5483
5881
  const body = args.backdrop?.body ?? placeholderView;
5484
5882
  const heavy = args.backdrop?.heavy ?? placeholderView;
5883
+ const heavy2 = args.backdrop?.heavy2 ?? placeholderView;
5485
5884
  const pipeline = opticsPipeline(args.targetFormat);
5486
5885
  const pass = encoder.beginRenderPass({
5487
5886
  label: `vitrea:pass:optics:${args.resourceId}`,
@@ -5505,7 +5904,8 @@ function createPassRunner(context) {
5505
5904
  { binding: 7, resource: args.fields.aux2.createView() },
5506
5905
  { binding: 8, resource: heavy },
5507
5906
  { binding: 9, resource: args.fields.presence.createView() },
5508
- { binding: 10, resource: args.localTone ?? placeholderView }
5907
+ { binding: 10, resource: args.localTone ?? placeholderView },
5908
+ { binding: 11, resource: heavy2 }
5509
5909
  ]
5510
5910
  }));
5511
5911
  pass.draw(3);
@@ -5763,7 +6163,7 @@ var chainUsage = () => GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTU
5763
6163
  var densityOf = (source, request) => texelsPerCssPx(source.width, source.height, request.placement, request.viewportCss[0], request.viewportCss[1]);
5764
6164
  var same = (next, existing) => Math.abs(next - existing) <= 1e-6 * Math.max(1, Math.abs(existing));
5765
6165
  var sameHeavySigma = (next, existing) => next > 0 === existing > 0 && same(next, existing);
5766
- var sameBody = (existing, request) => same(densityOf({ width: existing.sourceWidth, height: existing.sourceHeight }, request), existing.texelsPerCss) && same(request.bodySigmaCss, existing.bodySigmaCss) && sameHeavySigma(request.heavySigmaCss, existing.heavySigmaCss);
6166
+ var sameBody = (existing, request) => same(densityOf({ width: existing.sourceWidth, height: existing.sourceHeight }, request), existing.texelsPerCss) && same(request.bodySigmaCss, existing.bodySigmaCss) && sameHeavySigma(request.heavySigmaCss, existing.heavySigmaCss) && sameHeavySigma(request.heavy2SigmaCss, existing.heavy2SigmaCss);
5767
6167
  function createPyramidStore(context) {
5768
6168
  const { device, pool, cache } = context;
5769
6169
  const resources = /* @__PURE__ */ new Map();
@@ -5839,10 +6239,13 @@ function createPyramidStore(context) {
5839
6239
  if (target.heavy !== void 0 && pool.peek(poolKey.backdropHeavy(sourceId)) !== target.heavy) {
5840
6240
  return void 0;
5841
6241
  }
6242
+ if (target.heavy2 !== void 0 && pool.peek(poolKey.backdropHeavy2(sourceId)) !== target.heavy2) {
6243
+ return void 0;
6244
+ }
5842
6245
  return target;
5843
6246
  };
5844
6247
  const mipView = (texture, level) => texture.createView({ baseMipLevel: level, mipLevelCount: 1, dimension: "2d" });
5845
- function allocate(sourceId, plan, bodyLevel, sizeEpoch, builtEpoch, bodySigmaTexels, density, bodySigmaCss, heavyLevel, heavySigmaCss) {
6248
+ function allocate(sourceId, plan, bodyLevel, sizeEpoch, builtEpoch, bodySigmaTexels, density, bodySigmaCss, heavyLevel, heavySigmaCss, heavy2Level, heavy2SigmaCss) {
5846
6249
  const existing = resources.get(sourceId);
5847
6250
  const levelSize = (level) => plan.levels[level] ?? plan.levels[0];
5848
6251
  const bodyWidth = levelSize(bodyLevel).width;
@@ -5875,6 +6278,19 @@ function createPyramidStore(context) {
5875
6278
  label: `vitrea:pyramid:${sourceId}:heavy`
5876
6279
  });
5877
6280
  }
6281
+ let heavy2;
6282
+ if (heavy2Level === void 0) {
6283
+ pool.release(poolKey.backdropHeavy2(sourceId));
6284
+ pool.release(poolKey.backdropHeavy2Scratch(sourceId));
6285
+ } else {
6286
+ heavy2 = pool.acquire(poolKey.backdropHeavy2(sourceId), {
6287
+ width: levelSize(heavy2Level).width,
6288
+ height: levelSize(heavy2Level).height,
6289
+ format: WORKING_TEXTURE_FORMAT,
6290
+ usage: chainUsage(),
6291
+ label: `vitrea:pyramid:${sourceId}:heavy2`
6292
+ });
6293
+ }
5878
6294
  let stats = existing?.stats;
5879
6295
  if (stats === void 0) {
5880
6296
  stats = device.createBuffer({
@@ -5883,7 +6299,7 @@ function createPyramidStore(context) {
5883
6299
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
5884
6300
  });
5885
6301
  }
5886
- if (existing === void 0 || existing.chain !== chain || existing.body !== body || existing.heavy !== heavy) {
6302
+ if (existing === void 0 || existing.chain !== chain || existing.body !== body || existing.heavy !== heavy || existing.heavy2 !== heavy2) {
5887
6303
  reallocations += 1;
5888
6304
  }
5889
6305
  const next = {
@@ -5892,6 +6308,7 @@ function createPyramidStore(context) {
5892
6308
  chain,
5893
6309
  body,
5894
6310
  heavy,
6311
+ heavy2,
5895
6312
  stats,
5896
6313
  sizeEpoch,
5897
6314
  builtEpoch,
@@ -5900,7 +6317,8 @@ function createPyramidStore(context) {
5900
6317
  sourceWidth: density.sourceWidth,
5901
6318
  sourceHeight: density.sourceHeight,
5902
6319
  bodySigmaCss,
5903
- heavySigmaCss
6320
+ heavySigmaCss,
6321
+ heavy2SigmaCss
5904
6322
  };
5905
6323
  resources.set(sourceId, next);
5906
6324
  return next;
@@ -5984,7 +6402,7 @@ function createPyramidStore(context) {
5984
6402
  function runSeparableBlur(encoder, sourceId, kind, plan, chain, target, level, residualSigmaTexels) {
5985
6403
  const pipeline = chainPipeline("fs_blur");
5986
6404
  const size = plan.levels[level] ?? plan.levels[0];
5987
- const scratchKey = kind === "body" ? poolKey.backdropBodyScratch(sourceId) : poolKey.backdropHeavyScratch(sourceId);
6405
+ const scratchKey = kind === "body" ? poolKey.backdropBodyScratch(sourceId) : kind === "heavy" ? poolKey.backdropHeavyScratch(sourceId) : poolKey.backdropHeavy2Scratch(sourceId);
5988
6406
  const scratch = pool.acquire(scratchKey, {
5989
6407
  width: size.width,
5990
6408
  height: size.height,
@@ -6112,13 +6530,17 @@ function createPyramidStore(context) {
6112
6530
  const bodySigmaTexels = request.bodySigmaCss * texelsPerCss * planScale;
6113
6531
  const bodyPlan = bodyBlurPlan(bodySigmaTexels, plan);
6114
6532
  const heavyPlan = request.heavySigmaCss > 0 ? heavyTapPlan(request.heavySigmaCss * texelsPerCss * planScale, plan) : void 0;
6115
- const target = allocate(request.sourceId, plan, bodyPlan.level, frame.sizeEpoch, request.epoch, bodySigmaTexels, { texelsPerCss, sourceWidth: frame.width, sourceHeight: frame.height }, request.bodySigmaCss, heavyPlan?.level, request.heavySigmaCss);
6533
+ const heavy2Plan = request.heavy2SigmaCss > 0 ? heavyTapPlan(request.heavy2SigmaCss * texelsPerCss * planScale, plan) : void 0;
6534
+ const target = allocate(request.sourceId, plan, bodyPlan.level, frame.sizeEpoch, request.epoch, bodySigmaTexels, { texelsPerCss, sourceWidth: frame.width, sourceHeight: frame.height }, request.bodySigmaCss, heavyPlan?.level, request.heavySigmaCss, heavy2Plan?.level, request.heavy2SigmaCss);
6116
6535
  runImport(encoder, request.sourceId, frame, target.chain);
6117
6536
  runChain(encoder, request.sourceId, plan, target.chain);
6118
6537
  runSeparableBlur(encoder, request.sourceId, "body", plan, target.chain, target.body, bodyPlan.level, bodyPlan.residualSigmaTexels);
6119
6538
  if (heavyPlan !== void 0 && target.heavy !== void 0) {
6120
6539
  runSeparableBlur(encoder, request.sourceId, "heavy", plan, target.chain, target.heavy, heavyPlan.level, heavyPlan.residualSigmaTexels);
6121
6540
  }
6541
+ if (heavy2Plan !== void 0 && target.heavy2 !== void 0) {
6542
+ runSeparableBlur(encoder, request.sourceId, "heavy2", plan, target.chain, target.heavy2, heavy2Plan.level, heavy2Plan.residualSigmaTexels);
6543
+ }
6122
6544
  runAnalysis(encoder, request.sourceId, plan, target.chain, target.stats);
6123
6545
  provider.markImported();
6124
6546
  return { status: "built", resources: target };
@@ -6615,7 +7037,8 @@ function createWebGPURenderer(options = {}) {
6615
7037
  const sigmaCss = bodySigmaCssFor(sourceId);
6616
7038
  const sameSigma = Math.abs(sigmaCss - existing.bodySigmaCss) <= 1e-6 * Math.max(1, existing.bodySigmaCss);
6617
7039
  const sameHeavy = sameHeavySigma(heavySigmaCssFor(sourceId), existing.heavySigmaCss);
6618
- if (sameDensity && sameSigma && sameHeavy)
7040
+ const sameHeavy2 = sameHeavySigma(heavySecondSigmaCssFor(sourceId), existing.heavy2SigmaCss);
7041
+ if (sameDensity && sameSigma && sameHeavy && sameHeavy2)
6619
7042
  continue;
6620
7043
  requests.push({
6621
7044
  sourceId,
@@ -6641,6 +7064,11 @@ function createWebGPURenderer(options = {}) {
6641
7064
  return 0;
6642
7065
  return heavyTapSigmaAtScale(material, viewport.devicePixelRatio) / Math.max(viewport.devicePixelRatio, 1e-3);
6643
7066
  };
7067
+ const heavySecondSigmaCssFor = (sourceId) => {
7068
+ if (bodySigmaCssFor(sourceId) <= 0)
7069
+ return 0;
7070
+ return heavySecondTapSigmaAtScale(material, viewport.devicePixelRatio);
7071
+ };
6644
7072
  const groupTintSeed = (input) => input.surfaces.find((surface) => drawsMaterial(surface) && (surface.tint?.strength ?? 0) > 0)?.tint?.color;
6645
7073
  const stateOf = (input, resolution) => {
6646
7074
  const resolved = resolution?.groups.find((group) => group.groupId === input.groupId);
@@ -6710,6 +7138,10 @@ function createWebGPURenderer(options = {}) {
6710
7138
  // The heavy blur's own σ, through the same conversion (W26). At 0 the
6711
7139
  // pyramid allocates nothing and encodes nothing.
6712
7140
  heavySigmaCss: heavySigmaCssFor(request.sourceId),
7141
+ // The second heavy blur's σ (W30 G2). At 0 — the landed material —
7142
+ // the pyramid allocates nothing and encodes nothing, exactly as for
7143
+ // the first one.
7144
+ heavy2SigmaCss: heavySecondSigmaCssFor(request.sourceId),
6713
7145
  viewportCss: [viewport.widthCss, viewport.heightCss],
6714
7146
  ...isUsablePlacement(placement) ? { placement } : {}
6715
7147
  }, provider, encoder);
@@ -6772,10 +7204,12 @@ function createWebGPURenderer(options = {}) {
6772
7204
  const shadow = outerShadowUnderPolicy(policy, material);
6773
7205
  const shadowBackdropLuminance = input.backdropTone === void 0 ? void 0 : input.backdropToneLevel ?? relativeLuminance(input.backdropTone);
6774
7206
  let reachOcclusion = outerShadowThinOcclusion(shadowBackdropLuminance, shadow);
7207
+ let reachSpanPx = 0;
6775
7208
  for (const surface of surfaces) {
6776
7209
  reachOcclusion = Math.max(reachOcclusion, outerShadowOcclusionAt(shadow, shadowBackdropLuminance, surface.spanPx, surface.sizeThickness, material));
7210
+ reachSpanPx = Math.max(reachSpanPx, surface.spanPx);
6777
7211
  }
6778
- const shadowReachPx = outerShadowReachPx(shadow, reachOcclusion);
7212
+ const shadowReachPx = outerShadowReachPx(shadow, reachOcclusion, reachSpanPx);
6779
7213
  const snapped = snapRectToDevicePixels(groupFieldRect(surfaces, union, void 0, shadowReachPx), dpr);
6780
7214
  const rectDevice = clipFieldRectToCanvas(snapped, dpr, viewportDevice);
6781
7215
  if (rectDevice === void 0) {
@@ -6955,6 +7389,53 @@ function createWebGPURenderer(options = {}) {
6955
7389
  // itself is already in the texture, so this pass only chooses which of
6956
7390
  // two textures the deep sample comes from.
6957
7391
  heavyTapEnabled: pyramid?.heavy !== void 0,
7392
+ /*
7393
+ * W30's two operators (claims §5.156 §2 and §3; §5.158), every word of
7394
+ * them 0 on the landed material.
7395
+ *
7396
+ * The shadow's σ law crosses as three leaves rather than one width,
7397
+ * because the shader reads it per pixel from the casting surface's own
7398
+ * span. The scatter's scale conditioning crosses as the material's gain
7399
+ * and reference plus the SOURCE's own statistic — the analysis pass's
7400
+ * edge density, which arrives by readback and is therefore resolved
7401
+ * here, like `bodyChainLod`. `adapt` is the filtered reading, so the
7402
+ * statistic the shader sees is the one the drivers have settled on
7403
+ * rather than a raw frame's.
7404
+ *
7405
+ * Where nothing has been observed the statistic is the material's own
7406
+ * REFERENCE rather than 0 — the same gate `adaptStrength` takes two
7407
+ * fields above, and for the same reason. The shader evaluates
7408
+ * `gain · (statistic − reference)`, so a reference is the value at which
7409
+ * the operator contributes nothing; passing 0 instead is that identity
7410
+ * only while the reference happens to be 0, and the moment §5.159 fits
7411
+ * one every unobserved frame would evaluate `gain · (0 − ref)` at full
7412
+ * magnitude — a group whose backdrop readback has not landed yet would
7413
+ * draw at the operator's full excursion and then step to its resting
7414
+ * value. The reference says "no evidence", which is what an unobserved
7415
+ * source has (W30 G2 review closure, claims §5.158 §8, finding 3).
7416
+ */
7417
+ outerShadowSigmaLaw: [
7418
+ shadow.sigmaSlopePerSpan,
7419
+ shadow.sigmaSpanRefPx,
7420
+ shadow.sigmaThinOffsetPx
7421
+ ],
7422
+ sizeScatterScaleGain: material.sizeScatterScaleGain,
7423
+ sizeScatterScaleRef: material.sizeScatterScaleRef,
7424
+ backdropScaleStatistic: adapt?.observed === true ? adapt.edgeDensity : material.sizeScatterScaleRef,
7425
+ sizeHeavySecondShare: material.sizeHeavySecondShare,
7426
+ heavySecondEnabled: pyramid?.heavy2 !== void 0,
7427
+ // W31's body chroma retention (claims §5.164): a material constant, per
7428
+ // group, with no source-side half — the chromaticity it restores toward
7429
+ // is the blurred backdrop the optics pass already sampled per pixel.
7430
+ //
7431
+ // Under the accessibility fold it stands down (W31 Decision Log 3 (d);
7432
+ // claims §5.164 §8 (b) and §13): an occlusion lift covers more of the
7433
+ // backdrop, so restoring the nominal fraction of its chromaticity gives
7434
+ // back what the preference asked to have covered — measured at `R` 3.05
7435
+ // and 3.17 against a reference of 1. `bodyChromaRetentionUnderPolicy` is
7436
+ // exactly `material.bodyChromaRetention` where no preference is set,
7437
+ // which is why every standard row is bit-identical across this fold.
7438
+ bodyChromaRetention: bodyChromaRetentionUnderPolicy(material.bodyChromaRetention, policy),
6958
7439
  ...pyramid === void 0 && input.unsampledMaterial !== void 0 ? { domMaterial: {
6959
7440
  ...input.unsampledMaterial,
6960
7441
  mode: input.backdropTone === void 0 ? 1 : 2
@@ -7076,7 +7557,8 @@ function createWebGPURenderer(options = {}) {
7076
7557
  backdrop: pyramid === void 0 || policy.glass === "none" ? void 0 : {
7077
7558
  chain: pyramid.chain.createView(),
7078
7559
  body: pyramid.body.createView(),
7079
- heavy: pyramid.heavy?.createView()
7560
+ heavy: pyramid.heavy?.createView(),
7561
+ heavy2: pyramid.heavy2?.createView()
7080
7562
  }
7081
7563
  });
7082
7564
  if (active.highlight !== void 0) {
@@ -7389,6 +7871,6 @@ function createWebGPURenderer(options = {}) {
7389
7871
  };
7390
7872
  }
7391
7873
 
7392
- export { ADAPTIVE_LUMINANCE_HIGH, ADAPTIVE_LUMINANCE_LOW, ADAPTIVE_TINT_DARK, ADAPTIVE_TINT_LIGHT, ANALYSIS_DISPATCH, ANALYSIS_GRID, ANALYSIS_PASS_ID, ANALYSIS_STATS_FLOATS, ANALYSIS_TARGET_EXTENT, ANALYSIS_WORKGROUP, BACKDROP_ALPHA_MODES, BACKDROP_COLOR_SPACES, BACKDROP_KINDS, BACKDROP_PASS_ID, BACKDROP_TONE_HIGH, BACKDROP_TONE_LOW, BACKDROP_TONE_MAX, BACKDROP_TONE_SIZE_BIAS, CANVAS_FORMAT, CHAIN_SIGMA_AT_LEVEL_1, CROSS_CHECK_SHAPE_FLOATS, CROSS_CHECK_WORKGROUP, DEFAULT_MATERIAL_PROFILE, FAMILY_C_CROSS_CHECK, FIELD_PASS_ID, GOVERNOR_LADDER, HIGHLIGHT_PASS_ID, IDENTITY_FIT, IDLE_CHANNELS, INCREASED_OCCLUSION_LIFT, INSTANCE_BYTES, INSTANCE_FLOATS, LENS_AMOUNT_MAX, LENS_AMOUNT_PER_SPAN, LENS_EXTENT_GAIN, LENS_HEIGHT_MAX, LENS_HEIGHT_PER_SPAN, LENS_OVALIZATION, LENS_PROFILE_EXPONENT, LENS_REFRACTION_GAIN, LENS_SIZE_GAIN_MAX, LENS_THICKNESS_REFERENCE, LUMINANCE_WEIGHTS, MATERIAL_OPTICS, MATERIAL_VARIANTS, MAX_CHAIN_LEVELS, MIN_LEVEL_EXTENT, NOMINAL_GOVERNOR, NOMINAL_MATERIAL_POLICY, OPTICS_PASS_ID, OUTER_SHADOW, OUTER_SHADOW_THICK_SPANS, OUTER_SHADOW_THIN_L, OUTER_SHADOW_UNMEASURED_BACKDROP_LUMINANCE, OUTPUT_TEXTURE_FORMAT, PASS_LABEL, REFRACTION_LADDER, REFRACTION_SCALE, RENDERER_ERROR_CODES, RENDERER_PASS_IDS, RendererError, SIZE_OCCLUSION_GAIN, SIZE_SCATTER_FLOOR, SIZE_SCATTER_FLOOR_2X, SIZE_SCATTER_GAIN_FAR_2X, SIZE_SCATTER_GAIN_MAX, SIZE_SCATTER_GAIN_MAX_2X, SIZE_SCATTER_RAMP_REACH_1X_PX, SIZE_SCATTER_RAMP_REACH_2X_PX, SIZE_SCATTER_RAMP_START_FAR_1X, SIZE_SCATTER_RAMP_START_FAR_2X, SIZE_SCATTER_RAMP_START_THICK_1X, SIZE_SCATTER_RAMP_START_THICK_2X, SIZE_SCATTER_RAMP_START_THIN_1X, SIZE_SCATTER_RAMP_START_THIN_2X, SIZE_SCATTER_SPAN_MAX, SIZE_SCATTER_SPAN_MAX_2X, SIZE_SHADOW_GAIN_MAX, SIZE_SPAN_MAX, SIZE_SPAN_MIN, SRGB_ENCODING_EXPONENT, SUPPORTED_APP_TEXTURE_FORMATS, WGSL_ANALYSIS_PASS, WGSL_CROSS_CHECK_PASS, WGSL_DOWNSAMPLE_PASS, WGSL_FIELD_KERNELS, WGSL_FIELD_PASS, WGSL_FIELD_SAMPLE, WGSL_HIGHLIGHT_PASS, WGSL_IMPORT_PASS, WGSL_INSTANCE_STRUCT, WGSL_OPTICS_PASS, WGSL_PRELUDE, WGSL_RSUPN_GRAD, WGSL_RSUP_GRAD, WGSL_SMOOTH_UNION, WORKING_TEXTURE_FORMAT, ZERO_STATS, accessibilityRefractionCap, adaptationStrength, adaptedTintAlpha, adaptedTintColour, adaptiveTint, allShaderSource, alphaNormalisationMode, analysisModule, backdropFit, backdropToneAdaptation, backdropToneResponse, backdropToneSizeBiasUnderPolicy, backdropToneSolveWeight, backdropToneUnderPolicy, bodyBlurPlan, chainModule, clipFieldRectToCanvas, collapsedRimUnderPolicy, coverFit, createAdaptationState, createAppTextureProvider, createCopyProvider, createDeviceHost, createGovernor, createGpuContext, createGradientProvider, createPassRunner, createPipelineCache, createPyramidStore, createRebuildLedger, createStorageSlot, createTexturePool, createTimingCollector, createUniformSlot, createVideoProvider, createWebGPURenderer, crossCheckKernelModule, displayP3ToSrgbLinear, effectiveRefraction, encodeOutput, encodeOutputBytes, fieldModule, fieldPassSource, groupFieldRect, heavyTapSigmaAtScale, highlightModule, importColorMatrix, importModule, importPassSource, isUsablePlacement, lensAmountBasePx, lensDepthPx, lensDirection, lensDisplacementPx, lensExtentPx, lensHeightBasePx, lensMagnitudePx, lensOvalizationAt, lensSizeGain, lensSizeGainFromThickness, linearGradientStops, linearToSrgb, linearToSrgbChannel, occlusionAlphaUnderPolicy, occlusionLiftForPolicy, opticsModule, opticsUnderPolicy, outerShadowAlpha, outerShadowFalloff, outerShadowLiftRise, outerShadowOcclusionAt, outerShadowReachPx, outerShadowThickOcclusion, outerShadowThinOcclusion, outerShadowUnderPolicy, packInstances, pipelineKey, placementFit, planPyramid, poolKey, readbackDue, refractionRank, relativeLuminance, rendererError, resolveSurfaces, rimAlongSideFactor, rimAlongSideField, samePlacement, scatterDeepThickness, scatterFloorAtScale, scatterGainAt, scatterGainAtScale, scatterGainFarAtScale, scatterHeavyShareThickAtScale, scatterRampAreaMean, scatterRampReachDevicePx, scatterRampStart, scatterSharpShare, scatterSpanMaxAtScale, scatterThickness, shadowDepthPx, sizeOcclusionAlpha, sizeOcclusionAlphaAt, sizeOuterShadowOcclusion, sizeOuterShadowOcclusionAt, sizeScatterSigma, sizeScatterSigmaAt, sizeShadowDepth, sizeShadowDepthAt, sizeThickness, sizeThicknessUnderPolicy, sizeToneLevelFar, snapRectToDevicePixels, srgbToLinear, srgbToLinearChannel, statsFromBuffer, supportsTimestamps, texelsPerCssPx, tintShade, tintShadeLayer, tintToneAdaptation, tintedMaterialColour, validateAppTexture, withMaterialOverrides };
7393
- //# sourceMappingURL=dist-VQPRX7BX.js.map
7394
- //# sourceMappingURL=dist-VQPRX7BX.js.map
7874
+ export { ADAPTIVE_LUMINANCE_HIGH, ADAPTIVE_LUMINANCE_LOW, ADAPTIVE_TINT_DARK, ADAPTIVE_TINT_LIGHT, ANALYSIS_DISPATCH, ANALYSIS_GRID, ANALYSIS_PASS_ID, ANALYSIS_STATS_FLOATS, ANALYSIS_TARGET_EXTENT, ANALYSIS_WORKGROUP, BACKDROP_ALPHA_MODES, BACKDROP_COLOR_SPACES, BACKDROP_KINDS, BACKDROP_PASS_ID, BACKDROP_TONE_HIGH, BACKDROP_TONE_LOW, BACKDROP_TONE_MAX, BACKDROP_TONE_SIZE_BIAS, CANVAS_FORMAT, CHAIN_SIGMA_AT_LEVEL_1, CROSS_CHECK_SHAPE_FLOATS, CROSS_CHECK_WORKGROUP, DEFAULT_MATERIAL_PROFILE, FAMILY_C_CROSS_CHECK, FIELD_PASS_ID, GOVERNOR_LADDER, HIGHLIGHT_PASS_ID, IDENTITY_FIT, IDLE_CHANNELS, INCREASED_OCCLUSION_LIFT, INSTANCE_BYTES, INSTANCE_FLOATS, LENS_AMOUNT_MAX, LENS_AMOUNT_PER_SPAN, LENS_EXTENT_GAIN, LENS_HEIGHT_MAX, LENS_HEIGHT_PER_SPAN, LENS_OVALIZATION, LENS_PROFILE_EXPONENT, LENS_REFRACTION_GAIN, LENS_SIZE_GAIN_MAX, LENS_THICKNESS_REFERENCE, LUMINANCE_WEIGHTS, MATERIAL_DIGEST_RULE_VERSION, MATERIAL_IDENTITY_TABLE, MATERIAL_OPTICS, MATERIAL_VARIANTS, MAX_CHAIN_LEVELS, MIN_LEVEL_EXTENT, NOMINAL_GOVERNOR, NOMINAL_MATERIAL_POLICY, OPTICS_PASS_ID, OUTER_SHADOW, OUTER_SHADOW_THICK_SPANS, OUTER_SHADOW_THIN_L, OUTER_SHADOW_UNMEASURED_BACKDROP_LUMINANCE, OUTPUT_TEXTURE_FORMAT, PASS_LABEL, REFRACTION_LADDER, REFRACTION_SCALE, RENDERER_ERROR_CODES, RENDERER_PASS_IDS, RendererError, SIZE_OCCLUSION_GAIN, SIZE_SCATTER_FLOOR, SIZE_SCATTER_FLOOR_2X, SIZE_SCATTER_GAIN_FAR_2X, SIZE_SCATTER_GAIN_MAX, SIZE_SCATTER_GAIN_MAX_2X, SIZE_SCATTER_RAMP_REACH_1X_PX, SIZE_SCATTER_RAMP_REACH_2X_PX, SIZE_SCATTER_RAMP_START_FAR_1X, SIZE_SCATTER_RAMP_START_FAR_2X, SIZE_SCATTER_RAMP_START_THICK_1X, SIZE_SCATTER_RAMP_START_THICK_2X, SIZE_SCATTER_RAMP_START_THIN_1X, SIZE_SCATTER_RAMP_START_THIN_2X, SIZE_SCATTER_SPAN_MAX, SIZE_SCATTER_SPAN_MAX_2X, SIZE_SHADOW_GAIN_MAX, SIZE_SPAN_MAX, SIZE_SPAN_MIN, SRGB_ENCODING_EXPONENT, SUPPORTED_APP_TEXTURE_FORMATS, WGSL_ANALYSIS_PASS, WGSL_CROSS_CHECK_PASS, WGSL_DOWNSAMPLE_PASS, WGSL_FIELD_KERNELS, WGSL_FIELD_PASS, WGSL_FIELD_SAMPLE, WGSL_HIGHLIGHT_PASS, WGSL_IMPORT_PASS, WGSL_INSTANCE_STRUCT, WGSL_OPTICS_PASS, WGSL_PRELUDE, WGSL_RSUPN_GRAD, WGSL_RSUP_GRAD, WGSL_SMOOTH_UNION, WORKING_TEXTURE_FORMAT, ZERO_STATS, accessibilityRefractionCap, adaptationStrength, adaptedTintAlpha, adaptedTintColour, adaptiveTint, allShaderSource, alphaNormalisationMode, analysisModule, backdropFit, backdropToneAdaptation, backdropToneResponse, backdropToneSizeBiasUnderPolicy, backdropToneSolveWeight, backdropToneUnderPolicy, bodyBlurPlan, chainModule, clipFieldRectToCanvas, collapsedRimUnderPolicy, coverFit, createAdaptationState, createAppTextureProvider, createCopyProvider, createDeviceHost, createGovernor, createGpuContext, createGradientProvider, createPassRunner, createPipelineCache, createPyramidStore, createRebuildLedger, createStorageSlot, createTexturePool, createTimingCollector, createUniformSlot, createVideoProvider, createWebGPURenderer, crossCheckKernelModule, displayP3ToSrgbLinear, effectiveRefraction, encodeOutput, encodeOutputBytes, fieldModule, fieldPassSource, groupFieldRect, heavySecondTapSigmaAtScale, heavyTapSigmaAtScale, highlightModule, importColorMatrix, importModule, importPassSource, isUsablePlacement, lensAmountBasePx, lensDepthPx, lensDirection, lensDisplacementPx, lensExtentPx, lensHeightBasePx, lensMagnitudePx, lensOvalizationAt, lensSizeGain, lensSizeGainFromThickness, linearGradientStops, linearToSrgb, linearToSrgbChannel, materialDigestDroppedLeaves, materialDigestInput, materialLeafAt, occlusionAlphaUnderPolicy, occlusionLiftForPolicy, opticsModule, opticsUnderPolicy, outerShadowAlpha, outerShadowFalloff, outerShadowLiftRise, outerShadowOcclusionAt, outerShadowReachPx, outerShadowSigmaPx, outerShadowThickOcclusion, outerShadowThinOcclusion, outerShadowUnderPolicy, packInstances, pipelineKey, placementFit, planPyramid, poolKey, readbackDue, refractionRank, relativeLuminance, rendererError, resolveSurfaces, rimAlongSideFactor, rimAlongSideField, samePlacement, scatterDeepThickness, scatterFloorAtScale, scatterGainAt, scatterGainAtScale, scatterGainFarAtScale, scatterHeavyShareThickAtScale, scatterRampAreaMean, scatterRampReachDevicePx, scatterRampStart, scatterSharpShare, scatterSpanMaxAtScale, scatterThickness, shadowDepthPx, sizeOcclusionAlpha, sizeOcclusionAlphaAt, sizeOuterShadowOcclusion, sizeOuterShadowOcclusionAt, sizeScatterSigma, sizeScatterSigmaAt, sizeShadowDepth, sizeShadowDepthAt, sizeThickness, sizeThicknessUnderPolicy, sizeToneLevelFar, snapRectToDevicePixels, srgbToLinear, srgbToLinearChannel, statsFromBuffer, supportsTimestamps, texelsPerCssPx, tintShade, tintShadeLayer, tintToneAdaptation, tintedMaterialColour, validateAppTexture, withMaterialOverrides };
7875
+ //# sourceMappingURL=dist-N437MSRG.js.map
7876
+ //# sourceMappingURL=dist-N437MSRG.js.map