@frapx/core 0.3.0 → 0.5.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.
- package/CHANGELOG.md +27 -0
- package/dist/index.d.ts +191 -5
- package/dist/index.js +539 -93
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19,13 +19,150 @@ var UnsupportedError = class extends ShaderError {
|
|
|
19
19
|
var TargetNotFoundError = class extends ShaderError {
|
|
20
20
|
};
|
|
21
21
|
var ShaderCompileError = class extends ShaderError {
|
|
22
|
+
constructor(message, details = {}) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.stage = details.stage ?? null;
|
|
25
|
+
this.lines = details.lines ?? [];
|
|
26
|
+
}
|
|
22
27
|
};
|
|
23
28
|
var TextureLoadError = class extends ShaderError {
|
|
24
29
|
};
|
|
25
30
|
var DestroyedError = class extends ShaderError {
|
|
26
31
|
};
|
|
27
32
|
|
|
33
|
+
// src/internal/shaderLog.ts
|
|
34
|
+
var ANGLE_STYLE = /^\s*ERROR:\s*\d+:(\d+):\s*(.*)$/;
|
|
35
|
+
var NVIDIA_STYLE = /^\s*\d+\((\d+)\)\s*:\s*error\s*\w*\s*:\s*(.*)$/i;
|
|
36
|
+
var NOISE = /compilation terminated|^\s*''\s*:\s*$/i;
|
|
37
|
+
var CONTEXT_LINES = 2;
|
|
38
|
+
var parseShaderLog = (log, source) => {
|
|
39
|
+
const sourceLines = source.split(/\r?\n/);
|
|
40
|
+
const lineAt = (line) => sourceLines[line - 1] ?? "";
|
|
41
|
+
const results = [];
|
|
42
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43
|
+
for (const entry of log.split(/\r?\n/)) {
|
|
44
|
+
const match = ANGLE_STYLE.exec(entry) ?? NVIDIA_STYLE.exec(entry);
|
|
45
|
+
if (!match) continue;
|
|
46
|
+
const line = Number(match[1]);
|
|
47
|
+
const message = match[2].trim();
|
|
48
|
+
if (!message || NOISE.test(message)) continue;
|
|
49
|
+
const key = `${line}:${message}`;
|
|
50
|
+
if (seen.has(key)) continue;
|
|
51
|
+
seen.add(key);
|
|
52
|
+
const context = [];
|
|
53
|
+
const first = Math.max(1, line - CONTEXT_LINES);
|
|
54
|
+
const last = Math.min(sourceLines.length, line + CONTEXT_LINES);
|
|
55
|
+
for (let current = first; current <= last; current++) {
|
|
56
|
+
context.push({ line: current, source: lineAt(current) });
|
|
57
|
+
}
|
|
58
|
+
results.push({ line, message, source: lineAt(line), context });
|
|
59
|
+
}
|
|
60
|
+
return results;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/internal/webgl.ts
|
|
64
|
+
var defaultVertexShader = `
|
|
65
|
+
attribute vec2 a_position;
|
|
66
|
+
varying vec2 v_uv;
|
|
67
|
+
|
|
68
|
+
void main() {
|
|
69
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
70
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
var defaultVertexShader300 = `#version 300 es
|
|
74
|
+
in vec2 a_position;
|
|
75
|
+
out vec2 v_uv;
|
|
76
|
+
|
|
77
|
+
void main() {
|
|
78
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
79
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
var isGLSL300 = (src) => /^\s*#version\s+300\s+es\b/.test(src);
|
|
83
|
+
var isWebGL2Context = (gl) => typeof gl.createVertexArray === "function";
|
|
84
|
+
var createProgram = (gl, vertexSource, fragmentSource) => {
|
|
85
|
+
const vertex = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
86
|
+
let fragment;
|
|
87
|
+
try {
|
|
88
|
+
fragment = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
gl.deleteShader(vertex);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
const program = gl.createProgram();
|
|
94
|
+
if (!program) {
|
|
95
|
+
throw new ShaderCompileError("Failed to create WebGL program.");
|
|
96
|
+
}
|
|
97
|
+
gl.attachShader(program, vertex);
|
|
98
|
+
gl.attachShader(program, fragment);
|
|
99
|
+
gl.linkProgram(program);
|
|
100
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
101
|
+
const log = gl.getProgramInfoLog(program) ?? "Unknown program link error.";
|
|
102
|
+
gl.deleteProgram(program);
|
|
103
|
+
gl.deleteShader(vertex);
|
|
104
|
+
gl.deleteShader(fragment);
|
|
105
|
+
throw new ShaderCompileError(log, { stage: "link" });
|
|
106
|
+
}
|
|
107
|
+
gl.deleteShader(vertex);
|
|
108
|
+
gl.deleteShader(fragment);
|
|
109
|
+
return program;
|
|
110
|
+
};
|
|
111
|
+
var createFullscreenBuffer = (gl, program) => {
|
|
112
|
+
const buffer = gl.createBuffer();
|
|
113
|
+
if (!buffer) {
|
|
114
|
+
throw new ShaderCompileError("Failed to create fullscreen buffer.");
|
|
115
|
+
}
|
|
116
|
+
const location = gl.getAttribLocation(program, "a_position");
|
|
117
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
118
|
+
gl.bufferData(
|
|
119
|
+
gl.ARRAY_BUFFER,
|
|
120
|
+
new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),
|
|
121
|
+
gl.STATIC_DRAW
|
|
122
|
+
);
|
|
123
|
+
if (location >= 0) {
|
|
124
|
+
gl.enableVertexAttribArray(location);
|
|
125
|
+
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
|
|
126
|
+
}
|
|
127
|
+
return buffer;
|
|
128
|
+
};
|
|
129
|
+
var compileShader = (gl, type, source) => {
|
|
130
|
+
const shader = gl.createShader(type);
|
|
131
|
+
if (!shader) {
|
|
132
|
+
throw new ShaderCompileError("Failed to create WebGL shader.");
|
|
133
|
+
}
|
|
134
|
+
gl.shaderSource(shader, source);
|
|
135
|
+
gl.compileShader(shader);
|
|
136
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
137
|
+
const log = gl.getShaderInfoLog(shader) ?? "Unknown shader compile error.";
|
|
138
|
+
gl.deleteShader(shader);
|
|
139
|
+
throw new ShaderCompileError(log, {
|
|
140
|
+
stage: type === gl.VERTEX_SHADER ? "vertex" : "fragment",
|
|
141
|
+
lines: parseShaderLog(log, source)
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return shader;
|
|
145
|
+
};
|
|
146
|
+
|
|
28
147
|
// src/internal/capture.ts
|
|
148
|
+
var waitForGpu = (gl) => {
|
|
149
|
+
if (!isWebGL2Context(gl)) return Promise.resolve();
|
|
150
|
+
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
151
|
+
if (!sync) return Promise.resolve();
|
|
152
|
+
gl.flush();
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
const poll = () => {
|
|
155
|
+
const status = gl.clientWaitSync(sync, 0, 0);
|
|
156
|
+
if (status === gl.TIMEOUT_EXPIRED) {
|
|
157
|
+
setTimeout(poll, 4);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
gl.deleteSync(sync);
|
|
161
|
+
resolve();
|
|
162
|
+
};
|
|
163
|
+
poll();
|
|
164
|
+
});
|
|
165
|
+
};
|
|
29
166
|
var readPixels = (gl, framebuffer, width, height, format) => {
|
|
30
167
|
const length = width * height * 4;
|
|
31
168
|
const data = format === "rgba16f" ? new Float32Array(length) : new Uint8Array(length);
|
|
@@ -166,6 +303,8 @@ var RuntimeHost = class {
|
|
|
166
303
|
this.rafId = 0;
|
|
167
304
|
this.running = false;
|
|
168
305
|
this.inRender = false;
|
|
306
|
+
this.captureDrawing = false;
|
|
307
|
+
this.resizePending = false;
|
|
169
308
|
this.destroyed = false;
|
|
170
309
|
this.gates = /* @__PURE__ */ new Set();
|
|
171
310
|
this.reducedMotionActive = false;
|
|
@@ -181,6 +320,7 @@ var RuntimeHost = class {
|
|
|
181
320
|
this.removeListeners = [];
|
|
182
321
|
this.currentStatus = "idle";
|
|
183
322
|
this.contextRestoredShouldRun = false;
|
|
323
|
+
this.statusBeforeLoss = "idle";
|
|
184
324
|
this.manualDelta = 0;
|
|
185
325
|
this.lastManualTime = null;
|
|
186
326
|
this.objectFitRestore = null;
|
|
@@ -285,30 +425,35 @@ var RuntimeHost = class {
|
|
|
285
425
|
if (this.inRender) {
|
|
286
426
|
throw new Error("captureFrame() cannot be called while a frame is drawing.");
|
|
287
427
|
}
|
|
288
|
-
|
|
428
|
+
const sourceFormat = this.options.captureSource?.()?.format;
|
|
429
|
+
if (format === "rgba16f" && sourceFormat !== "rgba16f" && sourceFormat !== "rgba32f") {
|
|
289
430
|
throw new UnsupportedError(
|
|
290
431
|
"rgba16f capture needs a float render target, such as feedback with format rgba16f."
|
|
291
432
|
);
|
|
292
433
|
}
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
434
|
+
options.signal?.throwIfAborted();
|
|
435
|
+
const delta = this.isManual ? this.manualDelta : 0;
|
|
436
|
+
const drawContext = this.context;
|
|
437
|
+
if (!this.options.onCaptureDraw) {
|
|
438
|
+
this.renderInternal(this.elapsed, delta);
|
|
439
|
+
return this.readCapture(format, drawContext);
|
|
297
440
|
}
|
|
298
|
-
|
|
299
|
-
|
|
441
|
+
try {
|
|
442
|
+
await this.renderForCapture(this.options.onCaptureDraw, delta, options.signal);
|
|
443
|
+
return this.readCapture(format, drawContext);
|
|
444
|
+
} finally {
|
|
445
|
+
if (this.resizePending) {
|
|
446
|
+
this.resizePending = false;
|
|
447
|
+
this.resize();
|
|
448
|
+
}
|
|
300
449
|
}
|
|
301
|
-
const source = this.options.captureSource();
|
|
302
|
-
return readPixels(
|
|
303
|
-
gl,
|
|
304
|
-
source.framebuffer,
|
|
305
|
-
source.width,
|
|
306
|
-
source.height,
|
|
307
|
-
"rgba16f"
|
|
308
|
-
);
|
|
309
450
|
}
|
|
310
451
|
resize() {
|
|
311
452
|
if (this.destroyed || !this.dom) return;
|
|
453
|
+
if (this.captureDrawing) {
|
|
454
|
+
this.resizePending = true;
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
312
457
|
const prevWidth = this.width;
|
|
313
458
|
const prevHeight = this.height;
|
|
314
459
|
const prevViewportWidth = this.viewportWidth;
|
|
@@ -401,7 +546,7 @@ var RuntimeHost = class {
|
|
|
401
546
|
this.renderInternal(this.elapsed, 0);
|
|
402
547
|
}
|
|
403
548
|
renderInternal(time, delta) {
|
|
404
|
-
if (!this.dom || !this.context) return;
|
|
549
|
+
if (!this.dom || !this.context || this.inRender) return;
|
|
405
550
|
const gl = this.context;
|
|
406
551
|
this.inRender = true;
|
|
407
552
|
try {
|
|
@@ -412,6 +557,47 @@ var RuntimeHost = class {
|
|
|
412
557
|
this.inRender = false;
|
|
413
558
|
}
|
|
414
559
|
}
|
|
560
|
+
// Holds the render lock across an asynchronous capture draw so no other
|
|
561
|
+
// frame interleaves with it.
|
|
562
|
+
async renderForCapture(draw, delta, signal) {
|
|
563
|
+
if (!this.dom || !this.context) return;
|
|
564
|
+
this.inRender = true;
|
|
565
|
+
this.captureDrawing = true;
|
|
566
|
+
try {
|
|
567
|
+
this.measureSize();
|
|
568
|
+
await draw(this.createFrameState(this.elapsed, delta, this.context), signal);
|
|
569
|
+
this.frame++;
|
|
570
|
+
} finally {
|
|
571
|
+
this.inRender = false;
|
|
572
|
+
this.captureDrawing = false;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
// Reads the frame just drawn, in the task that drew it: the drawing buffer
|
|
576
|
+
// is not preserved. Fails if the context drawn into is gone, including when
|
|
577
|
+
// it was lost and restored while an asynchronous draw was pending.
|
|
578
|
+
readCapture(format, drawContext) {
|
|
579
|
+
if (this.destroyed) {
|
|
580
|
+
throw new DestroyedError("Runtime host was destroyed.");
|
|
581
|
+
}
|
|
582
|
+
const gl = this.context;
|
|
583
|
+
if (!gl || gl !== drawContext || !this.dom) {
|
|
584
|
+
throw new UnsupportedError("The WebGL context was lost while capturing.");
|
|
585
|
+
}
|
|
586
|
+
if (format === "png") {
|
|
587
|
+
return canvasToPng(this.dom.canvas);
|
|
588
|
+
}
|
|
589
|
+
if (format === "rgba8") {
|
|
590
|
+
return readPixels(gl, null, this.width, this.height, "rgba8");
|
|
591
|
+
}
|
|
592
|
+
const source = this.options.captureSource();
|
|
593
|
+
return readPixels(
|
|
594
|
+
gl,
|
|
595
|
+
source.framebuffer,
|
|
596
|
+
source.width,
|
|
597
|
+
source.height,
|
|
598
|
+
"rgba16f"
|
|
599
|
+
);
|
|
600
|
+
}
|
|
415
601
|
createFrameState(time, delta, gl = this.context) {
|
|
416
602
|
return {
|
|
417
603
|
gl,
|
|
@@ -647,6 +833,7 @@ var RuntimeHost = class {
|
|
|
647
833
|
const onLost = (event) => {
|
|
648
834
|
event.preventDefault();
|
|
649
835
|
this.contextRestoredShouldRun = this.running;
|
|
836
|
+
this.statusBeforeLoss = this.currentStatus;
|
|
650
837
|
this.cancelFrame();
|
|
651
838
|
this.context = null;
|
|
652
839
|
this.currentStatus = "context-lost";
|
|
@@ -661,6 +848,7 @@ var RuntimeHost = class {
|
|
|
661
848
|
const afterRestore = async () => {
|
|
662
849
|
try {
|
|
663
850
|
await this.options.onContextRestored?.(gl, this.width, this.height);
|
|
851
|
+
if (this.currentStatus === "context-lost") this.currentStatus = this.statusBeforeLoss;
|
|
664
852
|
if (this.contextRestoredShouldRun) this.start();
|
|
665
853
|
else if (!this.isManual) this.render();
|
|
666
854
|
} catch (error) {
|
|
@@ -720,6 +908,211 @@ var assertWithinLimits = (gl, resolution) => {
|
|
|
720
908
|
var toUniformName = (name) => name.startsWith("u_") ? name : `u_${name}`;
|
|
721
909
|
var fromUniformName = (name) => name.startsWith("u_") ? name.slice(2) : name;
|
|
722
910
|
|
|
911
|
+
// src/internal/uniforms.ts
|
|
912
|
+
var normalizeUniform = (input) => {
|
|
913
|
+
if (typeof input === "number") {
|
|
914
|
+
return { type: "float", value: [input] };
|
|
915
|
+
}
|
|
916
|
+
if (input instanceof Float32Array) {
|
|
917
|
+
if (input.length === 9) return { type: "mat3", value: input };
|
|
918
|
+
if (input.length === 16) return { type: "mat4", value: input };
|
|
919
|
+
if (input.length === 2) return { type: "vec2", value: input };
|
|
920
|
+
if (input.length === 3) return { type: "vec3", value: input };
|
|
921
|
+
if (input.length === 4) return { type: "vec4", value: input };
|
|
922
|
+
}
|
|
923
|
+
if (isExplicitUniform(input)) {
|
|
924
|
+
return { type: input.type, value: input.value };
|
|
925
|
+
}
|
|
926
|
+
if (Array.isArray(input)) {
|
|
927
|
+
if (input.length === 2) return { type: "vec2", value: input };
|
|
928
|
+
if (input.length === 3) return { type: "vec3", value: input };
|
|
929
|
+
if (input.length === 4) return { type: "vec4", value: input };
|
|
930
|
+
}
|
|
931
|
+
return { type: "float", value: [0] };
|
|
932
|
+
};
|
|
933
|
+
var applyUniform = (gl, location, uniform) => {
|
|
934
|
+
const value = uniform.value;
|
|
935
|
+
switch (uniform.type) {
|
|
936
|
+
case "float":
|
|
937
|
+
gl.uniform1f(location, value[0] ?? 0);
|
|
938
|
+
break;
|
|
939
|
+
case "vec2":
|
|
940
|
+
gl.uniform2f(location, value[0] ?? 0, value[1] ?? 0);
|
|
941
|
+
break;
|
|
942
|
+
case "vec3":
|
|
943
|
+
gl.uniform3f(location, value[0] ?? 0, value[1] ?? 0, value[2] ?? 0);
|
|
944
|
+
break;
|
|
945
|
+
case "vec4":
|
|
946
|
+
gl.uniform4f(
|
|
947
|
+
location,
|
|
948
|
+
value[0] ?? 0,
|
|
949
|
+
value[1] ?? 0,
|
|
950
|
+
value[2] ?? 0,
|
|
951
|
+
value[3] ?? 0
|
|
952
|
+
);
|
|
953
|
+
break;
|
|
954
|
+
case "mat3":
|
|
955
|
+
gl.uniformMatrix3fv(location, false, value);
|
|
956
|
+
break;
|
|
957
|
+
case "mat4":
|
|
958
|
+
gl.uniformMatrix4fv(location, false, value);
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
var applyProgramUniform = (gl, program, location, uniform) => {
|
|
963
|
+
gl.useProgram(program);
|
|
964
|
+
applyUniform(gl, location, uniform);
|
|
965
|
+
};
|
|
966
|
+
var applyUniforms = (gl, locations, cache, onMissing) => {
|
|
967
|
+
for (const [name, uniform] of cache) {
|
|
968
|
+
const location = locations.get(name);
|
|
969
|
+
if (!location) {
|
|
970
|
+
onMissing?.(name);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
applyUniform(gl, location, uniform);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
var collectUniformLocations = (gl, program) => {
|
|
977
|
+
const locations = /* @__PURE__ */ new Map();
|
|
978
|
+
const count = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
|
|
979
|
+
for (let index = 0; index < count; index += 1) {
|
|
980
|
+
const active = gl.getActiveUniform(program, index);
|
|
981
|
+
if (!active) continue;
|
|
982
|
+
const name = active.name.replace(/\[0\]$/, "");
|
|
983
|
+
const location = gl.getUniformLocation(program, name);
|
|
984
|
+
if (location) {
|
|
985
|
+
locations.set(name, location);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return locations;
|
|
989
|
+
};
|
|
990
|
+
var createUniformCache = (uniforms) => {
|
|
991
|
+
const cache = /* @__PURE__ */ new Map();
|
|
992
|
+
for (const [name, value] of Object.entries(uniforms ?? {})) {
|
|
993
|
+
cache.set(toUniformName(name), normalizeUniform(value));
|
|
994
|
+
}
|
|
995
|
+
return cache;
|
|
996
|
+
};
|
|
997
|
+
var isExplicitUniform = (input) => {
|
|
998
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return false;
|
|
999
|
+
return "type" in input && "value" in input;
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
// src/internal/bindings.ts
|
|
1003
|
+
var createUniformBindings = () => {
|
|
1004
|
+
const sources = [];
|
|
1005
|
+
const evaluate = (time) => {
|
|
1006
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1007
|
+
for (const source of sources) {
|
|
1008
|
+
for (const [name, value] of Object.entries(source.get(time))) {
|
|
1009
|
+
merged.set(toUniformName(name), normalizeUniform(value));
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
return merged;
|
|
1013
|
+
};
|
|
1014
|
+
return {
|
|
1015
|
+
add(source) {
|
|
1016
|
+
sources.push(source);
|
|
1017
|
+
let removed = false;
|
|
1018
|
+
return () => {
|
|
1019
|
+
if (removed) return;
|
|
1020
|
+
removed = true;
|
|
1021
|
+
const index = sources.indexOf(source);
|
|
1022
|
+
if (index >= 0) sources.splice(index, 1);
|
|
1023
|
+
};
|
|
1024
|
+
},
|
|
1025
|
+
get size() {
|
|
1026
|
+
return sources.length;
|
|
1027
|
+
},
|
|
1028
|
+
evaluate,
|
|
1029
|
+
signature(time) {
|
|
1030
|
+
if (sources.length === 0) return "";
|
|
1031
|
+
let text = "";
|
|
1032
|
+
for (const [name, uniform] of evaluate(time)) {
|
|
1033
|
+
text += `${name}:${uniform.type}:${Array.from(uniform.value).join(",")};`;
|
|
1034
|
+
}
|
|
1035
|
+
return text;
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
};
|
|
1039
|
+
var EASES = {
|
|
1040
|
+
linear: (t) => t,
|
|
1041
|
+
smooth: (t) => t * t * (3 - 2 * t),
|
|
1042
|
+
step: () => 0
|
|
1043
|
+
};
|
|
1044
|
+
var toEaseFunction = (ease) => {
|
|
1045
|
+
if (typeof ease === "function") return ease;
|
|
1046
|
+
const fn = EASES[ease];
|
|
1047
|
+
if (!fn) throw new RangeError(`Unknown ease: ${String(ease)}`);
|
|
1048
|
+
return fn;
|
|
1049
|
+
};
|
|
1050
|
+
var componentsOf = (value) => typeof value === "number" ? [value] : value;
|
|
1051
|
+
var isKeyList = (track) => Array.isArray(track);
|
|
1052
|
+
var compileTrack = (name, track, fallback) => {
|
|
1053
|
+
const list = isKeyList(track);
|
|
1054
|
+
const keys = list ? track : track.keys;
|
|
1055
|
+
const ease = toEaseFunction((list ? void 0 : track.ease) ?? fallback);
|
|
1056
|
+
if (keys.length === 0) throw new TypeError(`Keyframe track ${name} has no keys.`);
|
|
1057
|
+
const sorted = [...keys].sort((a, b) => a[0] - b[0]);
|
|
1058
|
+
const scalar = typeof sorted[0][1] === "number";
|
|
1059
|
+
const size = componentsOf(sorted[0][1]).length;
|
|
1060
|
+
for (const [time, value] of sorted) {
|
|
1061
|
+
if (!Number.isFinite(time)) throw new TypeError(`Keyframe track ${name} has a non-finite time.`);
|
|
1062
|
+
const components = componentsOf(value);
|
|
1063
|
+
if (typeof value === "number" !== scalar || components.length !== size) {
|
|
1064
|
+
throw new TypeError(`Keyframe track ${name} mixes values of different sizes.`);
|
|
1065
|
+
}
|
|
1066
|
+
if (!components.every(Number.isFinite)) {
|
|
1067
|
+
throw new TypeError(`Keyframe track ${name} has a non-finite value.`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return {
|
|
1071
|
+
times: sorted.map(([time]) => time),
|
|
1072
|
+
values: sorted.map(([, value]) => componentsOf(value)),
|
|
1073
|
+
scalar,
|
|
1074
|
+
ease
|
|
1075
|
+
};
|
|
1076
|
+
};
|
|
1077
|
+
var sampleTrack = (track, time) => {
|
|
1078
|
+
const { times, values } = track;
|
|
1079
|
+
const last = times.length - 1;
|
|
1080
|
+
let components;
|
|
1081
|
+
if (time <= times[0]) {
|
|
1082
|
+
components = values[0];
|
|
1083
|
+
} else if (time >= times[last]) {
|
|
1084
|
+
components = values[last];
|
|
1085
|
+
} else {
|
|
1086
|
+
let low = 0;
|
|
1087
|
+
let high = last;
|
|
1088
|
+
while (high - low > 1) {
|
|
1089
|
+
const middle = low + high >> 1;
|
|
1090
|
+
if (times[middle] <= time) low = middle;
|
|
1091
|
+
else high = middle;
|
|
1092
|
+
}
|
|
1093
|
+
const span = times[high] - times[low];
|
|
1094
|
+
const t = span > 0 ? track.ease((time - times[low]) / span) : 0;
|
|
1095
|
+
const from = values[low];
|
|
1096
|
+
const to = values[high];
|
|
1097
|
+
components = from.map((value, index) => value + (to[index] - value) * t);
|
|
1098
|
+
}
|
|
1099
|
+
return track.scalar ? components[0] : Array.from(components);
|
|
1100
|
+
};
|
|
1101
|
+
var keyframes = (tracks, options = {}) => {
|
|
1102
|
+
const fallback = options.ease ?? "linear";
|
|
1103
|
+
toEaseFunction(fallback);
|
|
1104
|
+
const compiled = Object.entries(tracks).map(
|
|
1105
|
+
([name, track]) => [name, compileTrack(name, track, fallback)]
|
|
1106
|
+
);
|
|
1107
|
+
return {
|
|
1108
|
+
get(time) {
|
|
1109
|
+
const values = {};
|
|
1110
|
+
for (const [name, track] of compiled) values[name] = sampleTrack(track, time);
|
|
1111
|
+
return values;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
};
|
|
1115
|
+
|
|
723
1116
|
// src/internal/textures.ts
|
|
724
1117
|
var loadTexture = async (gl, name, input, unit) => {
|
|
725
1118
|
const options = normalizeTextureInput(input);
|
|
@@ -777,79 +1170,118 @@ var isTextureSource = (input) => typeof HTMLImageElement !== "undefined" && (inp
|
|
|
777
1170
|
var getSourceWidth = (source) => "naturalWidth" in source ? source.naturalWidth : source.width;
|
|
778
1171
|
var getSourceHeight = (source) => "naturalHeight" in source ? source.naturalHeight : source.height;
|
|
779
1172
|
|
|
780
|
-
// src/internal/
|
|
781
|
-
var
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1173
|
+
// src/internal/textureSet.ts
|
|
1174
|
+
var createTextureSet = (inputs = {}) => {
|
|
1175
|
+
const textureInputs = new Map(Object.entries(inputs));
|
|
1176
|
+
let textures = /* @__PURE__ */ new Map();
|
|
1177
|
+
const units = /* @__PURE__ */ new Map();
|
|
1178
|
+
const versions = /* @__PURE__ */ new Map();
|
|
1179
|
+
const reserveUnit = (gl, name) => {
|
|
1180
|
+
const existing = units.get(name);
|
|
1181
|
+
if (existing !== void 0) return existing;
|
|
1182
|
+
const maxUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
1183
|
+
const used = new Set(units.values());
|
|
1184
|
+
for (let unit = 0; unit < maxUnits; unit += 1) {
|
|
1185
|
+
if (!used.has(unit)) {
|
|
1186
|
+
units.set(name, unit);
|
|
1187
|
+
return unit;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
throw new TextureLoadError(
|
|
1191
|
+
`Texture unit limit exceeded while loading texture: ${name}`
|
|
1192
|
+
);
|
|
1193
|
+
};
|
|
1194
|
+
return {
|
|
1195
|
+
get textures() {
|
|
1196
|
+
return textures;
|
|
1197
|
+
},
|
|
1198
|
+
hasInput(name) {
|
|
1199
|
+
return textureInputs.has(name);
|
|
1200
|
+
},
|
|
1201
|
+
reserveUnit,
|
|
1202
|
+
async loadAll(gl, isCancelled) {
|
|
1203
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
1204
|
+
for (const [name, input] of textureInputs) {
|
|
1205
|
+
if (isCancelled()) break;
|
|
1206
|
+
const unit = reserveUnit(gl, name);
|
|
1207
|
+
loaded.set(name, await loadTexture(gl, name, input, unit));
|
|
1208
|
+
}
|
|
1209
|
+
for (const texture of textures.values()) {
|
|
1210
|
+
gl.deleteTexture(texture.texture);
|
|
1211
|
+
}
|
|
1212
|
+
textures = loaded;
|
|
1213
|
+
},
|
|
1214
|
+
async set(gl, name, input, isCancelled) {
|
|
1215
|
+
const version = (versions.get(name) ?? 0) + 1;
|
|
1216
|
+
versions.set(name, version);
|
|
1217
|
+
try {
|
|
1218
|
+
const unit = reserveUnit(gl, name);
|
|
1219
|
+
const loaded = await loadTexture(gl, name, input, unit);
|
|
1220
|
+
if (isCancelled() || versions.get(name) !== version) {
|
|
1221
|
+
gl.deleteTexture(loaded.texture);
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
const previous = textures.get(name);
|
|
1225
|
+
textureInputs.set(name, input);
|
|
1226
|
+
textures.set(name, loaded);
|
|
1227
|
+
if (previous) gl.deleteTexture(previous.texture);
|
|
1228
|
+
return loaded;
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
if (!textures.has(name) && !textureInputs.has(name)) {
|
|
1231
|
+
units.delete(name);
|
|
1232
|
+
}
|
|
1233
|
+
throw error;
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
bind(gl, locations, onMissing) {
|
|
1237
|
+
for (const texture of textures.values()) {
|
|
1238
|
+
const unit = units.get(texture.name);
|
|
1239
|
+
if (unit === void 0) continue;
|
|
1240
|
+
const location = locations.get(texture.uniformName);
|
|
1241
|
+
if (!location) {
|
|
1242
|
+
onMissing?.(texture.uniformName);
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
1246
|
+
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
|
|
1247
|
+
gl.uniform1i(location, unit);
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
dispose(gl) {
|
|
1251
|
+
for (const texture of textures.values()) {
|
|
1252
|
+
gl.deleteTexture(texture.texture);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
839
1256
|
};
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
gl.
|
|
846
|
-
gl
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1257
|
+
|
|
1258
|
+
// src/internal/fullscreenProgram.ts
|
|
1259
|
+
var createFullscreenProgram = (gl, fragment, vertex) => {
|
|
1260
|
+
const defaultVertex = isGLSL300(fragment) ? defaultVertexShader300 : defaultVertexShader;
|
|
1261
|
+
const program = createProgram(gl, vertex ?? defaultVertex, fragment);
|
|
1262
|
+
gl.useProgram(program);
|
|
1263
|
+
const buffer = createFullscreenBuffer(gl, program);
|
|
1264
|
+
const attribLocation = gl.getAttribLocation(program, "a_position");
|
|
1265
|
+
const locations = collectUniformLocations(gl, program);
|
|
1266
|
+
return {
|
|
1267
|
+
program,
|
|
1268
|
+
locations,
|
|
1269
|
+
use() {
|
|
1270
|
+
gl.useProgram(program);
|
|
1271
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
1272
|
+
if (attribLocation >= 0) {
|
|
1273
|
+
gl.enableVertexAttribArray(attribLocation);
|
|
1274
|
+
gl.vertexAttribPointer(attribLocation, 2, gl.FLOAT, false, 0, 0);
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
draw() {
|
|
1278
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
1279
|
+
},
|
|
1280
|
+
dispose() {
|
|
1281
|
+
gl.deleteBuffer(buffer);
|
|
1282
|
+
gl.deleteProgram(program);
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
853
1285
|
};
|
|
854
1286
|
|
|
855
1287
|
// src/internal/blit.ts
|
|
@@ -1007,16 +1439,17 @@ var Target = class {
|
|
|
1007
1439
|
throw new ShaderCompileError("Failed to create render target framebuffer.");
|
|
1008
1440
|
}
|
|
1009
1441
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
1010
|
-
if (this.format
|
|
1442
|
+
if (this.format !== "rgba8" && isWebGL2Context(gl)) {
|
|
1443
|
+
const half = this.format === "rgba16f";
|
|
1011
1444
|
gl.texImage2D(
|
|
1012
1445
|
gl.TEXTURE_2D,
|
|
1013
1446
|
0,
|
|
1014
|
-
gl.RGBA16F,
|
|
1447
|
+
half ? gl.RGBA16F : gl.RGBA32F,
|
|
1015
1448
|
this.width,
|
|
1016
1449
|
this.height,
|
|
1017
1450
|
0,
|
|
1018
1451
|
gl.RGBA,
|
|
1019
|
-
gl.HALF_FLOAT,
|
|
1452
|
+
half ? gl.HALF_FLOAT : gl.FLOAT,
|
|
1020
1453
|
null
|
|
1021
1454
|
);
|
|
1022
1455
|
} else {
|
|
@@ -1033,7 +1466,8 @@ var Target = class {
|
|
|
1033
1466
|
);
|
|
1034
1467
|
}
|
|
1035
1468
|
const wrap = getWrap2(gl, this.options.wrap ?? "clamp");
|
|
1036
|
-
const
|
|
1469
|
+
const linear = this.options.filter !== "nearest" && (this.format !== "rgba32f" || gl.getExtension("OES_texture_float_linear") !== null);
|
|
1470
|
+
const filter = linear ? gl.LINEAR : gl.NEAREST;
|
|
1037
1471
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
|
|
1038
1472
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
|
|
1039
1473
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
|
|
@@ -1076,12 +1510,20 @@ export {
|
|
|
1076
1510
|
TargetNotFoundError,
|
|
1077
1511
|
TextureLoadError,
|
|
1078
1512
|
UnsupportedError,
|
|
1513
|
+
applyProgramUniform,
|
|
1514
|
+
applyUniform,
|
|
1515
|
+
applyUniforms,
|
|
1516
|
+
collectUniformLocations,
|
|
1079
1517
|
createBlitter,
|
|
1080
1518
|
createFullscreenBuffer,
|
|
1519
|
+
createFullscreenProgram,
|
|
1081
1520
|
createPingPong,
|
|
1082
1521
|
createProgram,
|
|
1083
1522
|
createRenderTarget,
|
|
1084
1523
|
createRuntimeHost,
|
|
1524
|
+
createTextureSet,
|
|
1525
|
+
createUniformBindings,
|
|
1526
|
+
createUniformCache,
|
|
1085
1527
|
defaultVertexShader,
|
|
1086
1528
|
defaultVertexShader300,
|
|
1087
1529
|
fromUniformName,
|
|
@@ -1091,13 +1533,17 @@ export {
|
|
|
1091
1533
|
hexToRgba,
|
|
1092
1534
|
isGLSL300,
|
|
1093
1535
|
isWebGL2Context,
|
|
1536
|
+
keyframes,
|
|
1094
1537
|
loadTexture,
|
|
1538
|
+
normalizeUniform,
|
|
1539
|
+
parseShaderLog,
|
|
1095
1540
|
resizeCanvasToTarget,
|
|
1096
1541
|
resolveDpr,
|
|
1097
1542
|
resolveElement,
|
|
1098
1543
|
resolveRenderTargetFormat,
|
|
1099
1544
|
setupCanvas,
|
|
1100
1545
|
supportsRenderTargetFormat,
|
|
1101
|
-
toUniformName
|
|
1546
|
+
toUniformName,
|
|
1547
|
+
waitForGpu
|
|
1102
1548
|
};
|
|
1103
1549
|
//# sourceMappingURL=index.js.map
|