shaders-oneshot 0.0.81-shared.1
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.
- package/LICENSE +201 -0
- package/NOTICE +5 -0
- package/README.md +7 -0
- package/dist/empty-pixel.d.ts +1 -0
- package/dist/empty-pixel.js +10 -0
- package/dist/empty-pixel.js.map +7 -0
- package/dist/get-shader-color-from-string.d.ts +3 -0
- package/dist/get-shader-color-from-string.js +101 -0
- package/dist/get-shader-color-from-string.js.map +7 -0
- package/dist/get-shader-color-from-string.test.d.ts +1 -0
- package/dist/get-shader-color-from-string.test.js +69 -0
- package/dist/get-shader-noise-texture.d.ts +1 -0
- package/dist/get-shader-noise-texture.js +18 -0
- package/dist/get-shader-noise-texture.js.map +7 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +194 -0
- package/dist/index.js.map +7 -0
- package/dist/shader-color-spaces.d.ts +6 -0
- package/dist/shader-color-spaces.js +96 -0
- package/dist/shader-color-spaces.js.map +7 -0
- package/dist/shader-mount.d.ts +199 -0
- package/dist/shader-mount.js +735 -0
- package/dist/shader-mount.js.map +7 -0
- package/dist/shader-sizing.d.ts +30 -0
- package/dist/shader-sizing.js +38 -0
- package/dist/shader-sizing.js.map +7 -0
- package/dist/shader-utils.d.ts +10 -0
- package/dist/shader-utils.js +131 -0
- package/dist/shader-utils.js.map +7 -0
- package/dist/shaders/color-panels.d.ts +70 -0
- package/dist/shaders/color-panels.js +224 -0
- package/dist/shaders/color-panels.js.map +7 -0
- package/dist/shaders/dithering.d.ts +62 -0
- package/dist/shaders/dithering.js +271 -0
- package/dist/shaders/dithering.js.map +7 -0
- package/dist/shaders/dot-grid.d.ts +65 -0
- package/dist/shaders/dot-grid.js +110 -0
- package/dist/shaders/dot-grid.js.map +7 -0
- package/dist/shaders/dot-orbit.d.ts +57 -0
- package/dist/shaders/dot-orbit.js +123 -0
- package/dist/shaders/dot-orbit.js.map +7 -0
- package/dist/shaders/fluted-glass.d.ts +112 -0
- package/dist/shaders/fluted-glass.js +361 -0
- package/dist/shaders/fluted-glass.js.map +7 -0
- package/dist/shaders/gem-smoke.d.ts +96 -0
- package/dist/shaders/gem-smoke.js +573 -0
- package/dist/shaders/gem-smoke.js.map +7 -0
- package/dist/shaders/god-rays.d.ts +67 -0
- package/dist/shaders/god-rays.js +133 -0
- package/dist/shaders/god-rays.js.map +7 -0
- package/dist/shaders/grain-gradient.d.ts +80 -0
- package/dist/shaders/grain-gradient.js +301 -0
- package/dist/shaders/grain-gradient.js.map +7 -0
- package/dist/shaders/halftone-cmyk.d.ts +104 -0
- package/dist/shaders/halftone-cmyk.js +295 -0
- package/dist/shaders/halftone-cmyk.js.map +7 -0
- package/dist/shaders/halftone-dots.d.ts +80 -0
- package/dist/shaders/halftone-dots.js +321 -0
- package/dist/shaders/halftone-dots.js.map +7 -0
- package/dist/shaders/heatmap.d.ts +66 -0
- package/dist/shaders/heatmap.js +384 -0
- package/dist/shaders/heatmap.js.map +7 -0
- package/dist/shaders/image-dithering.d.ts +64 -0
- package/dist/shaders/image-dithering.js +199 -0
- package/dist/shaders/image-dithering.js.map +7 -0
- package/dist/shaders/lens-distortion.d.ts +97 -0
- package/dist/shaders/lens-distortion.js +276 -0
- package/dist/shaders/lens-distortion.js.map +7 -0
- package/dist/shaders/liquid-metal.d.ts +89 -0
- package/dist/shaders/liquid-metal.js +633 -0
- package/dist/shaders/liquid-metal.js.map +7 -0
- package/dist/shaders/mesh-gradient.d.ts +52 -0
- package/dist/shaders/mesh-gradient.js +131 -0
- package/dist/shaders/mesh-gradient.js.map +7 -0
- package/dist/shaders/metaballs.d.ts +51 -0
- package/dist/shaders/metaballs.js +111 -0
- package/dist/shaders/metaballs.js.map +7 -0
- package/dist/shaders/neuro-noise.d.ts +47 -0
- package/dist/shaders/neuro-noise.js +77 -0
- package/dist/shaders/neuro-noise.js.map +7 -0
- package/dist/shaders/paper-texture.d.ts +109 -0
- package/dist/shaders/paper-texture.js +527 -0
- package/dist/shaders/paper-texture.js.map +7 -0
- package/dist/shaders/perlin-noise.d.ts +52 -0
- package/dist/shaders/perlin-noise.js +167 -0
- package/dist/shaders/perlin-noise.js.map +7 -0
- package/dist/shaders/pulsing-border.d.ts +98 -0
- package/dist/shaders/pulsing-border.js +239 -0
- package/dist/shaders/pulsing-border.js.map +7 -0
- package/dist/shaders/simplex-noise.d.ts +45 -0
- package/dist/shaders/simplex-noise.js +92 -0
- package/dist/shaders/simplex-noise.js.map +7 -0
- package/dist/shaders/smoke-ring.d.ts +61 -0
- package/dist/shaders/smoke-ring.js +133 -0
- package/dist/shaders/smoke-ring.js.map +7 -0
- package/dist/shaders/spiral.d.ts +61 -0
- package/dist/shaders/spiral.js +83 -0
- package/dist/shaders/spiral.js.map +7 -0
- package/dist/shaders/static-mesh-gradient.d.ts +63 -0
- package/dist/shaders/static-mesh-gradient.js +131 -0
- package/dist/shaders/static-mesh-gradient.js.map +7 -0
- package/dist/shaders/static-radial-gradient.d.ts +72 -0
- package/dist/shaders/static-radial-gradient.js +188 -0
- package/dist/shaders/static-radial-gradient.js.map +7 -0
- package/dist/shaders/swirl.d.ts +63 -0
- package/dist/shaders/swirl.js +104 -0
- package/dist/shaders/swirl.js.map +7 -0
- package/dist/shaders/voronoi.d.ts +65 -0
- package/dist/shaders/voronoi.js +139 -0
- package/dist/shaders/voronoi.js.map +7 -0
- package/dist/shaders/warp.d.ts +70 -0
- package/dist/shaders/warp.js +130 -0
- package/dist/shaders/warp.js.map +7 -0
- package/dist/shaders/water.d.ts +59 -0
- package/dist/shaders/water.js +122 -0
- package/dist/shaders/water.js.map +7 -0
- package/dist/shaders/waves.d.ts +53 -0
- package/dist/shaders/waves.js +66 -0
- package/dist/shaders/waves.js.map +7 -0
- package/dist/shared-webgl-renderer.d.ts +63 -0
- package/dist/shared-webgl-renderer.js +262 -0
- package/dist/shared-webgl-renderer.js.map +7 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +7 -0
- package/dist/vertex-shader.d.ts +2 -0
- package/dist/vertex-shader.js +160 -0
- package/dist/vertex-shader.js.map +7 -0
- package/package.json +29 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';
|
|
2
|
+
/**
|
|
3
|
+
* Static line pattern configurable into textures ranging from sharp zigzags to smooth flowing waves.
|
|
4
|
+
*
|
|
5
|
+
* Fragment shader uniforms:
|
|
6
|
+
* - u_colorFront (vec4): Foreground color in RGBA
|
|
7
|
+
* - u_colorBack (vec4): Background color in RGBA
|
|
8
|
+
* - u_shape (float): Line shape, 0 = zigzag, 1 = sine, 2-3 = irregular waves, fractional values morph between shapes, needs amplitude > 0, frequency > 0 (0 to 3)
|
|
9
|
+
* - u_amplitude (float): Wave amplitude, at frequency = 0 it only shifts the lines (0 to 1)
|
|
10
|
+
* - u_frequency (float): Wave frequency, needs amplitude > 0 (0 to 2)
|
|
11
|
+
* - u_spacing (float): Space between every two wavy lines (0 to 2)
|
|
12
|
+
* - u_proportion (float): Blend point between front and back colors, 0.5 = equal distribution (0 to 1)
|
|
13
|
+
* - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)
|
|
14
|
+
*
|
|
15
|
+
* Vertex shader outputs (used in fragment shader):
|
|
16
|
+
* - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied
|
|
17
|
+
*
|
|
18
|
+
* Vertex shader uniforms:
|
|
19
|
+
* - u_resolution (vec2): Canvas resolution in pixels
|
|
20
|
+
* - u_pixelRatio (float): Device pixel ratio
|
|
21
|
+
* - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)
|
|
22
|
+
* - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)
|
|
23
|
+
* - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas
|
|
24
|
+
* - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas
|
|
25
|
+
* - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)
|
|
26
|
+
* - u_scale (float): Overall zoom level of the graphics (0.01 to 4)
|
|
27
|
+
* - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)
|
|
28
|
+
* - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)
|
|
29
|
+
* - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
export declare const wavesFragmentShader: string;
|
|
33
|
+
export interface WavesUniforms extends ShaderSizingUniforms {
|
|
34
|
+
u_colorFront: [number, number, number, number];
|
|
35
|
+
u_colorBack: [number, number, number, number];
|
|
36
|
+
u_shape: number;
|
|
37
|
+
u_frequency: number;
|
|
38
|
+
u_amplitude: number;
|
|
39
|
+
u_spacing: number;
|
|
40
|
+
u_proportion: number;
|
|
41
|
+
u_softness: number;
|
|
42
|
+
}
|
|
43
|
+
export interface WavesParams extends ShaderSizingParams {
|
|
44
|
+
colorFront?: string;
|
|
45
|
+
colorBack?: string;
|
|
46
|
+
rotation?: number;
|
|
47
|
+
shape?: number;
|
|
48
|
+
frequency?: number;
|
|
49
|
+
amplitude?: number;
|
|
50
|
+
spacing?: number;
|
|
51
|
+
proportion?: number;
|
|
52
|
+
softness?: number;
|
|
53
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2
|
+
* Paper Shaders *
|
|
3
|
+
* https://github.com/paper-design/shaders *
|
|
4
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
5
|
+
|
|
6
|
+
import {} from "../shader-sizing.js";
|
|
7
|
+
import { declarePI } from "../shader-utils.js";
|
|
8
|
+
const wavesFragmentShader = `#version 300 es
|
|
9
|
+
precision mediump float;
|
|
10
|
+
|
|
11
|
+
uniform vec4 u_colorFront;
|
|
12
|
+
uniform vec4 u_colorBack;
|
|
13
|
+
uniform float u_shape;
|
|
14
|
+
uniform float u_frequency;
|
|
15
|
+
uniform float u_amplitude;
|
|
16
|
+
uniform float u_spacing;
|
|
17
|
+
uniform float u_proportion;
|
|
18
|
+
uniform float u_softness;
|
|
19
|
+
|
|
20
|
+
in vec2 v_patternUV;
|
|
21
|
+
|
|
22
|
+
out vec4 fragColor;
|
|
23
|
+
|
|
24
|
+
${declarePI}
|
|
25
|
+
|
|
26
|
+
void main() {
|
|
27
|
+
vec2 shape_uv = v_patternUV;
|
|
28
|
+
shape_uv *= 4.;
|
|
29
|
+
|
|
30
|
+
float wave = .5 * cos(shape_uv.x * u_frequency * TWO_PI);
|
|
31
|
+
float zigzag = 2. * abs(fract(shape_uv.x * u_frequency) - .5);
|
|
32
|
+
float irregular = sin(shape_uv.x * .25 * u_frequency * TWO_PI) * cos(shape_uv.x * u_frequency * TWO_PI);
|
|
33
|
+
float irregular2 = .75 * (sin(shape_uv.x * u_frequency * TWO_PI) + .5 * cos(shape_uv.x * .5 * u_frequency * TWO_PI));
|
|
34
|
+
|
|
35
|
+
float offset = mix(zigzag, wave, smoothstep(0., 1., u_shape));
|
|
36
|
+
offset = mix(offset, irregular, smoothstep(1., 2., u_shape));
|
|
37
|
+
offset = mix(offset, irregular2, smoothstep(2., 3., u_shape));
|
|
38
|
+
offset *= 2. * u_amplitude;
|
|
39
|
+
|
|
40
|
+
float spacing = (.001 + u_spacing);
|
|
41
|
+
float shape = .5 + .5 * sin((shape_uv.y + offset) * PI / spacing);
|
|
42
|
+
|
|
43
|
+
float aa = .0001 + fwidth(shape);
|
|
44
|
+
float dc = 1. - clamp(u_proportion, 0., 1.);
|
|
45
|
+
float e0 = dc - u_softness - aa;
|
|
46
|
+
float e1 = dc + u_softness + aa;
|
|
47
|
+
float res = smoothstep(min(e0, e1), max(e0, e1), shape);
|
|
48
|
+
|
|
49
|
+
vec3 fgColor = u_colorFront.rgb * u_colorFront.a;
|
|
50
|
+
float fgOpacity = u_colorFront.a;
|
|
51
|
+
vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
|
|
52
|
+
float bgOpacity = u_colorBack.a;
|
|
53
|
+
|
|
54
|
+
vec3 color = fgColor * res;
|
|
55
|
+
float opacity = fgOpacity * res;
|
|
56
|
+
|
|
57
|
+
color += bgColor * (1. - opacity);
|
|
58
|
+
opacity += bgOpacity * (1. - opacity);
|
|
59
|
+
|
|
60
|
+
fragColor = vec4(color, opacity);
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
export {
|
|
64
|
+
wavesFragmentShader
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=waves.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/shaders/waves.ts"],
|
|
4
|
+
"sourcesContent": ["import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\r\nimport { declarePI } from '../shader-utils.js';\r\n\r\n/**\r\n * Static line pattern configurable into textures ranging from sharp zigzags to smooth flowing waves.\r\n *\r\n * Fragment shader uniforms:\r\n * - u_colorFront (vec4): Foreground color in RGBA\r\n * - u_colorBack (vec4): Background color in RGBA\r\n * - u_shape (float): Line shape, 0 = zigzag, 1 = sine, 2-3 = irregular waves, fractional values morph between shapes, needs amplitude > 0, frequency > 0 (0 to 3)\r\n * - u_amplitude (float): Wave amplitude, at frequency = 0 it only shifts the lines (0 to 1)\r\n * - u_frequency (float): Wave frequency, needs amplitude > 0 (0 to 2)\r\n * - u_spacing (float): Space between every two wavy lines (0 to 2)\r\n * - u_proportion (float): Blend point between front and back colors, 0.5 = equal distribution (0 to 1)\r\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\r\n *\r\n * Vertex shader outputs (used in fragment shader):\r\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied\r\n *\r\n * Vertex shader uniforms:\r\n * - u_resolution (vec2): Canvas resolution in pixels\r\n * - u_pixelRatio (float): Device pixel ratio\r\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\r\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\r\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\r\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\r\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\r\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\r\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\r\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\r\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\r\n *\r\n */\r\n\r\n// language=GLSL\r\nexport const wavesFragmentShader: string = `#version 300 es\r\nprecision mediump float;\r\n\r\nuniform vec4 u_colorFront;\r\nuniform vec4 u_colorBack;\r\nuniform float u_shape;\r\nuniform float u_frequency;\r\nuniform float u_amplitude;\r\nuniform float u_spacing;\r\nuniform float u_proportion;\r\nuniform float u_softness;\r\n\r\nin vec2 v_patternUV;\r\n\r\nout vec4 fragColor;\r\n\r\n${ declarePI }\r\n\r\nvoid main() {\r\n vec2 shape_uv = v_patternUV;\r\n shape_uv *= 4.;\r\n\r\n float wave = .5 * cos(shape_uv.x * u_frequency * TWO_PI);\r\n float zigzag = 2. * abs(fract(shape_uv.x * u_frequency) - .5);\r\n float irregular = sin(shape_uv.x * .25 * u_frequency * TWO_PI) * cos(shape_uv.x * u_frequency * TWO_PI);\r\n float irregular2 = .75 * (sin(shape_uv.x * u_frequency * TWO_PI) + .5 * cos(shape_uv.x * .5 * u_frequency * TWO_PI));\r\n\r\n float offset = mix(zigzag, wave, smoothstep(0., 1., u_shape));\r\n offset = mix(offset, irregular, smoothstep(1., 2., u_shape));\r\n offset = mix(offset, irregular2, smoothstep(2., 3., u_shape));\r\n offset *= 2. * u_amplitude;\r\n\r\n float spacing = (.001 + u_spacing);\r\n float shape = .5 + .5 * sin((shape_uv.y + offset) * PI / spacing);\r\n\r\n float aa = .0001 + fwidth(shape);\r\n float dc = 1. - clamp(u_proportion, 0., 1.);\r\n float e0 = dc - u_softness - aa;\r\n float e1 = dc + u_softness + aa;\r\n float res = smoothstep(min(e0, e1), max(e0, e1), shape);\r\n\r\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\r\n float fgOpacity = u_colorFront.a;\r\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\r\n float bgOpacity = u_colorBack.a;\r\n\r\n vec3 color = fgColor * res;\r\n float opacity = fgOpacity * res;\r\n\r\n color += bgColor * (1. - opacity);\r\n opacity += bgOpacity * (1. - opacity);\r\n\r\n fragColor = vec4(color, opacity);\r\n}\r\n`;\r\n\r\nexport interface WavesUniforms extends ShaderSizingUniforms {\r\n u_colorFront: [number, number, number, number];\r\n u_colorBack: [number, number, number, number];\r\n u_shape: number;\r\n u_frequency: number;\r\n u_amplitude: number;\r\n u_spacing: number;\r\n u_proportion: number;\r\n u_softness: number;\r\n}\r\n\r\nexport interface WavesParams extends ShaderSizingParams {\r\n colorFront?: string;\r\n colorBack?: string;\r\n rotation?: number;\r\n shape?: number;\r\n frequency?: number;\r\n amplitude?: number;\r\n spacing?: number;\r\n proportion?: number;\r\n softness?: number;\r\n}\r\n"],
|
|
5
|
+
"mappings": ";;;;;AAAA,eAAmE;AACnE,SAAS,iBAAiB;AAkCnB,MAAM,sBAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBxC,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single WebGL2 context shared by every ShaderMount running in "shared" mode.
|
|
3
|
+
*
|
|
4
|
+
* Browsers cap the number of live WebGL contexts per page (16 in desktop Chrome). Once a new context would go over
|
|
5
|
+
* the cap, the browser kills whichever context rendered least recently, and it only brings it back after another
|
|
6
|
+
* context has been garbage collected. With one context per shader, pages with many shaders, or apps that mount and
|
|
7
|
+
* unmount them often, end up with shaders that silently turn black.
|
|
8
|
+
*
|
|
9
|
+
* Shared mounts compile their programs into this one context instead. On each animation frame the renderer packs
|
|
10
|
+
* every mount that needs a frame into an atlas on one OffscreenCanvas, draws them all, transfers the result as a
|
|
11
|
+
* single ImageBitmap and lets each mount copy its region into its own 2D canvas.
|
|
12
|
+
*/
|
|
13
|
+
/** What a ShaderMount implements to be drawn by the shared renderer */
|
|
14
|
+
export interface SharedRenderTarget {
|
|
15
|
+
/** Called at the start of a frame: advance time and return the pixel size to draw at, or null to skip this frame */
|
|
16
|
+
prepareSharedFrame(currentTime: number): SharedTargetSize | null;
|
|
17
|
+
/** Draw into the viewport the renderer has set, whose lower left corner is at (x, y) in GL coordinates */
|
|
18
|
+
drawShared(x: number, y: number): void;
|
|
19
|
+
/** Copy this target's region, given with a top left origin, out of the frame into the visible canvas */
|
|
20
|
+
presentShared(frame: ImageBitmap, x: number, y: number, width: number, height: number): void;
|
|
21
|
+
/** Called after every frame the target was scheduled for. Return true to be drawn again on the next frame */
|
|
22
|
+
finishSharedFrame(): boolean;
|
|
23
|
+
/** The shared context was lost and replaced: recreate every GL resource on the new context */
|
|
24
|
+
restoreShared(gl: WebGL2RenderingContext): void;
|
|
25
|
+
}
|
|
26
|
+
export interface SharedTargetSize {
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
}
|
|
30
|
+
export declare class SharedWebGLRenderer {
|
|
31
|
+
gl: WebGL2RenderingContext;
|
|
32
|
+
/** Largest width or height a single target can be drawn at */
|
|
33
|
+
maxSide: number;
|
|
34
|
+
private canvas;
|
|
35
|
+
private targets;
|
|
36
|
+
private scheduled;
|
|
37
|
+
private rafId;
|
|
38
|
+
private idleTimeout;
|
|
39
|
+
private recoveryTimeout;
|
|
40
|
+
private recoveries;
|
|
41
|
+
private isLost;
|
|
42
|
+
private isReleased;
|
|
43
|
+
private peakWidth;
|
|
44
|
+
private peakHeight;
|
|
45
|
+
private framesSinceShrinkCheck;
|
|
46
|
+
/** The shared renderer, created on first use. Null when the browser can't render this way (no WebGL2 OffscreenCanvas) */
|
|
47
|
+
static get(): SharedWebGLRenderer | null;
|
|
48
|
+
private constructor();
|
|
49
|
+
register(target: SharedRenderTarget): void;
|
|
50
|
+
unregister(target: SharedRenderTarget): void;
|
|
51
|
+
/** Draw the target on the next animation frame. Scheduling it several times before then draws it once */
|
|
52
|
+
schedule(target: SharedRenderTarget): void;
|
|
53
|
+
private frame;
|
|
54
|
+
private drawBatch;
|
|
55
|
+
private ensureCanvasSize;
|
|
56
|
+
/** Give memory back once the largest atlas we needed recently is much smaller than the canvas */
|
|
57
|
+
private maybeShrink;
|
|
58
|
+
private handleContextLost;
|
|
59
|
+
private scheduleRecovery;
|
|
60
|
+
private recover;
|
|
61
|
+
/** Nothing has used the context for a while: release it so it stops counting toward the browser's limit */
|
|
62
|
+
private release;
|
|
63
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2
|
+
* Paper Shaders *
|
|
3
|
+
* https://github.com/paper-design/shaders *
|
|
4
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
5
|
+
|
|
6
|
+
const CONTEXT_ATTRIBUTES = {
|
|
7
|
+
alpha: true,
|
|
8
|
+
premultipliedAlpha: true,
|
|
9
|
+
// Every draw is a quad that covers its whole viewport, so multisampling would only cost memory
|
|
10
|
+
antialias: false,
|
|
11
|
+
depth: false,
|
|
12
|
+
stencil: false,
|
|
13
|
+
preserveDrawingBuffer: false
|
|
14
|
+
};
|
|
15
|
+
const MAX_ATLAS_SIDE = 4096;
|
|
16
|
+
const SIZE_STEP = 256;
|
|
17
|
+
const SHRINK_CHECK_FRAMES = 600;
|
|
18
|
+
const IDLE_RELEASE_MS = 1e4;
|
|
19
|
+
const MAX_RECOVERIES = 5;
|
|
20
|
+
const RECOVERY_WINDOW_MS = 6e4;
|
|
21
|
+
const MAX_RECOVERY_ATTEMPTS = 6;
|
|
22
|
+
let instance = null;
|
|
23
|
+
class SharedWebGLRenderer {
|
|
24
|
+
gl;
|
|
25
|
+
/** Largest width or height a single target can be drawn at */
|
|
26
|
+
maxSide;
|
|
27
|
+
canvas;
|
|
28
|
+
targets = /* @__PURE__ */ new Set();
|
|
29
|
+
scheduled = /* @__PURE__ */ new Set();
|
|
30
|
+
rafId = null;
|
|
31
|
+
idleTimeout = null;
|
|
32
|
+
recoveryTimeout = null;
|
|
33
|
+
recoveries = [];
|
|
34
|
+
isLost = false;
|
|
35
|
+
isReleased = false;
|
|
36
|
+
peakWidth = 0;
|
|
37
|
+
peakHeight = 0;
|
|
38
|
+
framesSinceShrinkCheck = 0;
|
|
39
|
+
/** The shared renderer, created on first use. Null when the browser can't render this way (no WebGL2 OffscreenCanvas) */
|
|
40
|
+
static get() {
|
|
41
|
+
if (instance && !instance.isReleased) return instance;
|
|
42
|
+
const created = createContext();
|
|
43
|
+
if (!created) return null;
|
|
44
|
+
instance = new SharedWebGLRenderer(created.canvas, created.gl);
|
|
45
|
+
return instance;
|
|
46
|
+
}
|
|
47
|
+
constructor(canvas, gl) {
|
|
48
|
+
this.canvas = canvas;
|
|
49
|
+
this.gl = gl;
|
|
50
|
+
this.maxSide = getMaxSide(gl);
|
|
51
|
+
canvas.addEventListener("webglcontextlost", this.handleContextLost);
|
|
52
|
+
}
|
|
53
|
+
register(target) {
|
|
54
|
+
this.targets.add(target);
|
|
55
|
+
if (this.idleTimeout !== null) {
|
|
56
|
+
clearTimeout(this.idleTimeout);
|
|
57
|
+
this.idleTimeout = null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
unregister(target) {
|
|
61
|
+
this.targets.delete(target);
|
|
62
|
+
this.scheduled.delete(target);
|
|
63
|
+
if (this.targets.size === 0 && this.idleTimeout === null) {
|
|
64
|
+
this.idleTimeout = setTimeout(this.release, IDLE_RELEASE_MS);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Draw the target on the next animation frame. Scheduling it several times before then draws it once */
|
|
68
|
+
schedule(target) {
|
|
69
|
+
if (!this.targets.has(target)) return;
|
|
70
|
+
this.scheduled.add(target);
|
|
71
|
+
if (this.rafId === null && !this.isLost && !this.isReleased) {
|
|
72
|
+
this.rafId = requestAnimationFrame(this.frame);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
frame = (currentTime) => {
|
|
76
|
+
this.rafId = null;
|
|
77
|
+
if (this.gl.isContextLost()) {
|
|
78
|
+
this.handleContextLost();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const targets = Array.from(this.scheduled);
|
|
82
|
+
this.scheduled.clear();
|
|
83
|
+
const items = [];
|
|
84
|
+
for (const target of targets) {
|
|
85
|
+
const size = target.prepareSharedFrame(currentTime);
|
|
86
|
+
if (size && size.width > 0 && size.height > 0) {
|
|
87
|
+
items.push({
|
|
88
|
+
target,
|
|
89
|
+
width: Math.min(size.width, this.maxSide),
|
|
90
|
+
height: Math.min(size.height, this.maxSide),
|
|
91
|
+
x: 0,
|
|
92
|
+
y: 0
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
items.sort((a, b) => b.height - a.height);
|
|
97
|
+
let start = 0;
|
|
98
|
+
while (start < items.length) {
|
|
99
|
+
const batch = packShelves(items, start, this.maxSide);
|
|
100
|
+
this.drawBatch(items.slice(start, batch.end), batch.width, batch.height);
|
|
101
|
+
start = batch.end;
|
|
102
|
+
}
|
|
103
|
+
if (items.length > 0) {
|
|
104
|
+
this.maybeShrink();
|
|
105
|
+
}
|
|
106
|
+
for (const target of targets) {
|
|
107
|
+
if (target.finishSharedFrame()) {
|
|
108
|
+
this.schedule(target);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
drawBatch(items, width, height) {
|
|
113
|
+
this.ensureCanvasSize(width, height);
|
|
114
|
+
const gl = this.gl;
|
|
115
|
+
const bufferHeight = gl.drawingBufferHeight;
|
|
116
|
+
for (const item of items) {
|
|
117
|
+
const glY = bufferHeight - item.y - item.height;
|
|
118
|
+
gl.viewport(item.x, glY, item.width, item.height);
|
|
119
|
+
item.target.drawShared(item.x, glY);
|
|
120
|
+
}
|
|
121
|
+
const frame = this.canvas.transferToImageBitmap();
|
|
122
|
+
for (const item of items) {
|
|
123
|
+
item.target.presentShared(frame, item.x, item.y, item.width, item.height);
|
|
124
|
+
}
|
|
125
|
+
frame.close();
|
|
126
|
+
}
|
|
127
|
+
ensureCanvasSize(width, height) {
|
|
128
|
+
this.peakWidth = Math.max(this.peakWidth, width);
|
|
129
|
+
this.peakHeight = Math.max(this.peakHeight, height);
|
|
130
|
+
if (this.canvas.width >= width && this.canvas.height >= height) return;
|
|
131
|
+
this.canvas.width = Math.min(this.maxSide, Math.max(this.canvas.width, roundUpToStep(width)));
|
|
132
|
+
this.canvas.height = Math.min(this.maxSide, Math.max(this.canvas.height, roundUpToStep(height)));
|
|
133
|
+
const { drawingBufferWidth, drawingBufferHeight } = this.gl;
|
|
134
|
+
if (drawingBufferWidth < this.canvas.width || drawingBufferHeight < this.canvas.height) {
|
|
135
|
+
this.maxSide = Math.min(drawingBufferWidth, drawingBufferHeight);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Give memory back once the largest atlas we needed recently is much smaller than the canvas */
|
|
139
|
+
maybeShrink() {
|
|
140
|
+
this.framesSinceShrinkCheck++;
|
|
141
|
+
if (this.framesSinceShrinkCheck < SHRINK_CHECK_FRAMES) return;
|
|
142
|
+
const width = roundUpToStep(this.peakWidth);
|
|
143
|
+
const height = roundUpToStep(this.peakHeight);
|
|
144
|
+
if (width * height * 2 < this.canvas.width * this.canvas.height) {
|
|
145
|
+
this.canvas.width = width;
|
|
146
|
+
this.canvas.height = height;
|
|
147
|
+
}
|
|
148
|
+
this.framesSinceShrinkCheck = 0;
|
|
149
|
+
this.peakWidth = 0;
|
|
150
|
+
this.peakHeight = 0;
|
|
151
|
+
}
|
|
152
|
+
handleContextLost = () => {
|
|
153
|
+
if (this.isReleased || this.isLost) return;
|
|
154
|
+
this.isLost = true;
|
|
155
|
+
if (this.rafId !== null) {
|
|
156
|
+
cancelAnimationFrame(this.rafId);
|
|
157
|
+
this.rafId = null;
|
|
158
|
+
}
|
|
159
|
+
this.scheduleRecovery(0);
|
|
160
|
+
};
|
|
161
|
+
scheduleRecovery(attempt) {
|
|
162
|
+
const now = performance.now();
|
|
163
|
+
this.recoveries = this.recoveries.filter((time) => now - time < RECOVERY_WINDOW_MS);
|
|
164
|
+
if (this.recoveries.length >= MAX_RECOVERIES || attempt >= MAX_RECOVERY_ATTEMPTS) {
|
|
165
|
+
console.warn("Paper Shaders: the shared WebGL context keeps getting lost, giving up on restoring it");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const delay = attempt === 0 ? 0 : Math.min(1e3 * 2 ** (attempt - 1), 8e3);
|
|
169
|
+
this.recoveryTimeout = setTimeout(() => {
|
|
170
|
+
this.recoveryTimeout = null;
|
|
171
|
+
requestAnimationFrame(() => this.recover(attempt));
|
|
172
|
+
}, delay);
|
|
173
|
+
}
|
|
174
|
+
recover(attempt) {
|
|
175
|
+
if (this.isReleased) return;
|
|
176
|
+
const created = createContext();
|
|
177
|
+
if (!created) {
|
|
178
|
+
this.scheduleRecovery(attempt + 1);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this.recoveries.push(performance.now());
|
|
182
|
+
this.canvas.removeEventListener("webglcontextlost", this.handleContextLost);
|
|
183
|
+
this.canvas = created.canvas;
|
|
184
|
+
this.gl = created.gl;
|
|
185
|
+
this.maxSide = getMaxSide(created.gl);
|
|
186
|
+
this.canvas.addEventListener("webglcontextlost", this.handleContextLost);
|
|
187
|
+
this.isLost = false;
|
|
188
|
+
for (const target of this.targets) {
|
|
189
|
+
target.restoreShared(this.gl);
|
|
190
|
+
}
|
|
191
|
+
if (this.scheduled.size > 0 && this.rafId === null) {
|
|
192
|
+
this.rafId = requestAnimationFrame(this.frame);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** Nothing has used the context for a while: release it so it stops counting toward the browser's limit */
|
|
196
|
+
release = () => {
|
|
197
|
+
this.idleTimeout = null;
|
|
198
|
+
if (this.targets.size > 0) return;
|
|
199
|
+
this.isReleased = true;
|
|
200
|
+
if (this.rafId !== null) {
|
|
201
|
+
cancelAnimationFrame(this.rafId);
|
|
202
|
+
this.rafId = null;
|
|
203
|
+
}
|
|
204
|
+
if (this.recoveryTimeout !== null) {
|
|
205
|
+
clearTimeout(this.recoveryTimeout);
|
|
206
|
+
this.recoveryTimeout = null;
|
|
207
|
+
}
|
|
208
|
+
this.canvas.removeEventListener("webglcontextlost", this.handleContextLost);
|
|
209
|
+
this.gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
210
|
+
if (instance === this) {
|
|
211
|
+
instance = null;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function createContext() {
|
|
216
|
+
if (typeof OffscreenCanvas === "undefined" || typeof OffscreenCanvas.prototype.transferToImageBitmap !== "function") {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
const canvas = new OffscreenCanvas(SIZE_STEP, SIZE_STEP);
|
|
220
|
+
const gl = canvas.getContext("webgl2", CONTEXT_ATTRIBUTES);
|
|
221
|
+
if (!gl) return null;
|
|
222
|
+
return { canvas, gl };
|
|
223
|
+
}
|
|
224
|
+
function getMaxSide(gl) {
|
|
225
|
+
const viewportDims = gl.getParameter(gl.MAX_VIEWPORT_DIMS);
|
|
226
|
+
return Math.min(
|
|
227
|
+
MAX_ATLAS_SIDE,
|
|
228
|
+
gl.getParameter(gl.MAX_TEXTURE_SIZE) ?? MAX_ATLAS_SIDE,
|
|
229
|
+
gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) ?? MAX_ATLAS_SIDE,
|
|
230
|
+
viewportDims?.[0] ?? MAX_ATLAS_SIDE,
|
|
231
|
+
viewportDims?.[1] ?? MAX_ATLAS_SIDE
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
function roundUpToStep(size) {
|
|
235
|
+
return Math.max(SIZE_STEP, Math.ceil(size / SIZE_STEP) * SIZE_STEP);
|
|
236
|
+
}
|
|
237
|
+
function packShelves(items, start, maxSide) {
|
|
238
|
+
let x = 0;
|
|
239
|
+
let y = 0;
|
|
240
|
+
let shelfHeight = 0;
|
|
241
|
+
let width = 0;
|
|
242
|
+
let end = start;
|
|
243
|
+
for (; end < items.length; end++) {
|
|
244
|
+
const item = items[end];
|
|
245
|
+
if (x > 0 && x + item.width > maxSide) {
|
|
246
|
+
y += shelfHeight;
|
|
247
|
+
x = 0;
|
|
248
|
+
shelfHeight = 0;
|
|
249
|
+
}
|
|
250
|
+
if (end > start && y + item.height > maxSide) break;
|
|
251
|
+
item.x = x;
|
|
252
|
+
item.y = y;
|
|
253
|
+
x += item.width;
|
|
254
|
+
shelfHeight = Math.max(shelfHeight, item.height);
|
|
255
|
+
width = Math.max(width, x);
|
|
256
|
+
}
|
|
257
|
+
return { end, width, height: y + shelfHeight };
|
|
258
|
+
}
|
|
259
|
+
export {
|
|
260
|
+
SharedWebGLRenderer
|
|
261
|
+
};
|
|
262
|
+
//# sourceMappingURL=shared-webgl-renderer.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/shared-webgl-renderer.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * A single WebGL2 context shared by every ShaderMount running in \"shared\" mode.\n *\n * Browsers cap the number of live WebGL contexts per page (16 in desktop Chrome). Once a new context would go over\n * the cap, the browser kills whichever context rendered least recently, and it only brings it back after another\n * context has been garbage collected. With one context per shader, pages with many shaders, or apps that mount and\n * unmount them often, end up with shaders that silently turn black.\n *\n * Shared mounts compile their programs into this one context instead. On each animation frame the renderer packs\n * every mount that needs a frame into an atlas on one OffscreenCanvas, draws them all, transfers the result as a\n * single ImageBitmap and lets each mount copy its region into its own 2D canvas.\n */\n\n/** What a ShaderMount implements to be drawn by the shared renderer */\nexport interface SharedRenderTarget {\n /** Called at the start of a frame: advance time and return the pixel size to draw at, or null to skip this frame */\n prepareSharedFrame(currentTime: number): SharedTargetSize | null;\n /** Draw into the viewport the renderer has set, whose lower left corner is at (x, y) in GL coordinates */\n drawShared(x: number, y: number): void;\n /** Copy this target's region, given with a top left origin, out of the frame into the visible canvas */\n presentShared(frame: ImageBitmap, x: number, y: number, width: number, height: number): void;\n /** Called after every frame the target was scheduled for. Return true to be drawn again on the next frame */\n finishSharedFrame(): boolean;\n /** The shared context was lost and replaced: recreate every GL resource on the new context */\n restoreShared(gl: WebGL2RenderingContext): void;\n}\n\nexport interface SharedTargetSize {\n width: number;\n height: number;\n}\n\ninterface AtlasItem {\n target: SharedRenderTarget;\n width: number;\n height: number;\n x: number;\n y: number;\n}\n\nconst CONTEXT_ATTRIBUTES: WebGLContextAttributes = {\n alpha: true,\n premultipliedAlpha: true,\n // Every draw is a quad that covers its whole viewport, so multisampling would only cost memory\n antialias: false,\n depth: false,\n stencil: false,\n preserveDrawingBuffer: false,\n};\n\n/** Largest atlas side we pack into, keeps each transferred frame at 64MB or less */\nconst MAX_ATLAS_SIDE = 4096;\n/** Canvas sizes are rounded up to this step so small size changes don't reallocate the drawing buffer */\nconst SIZE_STEP = 256;\n/** How many frames to watch the needed size for before shrinking an oversized canvas */\nconst SHRINK_CHECK_FRAMES = 600;\n/** How long to keep the context alive with no mounts, e.g. while an app swaps pages */\nconst IDLE_RELEASE_MS = 10_000;\n/** At most this many context replacements per window, so we never fight other page elements for contexts forever */\nconst MAX_RECOVERIES = 5;\nconst RECOVERY_WINDOW_MS = 60_000;\nconst MAX_RECOVERY_ATTEMPTS = 6;\n\nlet instance: SharedWebGLRenderer | null = null;\n\nexport class SharedWebGLRenderer {\n public gl: WebGL2RenderingContext;\n /** Largest width or height a single target can be drawn at */\n public maxSide: number;\n private canvas: OffscreenCanvas;\n private targets: Set<SharedRenderTarget> = new Set();\n private scheduled: Set<SharedRenderTarget> = new Set();\n private rafId: number | null = null;\n private idleTimeout: ReturnType<typeof setTimeout> | null = null;\n private recoveryTimeout: ReturnType<typeof setTimeout> | null = null;\n private recoveries: number[] = [];\n private isLost = false;\n private isReleased = false;\n private peakWidth = 0;\n private peakHeight = 0;\n private framesSinceShrinkCheck = 0;\n\n /** The shared renderer, created on first use. Null when the browser can't render this way (no WebGL2 OffscreenCanvas) */\n static get(): SharedWebGLRenderer | null {\n if (instance && !instance.isReleased) return instance;\n const created = createContext();\n if (!created) return null;\n instance = new SharedWebGLRenderer(created.canvas, created.gl);\n return instance;\n }\n\n private constructor(canvas: OffscreenCanvas, gl: WebGL2RenderingContext) {\n this.canvas = canvas;\n this.gl = gl;\n this.maxSide = getMaxSide(gl);\n canvas.addEventListener('webglcontextlost', this.handleContextLost);\n }\n\n public register(target: SharedRenderTarget): void {\n this.targets.add(target);\n if (this.idleTimeout !== null) {\n clearTimeout(this.idleTimeout);\n this.idleTimeout = null;\n }\n }\n\n public unregister(target: SharedRenderTarget): void {\n this.targets.delete(target);\n this.scheduled.delete(target);\n if (this.targets.size === 0 && this.idleTimeout === null) {\n this.idleTimeout = setTimeout(this.release, IDLE_RELEASE_MS);\n }\n }\n\n /** Draw the target on the next animation frame. Scheduling it several times before then draws it once */\n public schedule(target: SharedRenderTarget): void {\n if (!this.targets.has(target)) return;\n this.scheduled.add(target);\n if (this.rafId === null && !this.isLost && !this.isReleased) {\n this.rafId = requestAnimationFrame(this.frame);\n }\n }\n\n private frame = (currentTime: number) => {\n this.rafId = null;\n\n if (this.gl.isContextLost()) {\n this.handleContextLost();\n return;\n }\n\n const targets = Array.from(this.scheduled);\n this.scheduled.clear();\n\n const items: AtlasItem[] = [];\n for (const target of targets) {\n const size = target.prepareSharedFrame(currentTime);\n if (size && size.width > 0 && size.height > 0) {\n items.push({\n target,\n width: Math.min(size.width, this.maxSide),\n height: Math.min(size.height, this.maxSide),\n x: 0,\n y: 0,\n });\n }\n }\n\n // Tallest first keeps the shelves tight\n items.sort((a, b) => b.height - a.height);\n let start = 0;\n while (start < items.length) {\n const batch = packShelves(items, start, this.maxSide);\n this.drawBatch(items.slice(start, batch.end), batch.width, batch.height);\n start = batch.end;\n }\n\n if (items.length > 0) {\n this.maybeShrink();\n }\n\n for (const target of targets) {\n if (target.finishSharedFrame()) {\n this.schedule(target);\n }\n }\n };\n\n private drawBatch(items: AtlasItem[], width: number, height: number) {\n this.ensureCanvasSize(width, height);\n\n const gl = this.gl;\n const bufferHeight = gl.drawingBufferHeight;\n for (const item of items) {\n const glY = bufferHeight - item.y - item.height;\n gl.viewport(item.x, glY, item.width, item.height);\n item.target.drawShared(item.x, glY);\n }\n\n // Hands the drawing buffer over without copying it, the canvas starts the next batch with a fresh one\n const frame = this.canvas.transferToImageBitmap();\n for (const item of items) {\n item.target.presentShared(frame, item.x, item.y, item.width, item.height);\n }\n frame.close();\n }\n\n private ensureCanvasSize(width: number, height: number) {\n this.peakWidth = Math.max(this.peakWidth, width);\n this.peakHeight = Math.max(this.peakHeight, height);\n\n if (this.canvas.width >= width && this.canvas.height >= height) return;\n\n this.canvas.width = Math.min(this.maxSide, Math.max(this.canvas.width, roundUpToStep(width)));\n this.canvas.height = Math.min(this.maxSide, Math.max(this.canvas.height, roundUpToStep(height)));\n\n // Browsers may allocate a smaller drawing buffer than asked for, never pack past what we actually got\n const { drawingBufferWidth, drawingBufferHeight } = this.gl;\n if (drawingBufferWidth < this.canvas.width || drawingBufferHeight < this.canvas.height) {\n this.maxSide = Math.min(drawingBufferWidth, drawingBufferHeight);\n }\n }\n\n /** Give memory back once the largest atlas we needed recently is much smaller than the canvas */\n private maybeShrink() {\n this.framesSinceShrinkCheck++;\n if (this.framesSinceShrinkCheck < SHRINK_CHECK_FRAMES) return;\n\n const width = roundUpToStep(this.peakWidth);\n const height = roundUpToStep(this.peakHeight);\n if (width * height * 2 < this.canvas.width * this.canvas.height) {\n this.canvas.width = width;\n this.canvas.height = height;\n }\n\n this.framesSinceShrinkCheck = 0;\n this.peakWidth = 0;\n this.peakHeight = 0;\n }\n\n private handleContextLost = () => {\n if (this.isReleased || this.isLost) return;\n this.isLost = true;\n\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // We don't wait for 'webglcontextrestored': browsers restore an evicted context only once another context has\n // been garbage collected, which can take a long time or never happen. The mounts keep showing their last frame\n // in their 2D canvases while we replace the context with a new one.\n this.scheduleRecovery(0);\n };\n\n private scheduleRecovery(attempt: number) {\n const now = performance.now();\n this.recoveries = this.recoveries.filter((time) => now - time < RECOVERY_WINDOW_MS);\n if (this.recoveries.length >= MAX_RECOVERIES || attempt >= MAX_RECOVERY_ATTEMPTS) {\n console.warn('Paper Shaders: the shared WebGL context keeps getting lost, giving up on restoring it');\n return;\n }\n\n const delay = attempt === 0 ? 0 : Math.min(1000 * 2 ** (attempt - 1), 8000);\n // The animation frame also holds recovery off while the tab is hidden, when nothing would be drawn anyway\n this.recoveryTimeout = setTimeout(() => {\n this.recoveryTimeout = null;\n requestAnimationFrame(() => this.recover(attempt));\n }, delay);\n }\n\n private recover(attempt: number) {\n if (this.isReleased) return;\n\n const created = createContext();\n if (!created) {\n this.scheduleRecovery(attempt + 1);\n return;\n }\n\n this.recoveries.push(performance.now());\n this.canvas.removeEventListener('webglcontextlost', this.handleContextLost);\n this.canvas = created.canvas;\n this.gl = created.gl;\n this.maxSide = getMaxSide(created.gl);\n this.canvas.addEventListener('webglcontextlost', this.handleContextLost);\n this.isLost = false;\n\n for (const target of this.targets) {\n target.restoreShared(this.gl);\n }\n if (this.scheduled.size > 0 && this.rafId === null) {\n this.rafId = requestAnimationFrame(this.frame);\n }\n }\n\n /** Nothing has used the context for a while: release it so it stops counting toward the browser's limit */\n private release = () => {\n this.idleTimeout = null;\n if (this.targets.size > 0) return;\n\n this.isReleased = true;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n if (this.recoveryTimeout !== null) {\n clearTimeout(this.recoveryTimeout);\n this.recoveryTimeout = null;\n }\n\n this.canvas.removeEventListener('webglcontextlost', this.handleContextLost);\n this.gl.getExtension('WEBGL_lose_context')?.loseContext();\n\n if (instance === this) {\n instance = null;\n }\n };\n}\n\nfunction createContext(): { canvas: OffscreenCanvas; gl: WebGL2RenderingContext } | null {\n if (typeof OffscreenCanvas === 'undefined' || typeof OffscreenCanvas.prototype.transferToImageBitmap !== 'function') {\n return null;\n }\n\n const canvas = new OffscreenCanvas(SIZE_STEP, SIZE_STEP);\n const gl = canvas.getContext('webgl2', CONTEXT_ATTRIBUTES);\n if (!gl) return null;\n\n return { canvas, gl };\n}\n\nfunction getMaxSide(gl: WebGL2RenderingContext): number {\n const viewportDims = gl.getParameter(gl.MAX_VIEWPORT_DIMS) as Int32Array | null;\n return Math.min(\n MAX_ATLAS_SIDE,\n gl.getParameter(gl.MAX_TEXTURE_SIZE) ?? MAX_ATLAS_SIDE,\n gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) ?? MAX_ATLAS_SIDE,\n viewportDims?.[0] ?? MAX_ATLAS_SIDE,\n viewportDims?.[1] ?? MAX_ATLAS_SIDE\n );\n}\n\nfunction roundUpToStep(size: number): number {\n return Math.max(SIZE_STEP, Math.ceil(size / SIZE_STEP) * SIZE_STEP);\n}\n\n/**\n * Place items left to right on shelves, starting a new shelf when one is full, until the atlas is full.\n * Writes each item's x/y (top left origin) and returns where the batch ended and the atlas size it needs.\n */\nfunction packShelves(\n items: AtlasItem[],\n start: number,\n maxSide: number\n): { end: number; width: number; height: number } {\n let x = 0;\n let y = 0;\n let shelfHeight = 0;\n let width = 0;\n let end = start;\n\n for (; end < items.length; end++) {\n const item = items[end]!;\n if (x > 0 && x + item.width > maxSide) {\n y += shelfHeight;\n x = 0;\n shelfHeight = 0;\n }\n // Every batch takes at least one item, sizes are already clamped to maxSide so it always fits\n if (end > start && y + item.height > maxSide) break;\n\n item.x = x;\n item.y = y;\n x += item.width;\n shelfHeight = Math.max(shelfHeight, item.height);\n width = Math.max(width, x);\n }\n\n return { end, width, height: y + shelfHeight };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Vertex shader for the shader mount */
|
|
2
|
+
export declare const vertexShaderSource = "#version 300 es\nprecision mediump float;\n\nlayout(location = 0) in vec4 a_position;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_imageAspectRatio;\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_worldWidth;\nuniform float u_worldHeight;\nuniform float u_fit;\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;\n\nout vec2 v_objectUV;\nout vec2 v_objectBoxSize;\nout vec2 v_responsiveUV;\nout vec2 v_responsiveBoxGivenSize;\nout vec2 v_patternUV;\nout vec2 v_patternBoxSize;\nout vec2 v_imageUV;\n\nvec3 getBoxSize(float boxRatio, vec2 givenBoxSize) {\n vec2 box = vec2(0.);\n // fit = none\n box.x = boxRatio * min(givenBoxSize.x / boxRatio, givenBoxSize.y);\n float noFitBoxWidth = box.x;\n if (u_fit == 1.) { // fit = contain\n box.x = boxRatio * min(u_resolution.x / boxRatio, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n box.x = boxRatio * max(u_resolution.x / boxRatio, u_resolution.y);\n }\n box.y = box.x / boxRatio;\n return vec3(box, noFitBoxWidth);\n}\n\nvoid main() {\n gl_Position = a_position;\n\n vec2 uv = gl_Position.xy * .5;\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);\n givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;\n float r = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n\n // ===================================================\n\n float fixedRatio = 1.;\n vec2 fixedRatioBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n\n v_objectBoxSize = getBoxSize(fixedRatio, fixedRatioBoxGivenSize).xy;\n vec2 objectWorldScale = u_resolution.xy / v_objectBoxSize;\n\n v_objectUV = uv;\n v_objectUV *= objectWorldScale;\n v_objectUV += boxOrigin * (objectWorldScale - 1.);\n v_objectUV += graphicOffset;\n v_objectUV /= u_scale;\n v_objectUV = graphicRotation * v_objectUV;\n\n // ===================================================\n\n v_responsiveBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n float responsiveRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 responsiveBoxSize = getBoxSize(responsiveRatio, v_responsiveBoxGivenSize).xy;\n vec2 responsiveBoxScale = u_resolution.xy / responsiveBoxSize;\n\n #ifdef ADD_HELPERS\n v_responsiveHelperBox = uv;\n v_responsiveHelperBox *= responsiveBoxScale;\n v_responsiveHelperBox += boxOrigin * (responsiveBoxScale - 1.);\n #endif\n\n v_responsiveUV = uv;\n v_responsiveUV *= responsiveBoxScale;\n v_responsiveUV += boxOrigin * (responsiveBoxScale - 1.);\n v_responsiveUV += graphicOffset;\n v_responsiveUV /= u_scale;\n v_responsiveUV.x *= responsiveRatio;\n v_responsiveUV = graphicRotation * v_responsiveUV;\n v_responsiveUV.x /= responsiveRatio;\n\n // ===================================================\n\n float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;\n vec2 patternBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y;\n\n vec3 boxSizeData = getBoxSize(patternBoxRatio, patternBoxGivenSize);\n v_patternBoxSize = boxSizeData.xy;\n float patternBoxNoFitBoxWidth = boxSizeData.z;\n vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize;\n\n v_patternUV = uv;\n v_patternUV += graphicOffset / patternBoxScale;\n v_patternUV += boxOrigin;\n v_patternUV -= boxOrigin / patternBoxScale;\n v_patternUV *= u_resolution.xy;\n v_patternUV /= u_pixelRatio;\n if (u_fit > 0.) {\n v_patternUV *= (patternBoxNoFitBoxWidth / v_patternBoxSize.x);\n }\n v_patternUV /= u_scale;\n v_patternUV = graphicRotation * v_patternUV;\n v_patternUV += boxOrigin / patternBoxScale;\n v_patternUV -= boxOrigin;\n // x100 is a default multiplier between vertex and fragmant shaders\n // we use it to avoid UV presision issues\n v_patternUV *= .01;\n\n // ===================================================\n\n vec2 imageBoxSize;\n if (u_fit == 1.) { // contain\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else if (u_fit == 2.) { // cover\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else {\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\n }\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\n\n v_imageUV = uv;\n v_imageUV *= imageBoxScale;\n v_imageUV += boxOrigin * (imageBoxScale - 1.);\n v_imageUV += graphicOffset;\n v_imageUV /= u_scale;\n v_imageUV.x *= u_imageAspectRatio;\n v_imageUV = graphicRotation * v_imageUV;\n v_imageUV.x /= u_imageAspectRatio;\n\n v_imageUV += .5;\n v_imageUV.y = 1. - v_imageUV.y;\n}";
|