@flighthq/effects-wgpu 0.4.0-next.1652.39f08fd → 0.4.0-next.1663.d9c5746

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.
@@ -5,5 +5,19 @@ export declare function applyGaussianBlurToWgpu(state: WgpuRenderState, source:
5
5
  blurY?: number;
6
6
  }>): void;
7
7
  export declare const defaultWgpuBlurEffectRunner: WgpuRenderEffectRunner;
8
+ /**
9
+ * Texels per logical pixel in a target, used to keep a blur's width in LOGICAL units.
10
+ *
11
+ * `blurX`/`blurY` are Gaussian standard deviations in logical pixels — CSS `blur(Xpx)` semantics — while
12
+ * the blur pass steps in texels. Those coincide only at 1:1 density, and a `sampleCount: 4` effect target
13
+ * is allocated at 2x per axis, so an unscaled sigma covered half the intended distance and every
14
+ * blur-based Wgpu effect came out narrower than its Gl twin.
15
+ *
16
+ * ★ MEASURED FROM WIDTHS RATHER THAN READ OFF `sampleCount`, because the blur runs on POOL targets: those
17
+ * are allocated from a descriptor that already carries the supersampled width while their own sampleCount
18
+ * stays 1. A sampleCount-based factor returns 1 for exactly the targets that need scaling — the first
19
+ * version of this fix did that and changed nothing measurable.
20
+ */
21
+ export declare function getWgpuRenderTargetTexelScale(targetWidth: number, canvasWidth: number): number;
8
22
  export declare function registerWgpuBlurEffect(state: WgpuRenderState): void;
9
23
  //# sourceMappingURL=wgpuBlurEffect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wgpuBlurEffect.d.ts","sourceRoot":"","sources":["../src/wgpuBlurEffect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AActH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAClC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAC3B,IAAI,CAEN;AAMD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAClC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,OAAO,EAAE,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACpD,IAAI,CAON;AAED,eAAO,MAAM,2BAA2B,EAAE,sBAKzC,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAEnE"}
1
+ {"version":3,"file":"wgpuBlurEffect.d.ts","sourceRoot":"","sources":["../src/wgpuBlurEffect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AActH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAClC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAC3B,IAAI,CAEN;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAClC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAChC,OAAO,EAAE,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACpD,IAAI,CAwBN;AAED,eAAO,MAAM,2BAA2B,EAAE,sBAKzC,CAAC;AAMF;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAG9F;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAEnE"}
@@ -12,13 +12,26 @@ import { registerWgpuRenderEffect } from './wgpuRenderEffectRegistry';
12
12
  export function applyBlurEffectToWgpu(state, source, dest, temp, effect) {
13
13
  applyGaussianBlurToWgpu(state, source, dest, temp, { blurX: effect.blurX, blurY: effect.blurY });
14
14
  }
15
- // Applies a faithful separable Gaussian blur to `source`, writing to `dest`. `blurX`/`blurY` are the
16
- // Gaussian standard deviations in pixels (default 4). Runs two unconditional separable passes,
17
- // source → temp (X) then temp → dest (Y); a zero-radius axis copies through unchanged, so the result
18
- // always lands in `dest` without a separate blit. `temp` is a ping-pong scratch distinct from both.
19
15
  export function applyGaussianBlurToWgpu(state, source, dest, temp, options) {
20
- const sigmaX = options.blurX ?? 4;
21
- const sigmaY = options.blurY ?? 4;
16
+ // SIGMA IS IN LOGICAL PIXELS; THE PASS STEPS IN TEXELS. On a supersampled target those are not the
17
+ // same unit a `sampleCount: 4` effect target is allocated at 2x per axis, so a sigma of 8 spanned
18
+ // only 4 logical pixels and every blur-based effect came out half as wide as its Gl twin. Bloom showed
19
+ // it most plainly: cores and background matched exactly while the halo ran 15.9 luminance short, with
20
+ // ZERO halo pixels brighter on Wgpu — a one-directional deficit, which is what a too-narrow blur looks
21
+ // like, not what a different blur looks like.
22
+ //
23
+ // Measured on effect-bloom: halo mean wgpu-webgl was -15.87 at sampleCount 4 and +1.02 with the same
24
+ // scene at sampleCount 1, where logical and texel units coincide. Scaling sigma by the supersample
25
+ // factor makes the two agree at any sample count, which is the contract the doc comment above states.
26
+ //
27
+ // ★ THE FACTOR IS MEASURED FROM THE TARGET, NOT READ OFF ITS sampleCount. The blur runs on POOL
28
+ // targets, and those are allocated from a descriptor that already carries the supersampled WIDTH while
29
+ // their own sampleCount is 1 — so asking `getWgpuRenderTargetSupersampleScale` gave 1 and the first
30
+ // version of this fix changed nothing at all. Texel density against the canvas is the thing that
31
+ // actually matters here, and it is true of any target however it was obtained.
32
+ const scale = getWgpuRenderTargetTexelScale(source.width, state.canvas.width);
33
+ const sigmaX = (options.blurX ?? 4) * scale;
34
+ const sigmaY = (options.blurY ?? 4) * scale;
22
35
  const radiusX = sigmaX > 0 ? Math.ceil(sigmaX * 3) : 0;
23
36
  const radiusY = sigmaY > 0 ? Math.ceil(sigmaY * 3) : 0;
24
37
  applyWgpuGaussianBlurPass(state, source, temp, sigmaX, radiusX, 1, 0);
@@ -30,6 +43,28 @@ export const defaultWgpuBlurEffectRunner = (ctx, effect) => {
30
43
  applyBlurEffectToWgpu(ctx.state, ctx.source, ctx.dest, temp, effect);
31
44
  releaseWgpuRenderTarget(ctx.pool, temp);
32
45
  };
46
+ // Applies a faithful separable Gaussian blur to `source`, writing to `dest`. `blurX`/`blurY` are the
47
+ // Gaussian standard deviations in pixels (default 4). Runs two unconditional separable passes,
48
+ // source → temp (X) then temp → dest (Y); a zero-radius axis copies through unchanged, so the result
49
+ // always lands in `dest` without a separate blit. `temp` is a ping-pong scratch distinct from both.
50
+ /**
51
+ * Texels per logical pixel in a target, used to keep a blur's width in LOGICAL units.
52
+ *
53
+ * `blurX`/`blurY` are Gaussian standard deviations in logical pixels — CSS `blur(Xpx)` semantics — while
54
+ * the blur pass steps in texels. Those coincide only at 1:1 density, and a `sampleCount: 4` effect target
55
+ * is allocated at 2x per axis, so an unscaled sigma covered half the intended distance and every
56
+ * blur-based Wgpu effect came out narrower than its Gl twin.
57
+ *
58
+ * ★ MEASURED FROM WIDTHS RATHER THAN READ OFF `sampleCount`, because the blur runs on POOL targets: those
59
+ * are allocated from a descriptor that already carries the supersampled width while their own sampleCount
60
+ * stays 1. A sampleCount-based factor returns 1 for exactly the targets that need scaling — the first
61
+ * version of this fix did that and changed nothing measurable.
62
+ */
63
+ export function getWgpuRenderTargetTexelScale(targetWidth, canvasWidth) {
64
+ if (!Number.isFinite(targetWidth) || !Number.isFinite(canvasWidth) || canvasWidth <= 0)
65
+ return 1;
66
+ return Math.max(1, Math.round(targetWidth / canvasWidth));
67
+ }
33
68
  export function registerWgpuBlurEffect(state) {
34
69
  registerWgpuRenderEffect(state, 'BlurEffect', defaultWgpuBlurEffectRunner);
35
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"wgpuBlurEffect.js","sourceRoot":"","sources":["../src/wgpuBlurEffect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wgpuBlurEffect.js","sourceRoot":"","sources":["../src/wgpuBlurEffect.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/effects-wgpu",
3
- "version": "0.4.0-next.1652.39f08fd",
3
+ "version": "0.4.0-next.1663.d9c5746",
4
4
  "author": "Joshua Granick and other contributors",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,14 +38,14 @@
38
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@flighthq/adjustments": "0.4.0-next.1652.39f08fd",
42
- "@flighthq/color": "0.4.0-next.1652.39f08fd",
43
- "@flighthq/effects": "0.4.0-next.1652.39f08fd",
44
- "@flighthq/geometry": "0.4.0-next.1652.39f08fd",
45
- "@flighthq/log": "0.4.0-next.1652.39f08fd",
46
- "@flighthq/render-wgpu": "0.4.0-next.1652.39f08fd",
47
- "@flighthq/registry": "0.4.0-next.1652.39f08fd",
48
- "@flighthq/types": "0.4.0-next.1652.39f08fd"
41
+ "@flighthq/adjustments": "0.4.0-next.1663.d9c5746",
42
+ "@flighthq/color": "0.4.0-next.1663.d9c5746",
43
+ "@flighthq/effects": "0.4.0-next.1663.d9c5746",
44
+ "@flighthq/geometry": "0.4.0-next.1663.d9c5746",
45
+ "@flighthq/log": "0.4.0-next.1663.d9c5746",
46
+ "@flighthq/render-wgpu": "0.4.0-next.1663.d9c5746",
47
+ "@flighthq/registry": "0.4.0-next.1663.d9c5746",
48
+ "@flighthq/types": "0.4.0-next.1663.d9c5746"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "@webgpu/types": "^0.1.70"
@@ -1,6 +1,7 @@
1
1
  import { createWgpuRenderStateForTest, installWgpuMock } from '@flighthq/render-wgpu/contract';
2
2
 
3
3
  import {
4
+ getWgpuRenderTargetTexelScale,
4
5
  applyBlurEffectToWgpu,
5
6
  applyGaussianBlurToWgpu,
6
7
  defaultWgpuBlurEffectRunner,
@@ -28,6 +29,33 @@ describe('defaultWgpuBlurEffectRunner', () => {
28
29
  });
29
30
  });
30
31
 
32
+ describe('getWgpuRenderTargetTexelScale', () => {
33
+ // ★ THE DEFECT: sigma is in logical pixels, the pass steps in texels, and a supersampled effect target
34
+ // holds two texels per logical pixel. Every blur-based Wgpu effect ran half as wide as its Gl twin.
35
+ // Measured on effect-bloom, halo mean wgpu-webgl: -15.87 before, +1.12 after, with 41% of halo pixels
36
+ // darker and 0% brighter beforehand — one-directional, which is a too-narrow blur rather than a
37
+ // different one.
38
+ it('reports 2 for a supersampled target and 1 at native density', () => {
39
+ expect(getWgpuRenderTargetTexelScale(1600, 800)).toBe(2);
40
+ expect(getWgpuRenderTargetTexelScale(800, 800)).toBe(1);
41
+ });
42
+
43
+ // A target SMALLER than the canvas is a downsampled chain, not a negative supersample: scaling sigma
44
+ // below its authored value would narrow the blur further, so the factor floors at 1.
45
+ it('never scales below 1', () => {
46
+ expect(getWgpuRenderTargetTexelScale(400, 800)).toBe(1);
47
+ expect(getWgpuRenderTargetTexelScale(1, 800)).toBe(1);
48
+ });
49
+
50
+ // Degenerate inputs return the identity rather than NaN or Infinity: a bad scale silently multiplies
51
+ // sigma and the failure would surface as a wrong picture rather than an error.
52
+ it('returns 1 for a zero or non-finite width instead of propagating it', () => {
53
+ expect(getWgpuRenderTargetTexelScale(800, 0)).toBe(1);
54
+ expect(getWgpuRenderTargetTexelScale(Number.NaN, 800)).toBe(1);
55
+ expect(getWgpuRenderTargetTexelScale(800, Number.POSITIVE_INFINITY)).toBe(1);
56
+ });
57
+ });
58
+
31
59
  describe('registerWgpuBlurEffect', () => {
32
60
  it('registers the default runner under BlurEffect', async () => {
33
61
  const state = await createWgpuRenderStateForTest();