@weasel-js/renderer 0.0.0-stage → 1.9.4
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 +21 -0
- package/README.md +61 -2
- package/dist/chunk-2VOBNWBN.js +11 -0
- package/dist/chunk-2VOBNWBN.js.map +1 -0
- package/dist/chunk-ORBXHIRB.js +178 -0
- package/dist/chunk-ORBXHIRB.js.map +1 -0
- package/dist/chunk-VHQJ56XQ.js +1437 -0
- package/dist/chunk-VHQJ56XQ.js.map +1 -0
- package/dist/imageCache-oJVgxhkr.d.ts +372 -0
- package/dist/index.d.ts +1070 -0
- package/dist/index.js +4086 -0
- package/dist/index.js.map +1 -0
- package/dist/mesh.d.ts +274 -0
- package/dist/mesh.js +603 -0
- package/dist/mesh.js.map +1 -0
- package/dist/paintKinds-B2vEy9A4.d.ts +330 -0
- package/dist/patterns-builtin.d.ts +118 -0
- package/dist/patterns-builtin.js +28 -0
- package/dist/patterns-builtin.js.map +1 -0
- package/dist/test-seams.d.ts +120 -0
- package/dist/test-seams.js +4 -0
- package/dist/test-seams.js.map +1 -0
- package/dist/testing/glRecorder.d.ts +36 -0
- package/dist/testing/glRecorder.js +155 -0
- package/dist/testing/glRecorder.js.map +1 -0
- package/dist/testing/vertexUploads.d.ts +30 -0
- package/dist/testing/vertexUploads.js +48 -0
- package/dist/testing/vertexUploads.js.map +1 -0
- package/package.json +71 -4
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Path } from '@weasel-js/geom';
|
|
2
|
+
import { Mesh } from '@weasel-js/geom/tessellate';
|
|
3
|
+
export { F as FLOATS_PER_VERTEX, _ as __setImageLoaderForTests, k as _resetImageCacheForTests, l as _resetPatternSpecCacheForTests, m as _resetTextureRegistryForTests } from './imageCache-oJVgxhkr.js';
|
|
4
|
+
export { _ as _resetPaintDrawKindsForTests, s as _resetProgramRegistryForTests } from './paintKinds-B2vEy9A4.js';
|
|
5
|
+
import '@weasel-js/paint';
|
|
6
|
+
import '@weasel-js/registry';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Return the tessellated Mesh for `path`, computing and caching on first call.
|
|
10
|
+
* Cached by Path identity (WeakMap) — different Path objects with the same
|
|
11
|
+
* coords are distinct cache entries.
|
|
12
|
+
*
|
|
13
|
+
* A rect path the batch stages as a quad never reaches this cache. One used
|
|
14
|
+
* as a clip, or filled with a pattern or vertex colors, does.
|
|
15
|
+
*
|
|
16
|
+
* Also bypassed whenever `DrawContext.flattenTolerance` is set: `fillMeshHandle`
|
|
17
|
+
* (see `../draw`) routes custom-tolerance fills through a fresh transient
|
|
18
|
+
* tessellation instead of this cache.
|
|
19
|
+
*/
|
|
20
|
+
declare function getMesh(path: Path): Mesh;
|
|
21
|
+
/** Test helper. Do not call from product code. */
|
|
22
|
+
declare function _resetCacheForTests(): void;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A stroke's marker commands, cached per path. The placed head is a fresh
|
|
26
|
+
* `Path`, and the fill cache keys on path identity — so rebuilding it every
|
|
27
|
+
* frame would re-tessellate and re-upload every head every frame.
|
|
28
|
+
*
|
|
29
|
+
* Keyed on value, so a painter that builds a new stroke object every frame
|
|
30
|
+
* still hits: the resolved lengths, the flatten tolerance, the registry's
|
|
31
|
+
* generation, the marker refs and whatever fields each head declares it reads.
|
|
32
|
+
* A head that does not declare them may read any field, so for it the stroke
|
|
33
|
+
* as the command carried it — before any `{ px }` length was resolved — joins
|
|
34
|
+
* the key by identity. Paint is applied over the cached geometry, so a new
|
|
35
|
+
* paint reuses the heads.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/** Test helper. Do not call from product code. */
|
|
39
|
+
declare function _resetMarkerCommandCacheForTests(): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Tessellated glyph outlines, in em space.
|
|
43
|
+
*
|
|
44
|
+
* This is the half of the outline text tier that is expensive: parsing `d`
|
|
45
|
+
* into a path and running earcut over it costs far more than the per-instance
|
|
46
|
+
* transform that follows. It is also the half that does not vary — em space
|
|
47
|
+
* is unit-scale and baseline-relative, so the same glyph of the same face is
|
|
48
|
+
* the same triangles at every size, position and zoom. Tessellate once, keep
|
|
49
|
+
* it, and pay only a scale-and-translate per occurrence.
|
|
50
|
+
*
|
|
51
|
+
* That is why the renderer transforms cached vertices on the CPU and appends
|
|
52
|
+
* them into one shared buffer rather than giving each glyph its own model
|
|
53
|
+
* matrix: a per-glyph matrix would mean a draw call per glyph, throwing away
|
|
54
|
+
* the batching the atlas tier already has, in exchange for saving an
|
|
55
|
+
* arithmetic pass over a few hundred vertices.
|
|
56
|
+
*
|
|
57
|
+
* ### Flatten tolerance
|
|
58
|
+
*
|
|
59
|
+
* Tessellating once means committing to a curve tolerance once, and em space
|
|
60
|
+
* makes that a clean tradeoff rather than a guess: an error of `T` em shows
|
|
61
|
+
* up as `T × fontSize × viewScale` screen pixels. At 1/4096 em, a glyph would
|
|
62
|
+
* have to reach ~4000 screen pixels tall before the flattening error covered
|
|
63
|
+
* a single pixel — well past any size a document is read at, and far enough
|
|
64
|
+
* past the tier's own threshold that the two never interact.
|
|
65
|
+
*
|
|
66
|
+
* ### Bound
|
|
67
|
+
*
|
|
68
|
+
* A plain `Map` with a cap and wholesale eviction. Glyph tessellations are
|
|
69
|
+
* small (a few hundred vertices) and the working set is a charset, not a
|
|
70
|
+
* document — the cap exists so a page that cycles through many faces cannot
|
|
71
|
+
* grow without limit, not because pressure is expected. Wholesale rather than
|
|
72
|
+
* LRU because the refill cost is bounded by what is actually on screen, and
|
|
73
|
+
* an LRU's bookkeeping would cost more than the misses it avoids.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/** Test helper. Do not call from product code. */
|
|
77
|
+
declare function _resetOutlineMeshCacheForTests(): void;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Tessellated glyph *outlines' strokes*, in em space.
|
|
81
|
+
*
|
|
82
|
+
* The sibling of `outlineMeshCache`, and cached for the same reason: parsing
|
|
83
|
+
* `d` and tessellating a ribbon costs far more than the per-instance
|
|
84
|
+
* transform that follows, and em space is size- and position-independent, so
|
|
85
|
+
* one tessellation serves every occurrence of that glyph.
|
|
86
|
+
*
|
|
87
|
+
* ### Why the width is in the key
|
|
88
|
+
*
|
|
89
|
+
* A fill mesh is the same triangles at every size. A stroke's is not — the
|
|
90
|
+
* ribbon's width relative to the glyph decides the geometry, so the em-space
|
|
91
|
+
* mesh depends on `strokeWidth / fontSize`. That ratio is a property of the
|
|
92
|
+
* run's style, not of the frame, so it is stable across frames and makes a
|
|
93
|
+
* perfectly good cache key; what it is *not* is free, so a document that
|
|
94
|
+
* animates stroke width or font size will miss every frame. That is the same
|
|
95
|
+
* bargain `tessellate` makes everywhere else, and it is the reason the width
|
|
96
|
+
* is quantized before it reaches the key: neighbouring sub-quantum widths
|
|
97
|
+
* would otherwise each get their own entry and evict each other.
|
|
98
|
+
*
|
|
99
|
+
* Joins, caps, miter limit, alignment and dash all change the ribbon too, so
|
|
100
|
+
* they are in the key as well — assembled by the caller, which already has
|
|
101
|
+
* the `Stroke` in hand.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/** Test helper. Do not call from product code. */
|
|
105
|
+
declare function _resetOutlineStrokeMeshCacheForTests(): void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Tessellated stroke ribbons for scene paths, keyed on `Path` identity.
|
|
109
|
+
*
|
|
110
|
+
* The sibling of `cache.ts` (fills) and `outlineStrokeMeshCache.ts` (glyph
|
|
111
|
+
* ribbons in em space), and it inherits their contract: identity, not content.
|
|
112
|
+
* A `Path` rebuilt with equal coords is a distinct entry.
|
|
113
|
+
*
|
|
114
|
+
* Design: `docs/superpowers/specs/2026-08-15-stroke-ribbon-cache-design.md`.
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
/** Test helper. Do not call from product code. */
|
|
118
|
+
declare function _resetStrokeMeshCacheForTests(): void;
|
|
119
|
+
|
|
120
|
+
export { _resetCacheForTests, _resetMarkerCommandCacheForTests, _resetOutlineMeshCacheForTests, _resetOutlineStrokeMeshCacheForTests, _resetStrokeMeshCacheForTests, getMesh };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { FLOATS_PER_VERTEX, __setImageLoaderForTests, _resetCacheForTests, _resetImageCacheForTests, _resetMarkerCommandCacheForTests, _resetOutlineMeshCacheForTests, _resetOutlineStrokeMeshCacheForTests, _resetPaintDrawKindsForTests, _resetPatternSpecCacheForTests, _resetProgramRegistryForTests, _resetStrokeMeshCacheForTests, getMesh } from './chunk-VHQJ56XQ.js';
|
|
2
|
+
export { _resetTextureRegistryForTests } from './chunk-ORBXHIRB.js';
|
|
3
|
+
//# sourceMappingURL=test-seams.js.map
|
|
4
|
+
//# sourceMappingURL=test-seams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"test-seams.js"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy-based recording WebGL2 context for unit tests.
|
|
3
|
+
*
|
|
4
|
+
* Records every method call as { name, args, result } so tests can assert
|
|
5
|
+
* against the call sequence. Returns synthetic handles for object creation
|
|
6
|
+
* (createShader/Program/Buffer/etc.); reports truthy for *Parameter status
|
|
7
|
+
* queries (i.e. assumes shader/program compilation succeeded).
|
|
8
|
+
*
|
|
9
|
+
* NOT a renderer correctness check — pixel-level correctness is verified
|
|
10
|
+
* end-to-end by the smoke test in dev/smoke.spec.ts. This recorder catches
|
|
11
|
+
* "did the dispatcher emit the right call sequence" bugs only.
|
|
12
|
+
*/
|
|
13
|
+
interface GLCall {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly args: readonly unknown[];
|
|
16
|
+
readonly result: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** What the recorded context reports about itself. */
|
|
19
|
+
interface GLRecorderOptions {
|
|
20
|
+
/** Reported as `gl.drawingBufferWidth`. Default 800. */
|
|
21
|
+
drawingBufferWidth?: number;
|
|
22
|
+
/** Reported as `gl.drawingBufferHeight`. Default 600. */
|
|
23
|
+
drawingBufferHeight?: number;
|
|
24
|
+
/** Returned by `gl.getContextAttributes()`. Default `{ stencil: true }`. */
|
|
25
|
+
contextAttributes?: Partial<WebGLContextAttributes>;
|
|
26
|
+
}
|
|
27
|
+
/** A stand-in context and the calls made on it, oldest first. */
|
|
28
|
+
interface GLRecorder {
|
|
29
|
+
readonly gl: WebGL2RenderingContext;
|
|
30
|
+
readonly calls: GLCall[];
|
|
31
|
+
reset(): void;
|
|
32
|
+
}
|
|
33
|
+
/** A WebGL2 stand-in that records every call and draws nothing. */
|
|
34
|
+
declare function makeGLRecorder(options?: GLRecorderOptions): GLRecorder;
|
|
35
|
+
|
|
36
|
+
export { type GLCall, type GLRecorder, type GLRecorderOptions, makeGLRecorder };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// src/testing/glRecorder.ts
|
|
2
|
+
var GL_CONSTANTS = {
|
|
3
|
+
// Shader / program
|
|
4
|
+
VERTEX_SHADER: 35633,
|
|
5
|
+
FRAGMENT_SHADER: 35632,
|
|
6
|
+
COMPILE_STATUS: 35713,
|
|
7
|
+
LINK_STATUS: 35714,
|
|
8
|
+
// Buffer targets
|
|
9
|
+
ARRAY_BUFFER: 34962,
|
|
10
|
+
ELEMENT_ARRAY_BUFFER: 34963,
|
|
11
|
+
STATIC_DRAW: 35044,
|
|
12
|
+
// Types
|
|
13
|
+
FLOAT: 5126,
|
|
14
|
+
UNSIGNED_INT: 5125,
|
|
15
|
+
// Primitives
|
|
16
|
+
TRIANGLES: 4,
|
|
17
|
+
// Errors / state
|
|
18
|
+
NO_ERROR: 0,
|
|
19
|
+
DEPTH_TEST: 2929,
|
|
20
|
+
BLEND: 3042,
|
|
21
|
+
STENCIL_TEST: 2960,
|
|
22
|
+
CULL_FACE: 2884,
|
|
23
|
+
UNPACK_FLIP_Y_WEBGL: 37440,
|
|
24
|
+
SCISSOR_TEST: 3089,
|
|
25
|
+
MAX_TEXTURE_SIZE: 3379,
|
|
26
|
+
// Blend factors
|
|
27
|
+
SRC_ALPHA: 770,
|
|
28
|
+
ONE_MINUS_SRC_ALPHA: 771,
|
|
29
|
+
ZERO: 0,
|
|
30
|
+
ONE: 1,
|
|
31
|
+
// Buffer bits
|
|
32
|
+
COLOR_BUFFER_BIT: 16384,
|
|
33
|
+
STENCIL_BUFFER_BIT: 1024,
|
|
34
|
+
DEPTH_BUFFER_BIT: 256,
|
|
35
|
+
// Stencil ops
|
|
36
|
+
KEEP: 7680,
|
|
37
|
+
REPLACE: 7681,
|
|
38
|
+
INVERT: 5386,
|
|
39
|
+
// Compare funcs
|
|
40
|
+
ALWAYS: 519,
|
|
41
|
+
NEVER: 512,
|
|
42
|
+
EQUAL: 514,
|
|
43
|
+
NOTEQUAL: 517,
|
|
44
|
+
// Color masks
|
|
45
|
+
FRONT_AND_BACK: 1032,
|
|
46
|
+
// Misc constants used in step 1
|
|
47
|
+
COLOR_WRITEMASK: 3107,
|
|
48
|
+
// Textures (step 3)
|
|
49
|
+
TEXTURE_2D: 3553,
|
|
50
|
+
TEXTURE0: 33984,
|
|
51
|
+
RGBA: 6408,
|
|
52
|
+
UNSIGNED_BYTE: 5121,
|
|
53
|
+
TEXTURE_MIN_FILTER: 10241,
|
|
54
|
+
TEXTURE_MAG_FILTER: 10240,
|
|
55
|
+
LINEAR: 9729,
|
|
56
|
+
NEAREST: 9728,
|
|
57
|
+
TEXTURE_WRAP_S: 10242,
|
|
58
|
+
TEXTURE_WRAP_T: 10243,
|
|
59
|
+
CLAMP_TO_EDGE: 33071,
|
|
60
|
+
// Wrap modes (step 4)
|
|
61
|
+
REPEAT: 10497,
|
|
62
|
+
MIRRORED_REPEAT: 33648,
|
|
63
|
+
// Mipmap minification filter
|
|
64
|
+
LINEAR_MIPMAP_LINEAR: 9987,
|
|
65
|
+
// Framebuffers and renderbuffers (offscreen effect passes). Every one of
|
|
66
|
+
// these has to be here before a test names it: an absent ALL-CAPS property
|
|
67
|
+
// reads as 0 through the proxy, so `expect(args[0]).toBe(gl.FRAMEBUFFER)`
|
|
68
|
+
// would compare 0 against 0 and pass over a renderer that bound nothing.
|
|
69
|
+
FRAMEBUFFER: 36160,
|
|
70
|
+
RENDERBUFFER: 36161,
|
|
71
|
+
FRAMEBUFFER_BINDING: 36006,
|
|
72
|
+
RENDERBUFFER_BINDING: 36007,
|
|
73
|
+
COLOR_ATTACHMENT0: 36064,
|
|
74
|
+
DEPTH_STENCIL_ATTACHMENT: 33306,
|
|
75
|
+
DEPTH24_STENCIL8: 35056,
|
|
76
|
+
RGBA8: 32856,
|
|
77
|
+
FRAMEBUFFER_COMPLETE: 36053
|
|
78
|
+
};
|
|
79
|
+
function syntheticHandle(name, seq) {
|
|
80
|
+
return { __id: `${name}_${seq}` };
|
|
81
|
+
}
|
|
82
|
+
function snapshot(value) {
|
|
83
|
+
return ArrayBuffer.isView(value) && !(value instanceof DataView) ? value.slice() : value;
|
|
84
|
+
}
|
|
85
|
+
function makeGLRecorder(options = {}) {
|
|
86
|
+
const calls = [];
|
|
87
|
+
const {
|
|
88
|
+
drawingBufferWidth = 800,
|
|
89
|
+
drawingBufferHeight = 600,
|
|
90
|
+
contextAttributes = { stencil: true }
|
|
91
|
+
} = options;
|
|
92
|
+
const DATA_PROPERTIES = {
|
|
93
|
+
drawingBufferWidth,
|
|
94
|
+
drawingBufferHeight
|
|
95
|
+
};
|
|
96
|
+
const handler = (name) => (...args) => {
|
|
97
|
+
let result = void 0;
|
|
98
|
+
switch (name) {
|
|
99
|
+
case "createShader":
|
|
100
|
+
case "createProgram":
|
|
101
|
+
case "createBuffer":
|
|
102
|
+
case "createVertexArray":
|
|
103
|
+
case "createTexture":
|
|
104
|
+
case "createFramebuffer":
|
|
105
|
+
case "createRenderbuffer":
|
|
106
|
+
result = syntheticHandle(name, calls.length);
|
|
107
|
+
break;
|
|
108
|
+
case "getUniformLocation":
|
|
109
|
+
case "getAttribLocation":
|
|
110
|
+
result = calls.length;
|
|
111
|
+
break;
|
|
112
|
+
case "getShaderParameter":
|
|
113
|
+
case "getProgramParameter":
|
|
114
|
+
result = true;
|
|
115
|
+
break;
|
|
116
|
+
case "getShaderInfoLog":
|
|
117
|
+
case "getProgramInfoLog":
|
|
118
|
+
result = "";
|
|
119
|
+
break;
|
|
120
|
+
case "getContextAttributes":
|
|
121
|
+
result = { ...contextAttributes };
|
|
122
|
+
break;
|
|
123
|
+
case "getError":
|
|
124
|
+
result = 0;
|
|
125
|
+
break;
|
|
126
|
+
default:
|
|
127
|
+
result = void 0;
|
|
128
|
+
}
|
|
129
|
+
calls.push({ name, args: args.map(snapshot), result });
|
|
130
|
+
return result;
|
|
131
|
+
};
|
|
132
|
+
const gl = new Proxy(
|
|
133
|
+
{},
|
|
134
|
+
{
|
|
135
|
+
get(_, prop) {
|
|
136
|
+
if (typeof prop !== "string") return void 0;
|
|
137
|
+
if (prop in DATA_PROPERTIES) return DATA_PROPERTIES[prop];
|
|
138
|
+
if (prop in GL_CONSTANTS) return GL_CONSTANTS[prop];
|
|
139
|
+
if (/^[A-Z_0-9]+$/.test(prop)) return 0;
|
|
140
|
+
return handler(prop);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
return {
|
|
145
|
+
gl,
|
|
146
|
+
calls,
|
|
147
|
+
reset() {
|
|
148
|
+
calls.length = 0;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { makeGLRecorder };
|
|
154
|
+
//# sourceMappingURL=glRecorder.js.map
|
|
155
|
+
//# sourceMappingURL=glRecorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/glRecorder.ts"],"names":[],"mappings":"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","file":"glRecorder.js","sourcesContent":["/**\n * Proxy-based recording WebGL2 context for unit tests.\n *\n * Records every method call as { name, args, result } so tests can assert\n * against the call sequence. Returns synthetic handles for object creation\n * (createShader/Program/Buffer/etc.); reports truthy for *Parameter status\n * queries (i.e. assumes shader/program compilation succeeded).\n *\n * NOT a renderer correctness check — pixel-level correctness is verified\n * end-to-end by the smoke test in dev/smoke.spec.ts. This recorder catches\n * \"did the dispatcher emit the right call sequence\" bugs only.\n */\n\nexport interface GLCall {\n readonly name: string;\n readonly args: readonly unknown[];\n readonly result: unknown;\n}\n\n/** What the recorded context reports about itself. */\nexport interface GLRecorderOptions {\n /** Reported as `gl.drawingBufferWidth`. Default 800. */\n drawingBufferWidth?: number;\n /** Reported as `gl.drawingBufferHeight`. Default 600. */\n drawingBufferHeight?: number;\n /** Returned by `gl.getContextAttributes()`. Default `{ stencil: true }`. */\n contextAttributes?: Partial<WebGLContextAttributes>;\n}\n\n/** A stand-in context and the calls made on it, oldest first. */\nexport interface GLRecorder {\n readonly gl: WebGL2RenderingContext;\n readonly calls: GLCall[];\n reset(): void;\n}\n\nconst GL_CONSTANTS: Readonly<Record<string, number>> = {\n // Shader / program\n VERTEX_SHADER: 0x8B31,\n FRAGMENT_SHADER: 0x8B30,\n COMPILE_STATUS: 0x8B81,\n LINK_STATUS: 0x8B82,\n // Buffer targets\n ARRAY_BUFFER: 0x8892,\n ELEMENT_ARRAY_BUFFER: 0x8893,\n STATIC_DRAW: 0x88E4,\n // Types\n FLOAT: 0x1406,\n UNSIGNED_INT: 0x1405,\n // Primitives\n TRIANGLES: 0x0004,\n // Errors / state\n NO_ERROR: 0,\n DEPTH_TEST: 0x0B71,\n BLEND: 0x0BE2,\n STENCIL_TEST: 0x0B90,\n CULL_FACE: 0x0B44,\n UNPACK_FLIP_Y_WEBGL: 0x9240,\n SCISSOR_TEST: 0x0C11,\n MAX_TEXTURE_SIZE: 0x0D33,\n // Blend factors\n SRC_ALPHA: 0x0302,\n ONE_MINUS_SRC_ALPHA: 0x0303,\n ZERO: 0,\n ONE: 1,\n // Buffer bits\n COLOR_BUFFER_BIT: 0x00004000,\n STENCIL_BUFFER_BIT: 0x00000400,\n DEPTH_BUFFER_BIT: 0x00000100,\n // Stencil ops\n KEEP: 0x1E00,\n REPLACE: 0x1E01,\n INVERT: 0x150A,\n // Compare funcs\n ALWAYS: 0x0207,\n NEVER: 0x0200,\n EQUAL: 0x0202,\n NOTEQUAL: 0x0205,\n // Color masks\n FRONT_AND_BACK: 0x0408,\n // Misc constants used in step 1\n COLOR_WRITEMASK: 0x0C23,\n // Textures (step 3)\n TEXTURE_2D: 0x0DE1,\n TEXTURE0: 0x84C0,\n RGBA: 0x1908,\n UNSIGNED_BYTE: 0x1401,\n TEXTURE_MIN_FILTER: 0x2801,\n TEXTURE_MAG_FILTER: 0x2800,\n LINEAR: 0x2601,\n NEAREST: 0x2600,\n TEXTURE_WRAP_S: 0x2802,\n TEXTURE_WRAP_T: 0x2803,\n CLAMP_TO_EDGE: 0x812F,\n // Wrap modes (step 4)\n REPEAT: 0x2901,\n MIRRORED_REPEAT: 0x8370,\n // Mipmap minification filter\n LINEAR_MIPMAP_LINEAR: 0x2703,\n // Framebuffers and renderbuffers (offscreen effect passes). Every one of\n // these has to be here before a test names it: an absent ALL-CAPS property\n // reads as 0 through the proxy, so `expect(args[0]).toBe(gl.FRAMEBUFFER)`\n // would compare 0 against 0 and pass over a renderer that bound nothing.\n FRAMEBUFFER: 0x8D40,\n RENDERBUFFER: 0x8D41,\n FRAMEBUFFER_BINDING: 0x8CA6,\n RENDERBUFFER_BINDING: 0x8CA7,\n COLOR_ATTACHMENT0: 0x8CE0,\n DEPTH_STENCIL_ATTACHMENT: 0x821A,\n DEPTH24_STENCIL8: 0x88F0,\n RGBA8: 0x8058,\n FRAMEBUFFER_COMPLETE: 0x8CD5,\n};\n\nfunction syntheticHandle(name: string, seq: number): { __id: string } {\n return { __id: `${name}_${seq}` };\n}\n\n/**\n * Copy typed-array arguments as they are recorded.\n *\n * A caller is entitled to reuse the array it uploads from — the text ring\n * packs every draw through one scratch buffer — so storing the reference\n * records a value that later frames overwrite. A test reading it back then\n * compares two frames and sees the same numbers twice, which passes.\n */\nfunction snapshot(value: unknown): unknown {\n return ArrayBuffer.isView(value) && !(value instanceof DataView)\n ? (value as unknown as { slice(): unknown }).slice()\n : value;\n}\n\n/** A WebGL2 stand-in that records every call and draws nothing. */\nexport function makeGLRecorder(options: GLRecorderOptions = {}): GLRecorder {\n const calls: GLCall[] = [];\n const {\n drawingBufferWidth = 800,\n drawingBufferHeight = 600,\n contextAttributes = { stencil: true },\n } = options;\n\n // Read as data, not called. The Proxy below hands back a recording function\n // for every lowercase property, so without this `drawingBufferHeight` is a\n // function and every y-flip computed from it is NaN — silently.\n const DATA_PROPERTIES: Readonly<Record<string, unknown>> = {\n drawingBufferWidth,\n drawingBufferHeight,\n };\n\n const handler = (name: string) => (...args: unknown[]) => {\n let result: unknown = undefined;\n switch (name) {\n case 'createShader':\n case 'createProgram':\n case 'createBuffer':\n case 'createVertexArray':\n case 'createTexture':\n case 'createFramebuffer':\n case 'createRenderbuffer':\n result = syntheticHandle(name, calls.length);\n break;\n case 'getUniformLocation':\n case 'getAttribLocation':\n result = calls.length;\n break;\n case 'getShaderParameter':\n case 'getProgramParameter':\n result = true;\n break;\n case 'getShaderInfoLog':\n case 'getProgramInfoLog':\n result = '';\n break;\n case 'getContextAttributes':\n result = { ...contextAttributes };\n break;\n case 'getError':\n result = 0;\n break;\n default:\n result = undefined;\n }\n calls.push({ name, args: args.map(snapshot), result });\n return result;\n };\n\n const gl = new Proxy(\n {},\n {\n get(_, prop: string | symbol) {\n if (typeof prop !== 'string') return undefined;\n if (prop in DATA_PROPERTIES) return DATA_PROPERTIES[prop];\n if (prop in GL_CONSTANTS) return GL_CONSTANTS[prop];\n if (/^[A-Z_0-9]+$/.test(prop)) return 0; // unknown all-caps constant → 0\n return handler(prop);\n },\n },\n ) as unknown as WebGL2RenderingContext;\n\n return {\n gl,\n calls,\n reset() {\n calls.length = 0;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GLRecorder } from './glRecorder.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read vertex geometry back out of a `GLRecorder`, with each upload's
|
|
5
|
+
* floats-per-vertex taken from the `vertexAttribPointer` recorded for that same
|
|
6
|
+
* buffer object.
|
|
7
|
+
*
|
|
8
|
+
* Pairing an upload with the pointer call next to it used to work because every
|
|
9
|
+
* draw minted its own VAO and buffers. The text paths now take a slot from a
|
|
10
|
+
* ring that configured its attributes once, so a frame after the first records
|
|
11
|
+
* no pointer calls at all — hence a tracker that remembers what it has learned
|
|
12
|
+
* rather than a function over one frame's calls. Ask it for `uploads()` after
|
|
13
|
+
* each frame and it carries the strides across `recorder.reset()`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** One vertex buffer upload. */
|
|
17
|
+
interface VertexUpload {
|
|
18
|
+
/** Floats per vertex. */
|
|
19
|
+
stride: number;
|
|
20
|
+
data: Float32Array;
|
|
21
|
+
}
|
|
22
|
+
/** Reads vertex uploads out of a recorder, remembering each buffer's stride. */
|
|
23
|
+
interface UploadTracker {
|
|
24
|
+
/** Every vertex upload recorded since the last `recorder.reset()`. */
|
|
25
|
+
uploads(): VertexUpload[];
|
|
26
|
+
}
|
|
27
|
+
/** A tracker over `recorder`; keep one across frames. */
|
|
28
|
+
declare function makeUploadTracker(recorder: GLRecorder): UploadTracker;
|
|
29
|
+
|
|
30
|
+
export { type UploadTracker, type VertexUpload, makeUploadTracker };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/testing/vertexUploads.ts
|
|
2
|
+
var ARRAY_BUFFER = 34962;
|
|
3
|
+
function payload(call) {
|
|
4
|
+
const data = call.name === "bufferData" ? call.args[1] : call.args[2];
|
|
5
|
+
if (!(data instanceof Float32Array)) return null;
|
|
6
|
+
if (call.name !== "bufferSubData") return data;
|
|
7
|
+
const srcOffset = typeof call.args[3] === "number" ? call.args[3] : 0;
|
|
8
|
+
const length = typeof call.args[4] === "number" ? call.args[4] : data.length - srcOffset;
|
|
9
|
+
return data.subarray(srcOffset, srcOffset + length);
|
|
10
|
+
}
|
|
11
|
+
function makeUploadTracker(recorder) {
|
|
12
|
+
const strides = /* @__PURE__ */ new Map();
|
|
13
|
+
return {
|
|
14
|
+
uploads() {
|
|
15
|
+
const out = [];
|
|
16
|
+
const pending = [];
|
|
17
|
+
let bound = null;
|
|
18
|
+
for (const call of recorder.calls) {
|
|
19
|
+
if (call.name === "bindBuffer") {
|
|
20
|
+
if (call.args[0] === ARRAY_BUFFER) bound = call.args[1];
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (call.name === "vertexAttribPointer") {
|
|
24
|
+
const bytes = call.args[4];
|
|
25
|
+
strides.set(bound, bytes === 0 ? 2 : bytes / 4);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (call.name !== "bufferData" && call.name !== "bufferSubData") continue;
|
|
29
|
+
const data = payload(call);
|
|
30
|
+
if (data) pending.push({ buffer: bound, data });
|
|
31
|
+
}
|
|
32
|
+
for (const { buffer, data } of pending) {
|
|
33
|
+
const stride = strides.get(buffer);
|
|
34
|
+
if (stride === void 0) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"vertexUploads: a vertex buffer was written with no vertexAttribPointer ever recorded for it \u2014 call uploads() on the frame that creates the buffer, before resetting the recorder."
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
out.push({ stride, data });
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { makeUploadTracker };
|
|
47
|
+
//# sourceMappingURL=vertexUploads.js.map
|
|
48
|
+
//# sourceMappingURL=vertexUploads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/vertexUploads.ts"],"names":[],"mappings":";AAeA,IAAM,YAAA,GAAe,KAAA;AAwBrB,SAAS,QAAQ,IAAA,EAAmC;AAClD,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,KAAS,YAAA,GAAe,IAAA,CAAK,KAAK,CAAC,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AACpE,EAAA,IAAI,EAAE,IAAA,YAAgB,YAAA,CAAA,EAAe,OAAO,IAAA;AAC5C,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,eAAA,EAAiB,OAAO,IAAA;AAC1C,EAAA,MAAM,SAAA,GAAY,OAAO,IAAA,CAAK,IAAA,CAAK,CAAC,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,GAAI,CAAA;AACpE,EAAA,MAAM,MAAA,GAAS,OAAO,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,KAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,GAAI,IAAA,CAAK,MAAA,GAAS,SAAA;AAC/E,EAAA,OAAO,IAAA,CAAK,QAAA,CAAS,SAAA,EAAW,SAAA,GAAY,MAAM,CAAA;AACpD;AAGO,SAAS,kBAAkB,QAAA,EAAqC;AAErE,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAqB;AAEzC,EAAA,OAAO;AAAA,IACL,OAAA,GAA0B;AACxB,MAAA,MAAM,MAAsB,EAAC;AAC7B,MAAA,MAAM,UAAqD,EAAC;AAC5D,MAAA,IAAI,KAAA,GAAiB,IAAA;AAIrB,MAAA,KAAA,MAAW,IAAA,IAAQ,SAAS,KAAA,EAAO;AACjC,QAAA,IAAI,IAAA,CAAK,SAAS,YAAA,EAAc;AAC9B,UAAA,IAAI,IAAA,CAAK,KAAK,CAAC,CAAA,KAAM,cAAc,KAAA,GAAQ,IAAA,CAAK,KAAK,CAAC,CAAA;AACtD,UAAA;AAAA,QACF;AACA,QAAA,IAAI,IAAA,CAAK,SAAS,qBAAA,EAAuB;AAEvC,UAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AACzB,UAAA,OAAA,CAAQ,IAAI,KAAA,EAAO,KAAA,KAAU,CAAA,GAAI,CAAA,GAAI,QAAQ,CAAC,CAAA;AAC9C,UAAA;AAAA,QACF;AACA,QAAA,IAAI,IAAA,CAAK,IAAA,KAAS,YAAA,IAAgB,IAAA,CAAK,SAAS,eAAA,EAAiB;AACjE,QAAA,MAAM,IAAA,GAAO,QAAQ,IAAI,CAAA;AACzB,QAAA,IAAI,MAAM,OAAA,CAAQ,IAAA,CAAK,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAM,CAAA;AAAA,MAChD;AAEA,MAAA,KAAA,MAAW,EAAE,MAAA,EAAQ,IAAA,EAAK,IAAK,OAAA,EAAS;AACtC,QAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACjC,QAAA,IAAI,WAAW,MAAA,EAAW;AACxB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WAGF;AAAA,QACF;AACA,QAAA,GAAA,CAAI,IAAA,CAAK,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,MAC3B;AACA,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,GACF;AACF","file":"vertexUploads.js","sourcesContent":["/**\n * Read vertex geometry back out of a `GLRecorder`, with each upload's\n * floats-per-vertex taken from the `vertexAttribPointer` recorded for that same\n * buffer object.\n *\n * Pairing an upload with the pointer call next to it used to work because every\n * draw minted its own VAO and buffers. The text paths now take a slot from a\n * ring that configured its attributes once, so a frame after the first records\n * no pointer calls at all — hence a tracker that remembers what it has learned\n * rather than a function over one frame's calls. Ask it for `uploads()` after\n * each frame and it carries the strides across `recorder.reset()`.\n */\n\nimport type { GLCall, GLRecorder } from './glRecorder';\n\nconst ARRAY_BUFFER = 0x8892;\n\n/** One vertex buffer upload. */\nexport interface VertexUpload {\n /** Floats per vertex. */\n stride: number;\n data: Float32Array;\n}\n\n/** Reads vertex uploads out of a recorder, remembering each buffer's stride. */\nexport interface UploadTracker {\n /** Every vertex upload recorded since the last `recorder.reset()`. */\n uploads(): VertexUpload[];\n}\n\n/**\n * The floats an upload actually sent.\n *\n * `bufferSubData(target, offset, src, srcOffset, length)` hands GL a *window*\n * into a scratch array the batch reuses across frames, so the recorded\n * argument is the whole scratch — the tail of it being whatever the last, and\n * possibly longer, run left there. Reading it whole compares live vertices\n * against stale ones.\n */\nfunction payload(call: GLCall): Float32Array | null {\n const data = call.name === 'bufferData' ? call.args[1] : call.args[2];\n if (!(data instanceof Float32Array)) return null;\n if (call.name !== 'bufferSubData') return data;\n const srcOffset = typeof call.args[3] === 'number' ? call.args[3] : 0;\n const length = typeof call.args[4] === 'number' ? call.args[4] : data.length - srcOffset;\n return data.subarray(srcOffset, srcOffset + length);\n}\n\n/** A tracker over `recorder`; keep one across frames. */\nexport function makeUploadTracker(recorder: GLRecorder): UploadTracker {\n // Keyed on the recorder's synthetic buffer handle, which is a stable object.\n const strides = new Map<unknown, number>();\n\n return {\n uploads(): VertexUpload[] {\n const out: VertexUpload[] = [];\n const pending: { buffer: unknown; data: Float32Array }[] = [];\n let bound: unknown = null;\n\n // Two passes: a buffer's attribute layout can be recorded either before\n // its first upload (the ring) or after it (every other draw path).\n for (const call of recorder.calls) {\n if (call.name === 'bindBuffer') {\n if (call.args[0] === ARRAY_BUFFER) bound = call.args[1];\n continue;\n }\n if (call.name === 'vertexAttribPointer') {\n // Byte stride; 0 means tightly packed, which for a_position is a vec2.\n const bytes = call.args[4] as number;\n strides.set(bound, bytes === 0 ? 2 : bytes / 4);\n continue;\n }\n if (call.name !== 'bufferData' && call.name !== 'bufferSubData') continue;\n const data = payload(call);\n if (data) pending.push({ buffer: bound, data });\n }\n\n for (const { buffer, data } of pending) {\n const stride = strides.get(buffer);\n if (stride === undefined) {\n throw new Error(\n 'vertexUploads: a vertex buffer was written with no vertexAttribPointer ' +\n 'ever recorded for it — call uploads() on the frame that creates the ' +\n 'buffer, before resetting the recorder.',\n );\n }\n out.push({ stride, data });\n }\n return out;\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,73 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/renderer",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.9.4",
|
|
4
|
+
"description": "A WebGL2 renderer for draw commands: batched path fills and strokes, MSDF and outline text, images and sprites, gradients, patterns, registered shader programs, and full-screen effects. No scene graph, no React.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./mesh": {
|
|
17
|
+
"import": "./dist/mesh.js",
|
|
18
|
+
"types": "./dist/mesh.d.ts"
|
|
19
|
+
},
|
|
20
|
+
"./patterns-builtin": {
|
|
21
|
+
"import": "./dist/patterns-builtin.js",
|
|
22
|
+
"types": "./dist/patterns-builtin.d.ts"
|
|
23
|
+
},
|
|
24
|
+
"./testing/glRecorder": {
|
|
25
|
+
"import": "./dist/testing/glRecorder.js",
|
|
26
|
+
"types": "./dist/testing/glRecorder.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./testing/vertexUploads": {
|
|
29
|
+
"import": "./dist/testing/vertexUploads.js",
|
|
30
|
+
"types": "./dist/testing/vertexUploads.d.ts"
|
|
31
|
+
},
|
|
32
|
+
"./test-seams": {
|
|
33
|
+
"import": "./dist/test-seams.js",
|
|
34
|
+
"types": "./dist/test-seams.d.ts"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@weasel-js/geom": "1.9.4",
|
|
40
|
+
"@weasel-js/paint": "1.9.4",
|
|
41
|
+
"@weasel-js/registry": "1.9.4",
|
|
42
|
+
"@weasel-js/stroke": "1.9.4",
|
|
43
|
+
"@weasel-js/text": "1.9.4"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@weasel-js/font": "1.9.4"
|
|
47
|
+
},
|
|
48
|
+
"author": "orochi235",
|
|
49
|
+
"homepage": "https://orochi235.github.io/weasel/",
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/orochi235/weasel.git",
|
|
53
|
+
"directory": "packages/renderer"
|
|
54
|
+
},
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/orochi235/weasel/issues"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=22"
|
|
60
|
+
},
|
|
61
|
+
"files": [
|
|
62
|
+
"dist",
|
|
63
|
+
"README.md",
|
|
64
|
+
"LICENSE"
|
|
65
|
+
],
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsup"
|
|
68
|
+
},
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public",
|
|
71
|
+
"provenance": true
|
|
72
|
+
}
|
|
73
|
+
}
|