vitest-environment-webgpu-node 3.0.1 → 3.0.2
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/README.md +34 -28
- package/dist/canvas.d.ts +86 -3
- package/dist/canvas.d.ts.map +1 -1
- package/dist/canvas.js +62 -3
- package/dist/canvas.js.map +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
- package/src/canvas.ts +94 -4
- package/src/index.ts +22 -1
package/README.md
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://npmjs.com/package/vitest-environment-webgpu-node"><img src="https://img.shields.io/npm/v/vitest-environment-webgpu-node.svg" alt="npm package"></a>
|
|
3
|
+
<a href="https://www.npmtrends.com/vitest-environment-webgpu-node"><img src="https://img.shields.io/npm/dw/vitest-environment-webgpu-node.svg" alt="npm downloads"></a>
|
|
4
|
+
<a href="https://codecov.io/gh/bhouston/vitest-gpu"><img src="https://codecov.io/gh/bhouston/vitest-gpu/branch/main/graph/badge.svg" alt="coverage"></a>
|
|
5
|
+
<a href="https://discord.gg/fwupDN493R"><img src="https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord" alt="discord chat"></a>
|
|
6
|
+
<a href="https://vitest-gpu.ben3d.ca/"><img src="https://img.shields.io/badge/docs-read-blue?style=flat" alt="documentation"></a>
|
|
7
|
+
</p>
|
|
8
|
+
<br/>
|
|
9
|
+
|
|
1
10
|
# vitest-environment-webgpu-node
|
|
2
11
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
> Real WebGPU tests in Vitest, with no browser.
|
|
13
|
+
|
|
14
|
+
- 🖥️ Real WebGPU via Dawn
|
|
15
|
+
- 🚫 No Browser or Mocks
|
|
16
|
+
- 🧩 Headless Canvas Included
|
|
17
|
+
- 🔧 Dawn Backend Options
|
|
18
|
+
- ⚡ Up to 2.4x Faster Than Browsers
|
|
19
|
+
|
|
20
|
+
Headless GPU testing for CI: `navigator.gpu` is backed by Google's Dawn through the
|
|
21
|
+
[`webgpu`](https://github.com/dawn-gpu/node-webgpu) npm package. Native GPU testing is up to 2.4x faster than
|
|
22
|
+
running the same tests in a browser, see the [blog post](https://ben3d.ca/blog/native-gpu-testing-for-vitest-and-jest).
|
|
23
|
+
|
|
24
|
+
Part of [vitest-gpu](https://github.com/bhouston/vitest-gpu), which also provides WebGL and image snapshot packages.
|
|
8
25
|
|
|
9
|
-
|
|
10
|
-
for CI: `navigator.gpu` is backed by Google's Dawn through the
|
|
11
|
-
[`webgpu`](https://github.com/dawn-gpu/node-webgpu) npm package. Native GPU testing is up to 2.4x faster
|
|
12
|
-
than running the same tests in a browser, see the
|
|
13
|
-
[blog post](https://ben3d.ca/blog/native-gpu-testing-for-vitest-and-jest).
|
|
26
|
+
[Read the Docs to Learn More](https://vitest-gpu.ben3d.ca/docs/webgpu/) about the WebGPU environment: Dawn options, TypeScript types, the headless canvas and CI setup.
|
|
14
27
|
|
|
15
28
|
```sh
|
|
16
29
|
pnpm add -D vitest vitest-environment-webgpu-node
|
|
@@ -40,7 +53,7 @@ The environment puts `navigator.gpu` and every `GPU*` class and constant (`GPUBu
|
|
|
40
53
|
|
|
41
54
|
Using Jest instead? See the equivalent
|
|
42
55
|
[`jest-environment-webgpu-node`](https://github.com/bhouston/jest-gpu/tree/main/packages/jest-environment-webgpu-node)
|
|
43
|
-
in [jest-gpu](https://github.com/bhouston/jest-gpu).
|
|
56
|
+
in [jest-gpu](https://github.com/bhouston/jest-gpu), documented at [jest-gpu.ben3d.ca](https://jest-gpu.ben3d.ca/).
|
|
44
57
|
|
|
45
58
|
## Compared to alternatives
|
|
46
59
|
|
|
@@ -56,10 +69,9 @@ Dawn in Node.
|
|
|
56
69
|
## Supported versions and platforms
|
|
57
70
|
|
|
58
71
|
- Vitest 4 and 5 (CI tests both). Vitest 3 is not supported.
|
|
59
|
-
- Node 22 or later. CI runs Node 22 and 26 on `macos-latest` and `
|
|
72
|
+
- Node 22 or later. CI runs Node 22 and 26 on `macos-latest`, `ubuntu-latest` and `windows-latest`.
|
|
60
73
|
- Type checking needs TypeScript 6 or later (see [TypeScript types](#typescript-types)).
|
|
61
|
-
- Linux without a GPU needs Mesa's software Vulkan driver, see [Linux and CI](#linux-and-ci).
|
|
62
|
-
not run on Windows.
|
|
74
|
+
- Linux without a GPU needs Mesa's software Vulkan driver, see [Linux and CI](#linux-and-ci).
|
|
63
75
|
|
|
64
76
|
## Useful for
|
|
65
77
|
|
|
@@ -159,24 +171,18 @@ Call `extendMatchers()` from it once, then pass the canvas as in the example abo
|
|
|
159
171
|
- [`demo/`](https://github.com/bhouston/vitest-gpu/tree/main/demo/test/webgpu) for device, upload, readback, triangle, three.js, Babylon Lite and vgpu tests,
|
|
160
172
|
and the [repository README](https://github.com/bhouston/vitest-gpu#readme).
|
|
161
173
|
|
|
162
|
-
##
|
|
174
|
+
## Resource cleanup
|
|
163
175
|
|
|
164
|
-
|
|
176
|
+
Environment teardown destroys devices requested through its `navigator.gpu` and cancels pending callbacks scheduled through its animation-frame shim. Device requests that finish after teardown are destroyed and rejected. Devices created through another GPU instance remain the caller's responsibility. Keep disposing renderers, buffers, textures and devices during long-running files to release resources sooner.
|
|
177
|
+
|
|
178
|
+
## Contribution
|
|
179
|
+
|
|
180
|
+
See [Contributing Guide](https://github.com/bhouston/vitest-gpu/blob/main/CONTRIBUTING.md).
|
|
165
181
|
|
|
166
182
|
## License
|
|
167
183
|
|
|
168
184
|
MIT
|
|
169
185
|
|
|
170
|
-
|
|
171
|
-
[npm-url]: https://www.npmjs.com/package/vitest-environment-webgpu-node
|
|
172
|
-
[downloads-badge]: https://img.shields.io/npm/dm/vitest-environment-webgpu-node.svg
|
|
173
|
-
[tests-badge]: https://github.com/bhouston/vitest-gpu/actions/workflows/ci.yml/badge.svg
|
|
174
|
-
[tests-url]: https://github.com/bhouston/vitest-gpu/actions/workflows/ci.yml
|
|
175
|
-
[discord-badge]: https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white
|
|
176
|
-
[discord-url]: https://discord.gg/fwupDN493R
|
|
177
|
-
[coverage-badge]: https://codecov.io/gh/bhouston/vitest-gpu/branch/main/graph/badge.svg
|
|
178
|
-
[coverage-url]: https://codecov.io/gh/bhouston/vitest-gpu
|
|
179
|
-
|
|
180
|
-
## Resource cleanup
|
|
186
|
+
## Author
|
|
181
187
|
|
|
182
|
-
|
|
188
|
+
[Ben Houston](https://ben3d.ca), Sponsored by [Land of Assets](https://landofassets.com).
|
package/dist/canvas.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/** RGBA8 pixels, top row first. Feed it to `vitest-screenshot`'s `toMatchScreenshot`. */
|
|
2
2
|
export type RgbaImage = {
|
|
3
|
+
/** Width in pixels. */
|
|
3
4
|
width: number;
|
|
5
|
+
/** Height in pixels. */
|
|
4
6
|
height: number;
|
|
7
|
+
/** RGBA8 bytes, four per pixel, row by row from the top. */
|
|
5
8
|
data: Uint8Array;
|
|
6
9
|
};
|
|
7
10
|
type DefaultCanvasElement = typeof globalThis extends {
|
|
@@ -12,11 +15,30 @@ type DefaultCanvasElement = typeof globalThis extends {
|
|
|
12
15
|
/** Minimal `GPUCanvasContext`: `getCurrentTexture()` is a texture the size of the canvas, kept until reconfigured or resized. */
|
|
13
16
|
export declare class HeadlessCanvasContext {
|
|
14
17
|
#private;
|
|
18
|
+
/** The canvas this context belongs to. */
|
|
15
19
|
readonly canvas: HeadlessCanvas;
|
|
16
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Creates the context for a canvas. Canvases create their own; you rarely call this.
|
|
22
|
+
*
|
|
23
|
+
* @param canvas - The canvas whose size this context's texture follows.
|
|
24
|
+
*/
|
|
25
|
+
constructor(
|
|
26
|
+
/** The canvas this context belongs to. */
|
|
27
|
+
canvas: HeadlessCanvas);
|
|
28
|
+
/**
|
|
29
|
+
* Stores the configuration and discards any texture created under the previous one, as `GPUCanvasContext.configure()` does.
|
|
30
|
+
*
|
|
31
|
+
* @param config - Device, format and usage for the canvas texture.
|
|
32
|
+
*/
|
|
17
33
|
configure(config: GPUCanvasConfiguration): void;
|
|
34
|
+
/** Clears the configuration and discards the current texture. */
|
|
18
35
|
unconfigure(): void;
|
|
36
|
+
/** The configuration passed to `configure()`, or `null` before it is called or after `unconfigure()`. */
|
|
19
37
|
getConfiguration(): GPUCanvasConfiguration | null;
|
|
38
|
+
/**
|
|
39
|
+
* The texture the canvas renders into. It is created on first use at the canvas size and reused until the context
|
|
40
|
+
* is reconfigured or the canvas is resized. Throws if `configure()` has not been called.
|
|
41
|
+
*/
|
|
20
42
|
getCurrentTexture(): GPUTexture;
|
|
21
43
|
/** Copies the current texture to the CPU. BGRA formats are swizzled to RGBA. */
|
|
22
44
|
readPixels(): Promise<RgbaImage>;
|
|
@@ -24,31 +46,92 @@ export declare class HeadlessCanvasContext {
|
|
|
24
46
|
/** Enough of `HTMLCanvasElement` for three.js `WebGPURenderer`, Babylon Lite and friends. */
|
|
25
47
|
export declare class HeadlessCanvas {
|
|
26
48
|
#private;
|
|
49
|
+
/** Canvas width in pixels. */
|
|
27
50
|
width: number;
|
|
51
|
+
/** Canvas height in pixels. */
|
|
28
52
|
height: number;
|
|
53
|
+
/** A plain object libraries can write CSS properties to; it has no effect on rendering. */
|
|
29
54
|
readonly style: Record<string, string>;
|
|
30
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Creates a canvas of the given size. {@link createCanvas} is the usual entry point.
|
|
57
|
+
*
|
|
58
|
+
* @param width - Canvas width in pixels.
|
|
59
|
+
* @param height - Canvas height in pixels.
|
|
60
|
+
*/
|
|
61
|
+
constructor(
|
|
62
|
+
/** Canvas width in pixels. */
|
|
63
|
+
width?: number,
|
|
64
|
+
/** Canvas height in pixels. */
|
|
65
|
+
height?: number);
|
|
66
|
+
/** Same as `width`; there is no layout in Node. */
|
|
31
67
|
get clientWidth(): number;
|
|
68
|
+
/** Same as `height`; there is no layout in Node. */
|
|
32
69
|
get clientHeight(): number;
|
|
70
|
+
/**
|
|
71
|
+
* Returns the canvas's `GPUCanvasContext` for `'webgpu'`.
|
|
72
|
+
*
|
|
73
|
+
* @param id - The context type. Only `'webgpu'` is supported.
|
|
74
|
+
* @returns The context for `'webgpu'`, or `null` for any other type.
|
|
75
|
+
*/
|
|
33
76
|
getContext(id: 'webgpu'): HeadlessCanvasContext;
|
|
77
|
+
/**
|
|
78
|
+
* Returns the canvas's `GPUCanvasContext` for `'webgpu'`.
|
|
79
|
+
*
|
|
80
|
+
* @param id - The context type. Only `'webgpu'` is supported.
|
|
81
|
+
* @returns The context for `'webgpu'`, or `null` for any other type.
|
|
82
|
+
*/
|
|
34
83
|
getContext(id: string): HeadlessCanvasContext | null;
|
|
84
|
+
/** Reads the current texture back as RGBA8 pixels; see {@link HeadlessCanvasContext.readPixels}. */
|
|
35
85
|
readPixels(): Promise<RgbaImage>;
|
|
36
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* This same object, cast to the element type a rendering library expects.
|
|
88
|
+
*
|
|
89
|
+
* @typeParam TElement - The element type to cast to. Defaults to `HTMLCanvasElement` when DOM types are available.
|
|
90
|
+
*/
|
|
37
91
|
asElement<TElement = DefaultCanvasElement>(): TElement;
|
|
92
|
+
/** A rectangle at the origin with the canvas's width and height. */
|
|
38
93
|
getBoundingClientRect(): {
|
|
94
|
+
/** Always 0. */
|
|
39
95
|
x: number;
|
|
96
|
+
/** Always 0. */
|
|
40
97
|
y: number;
|
|
98
|
+
/** The canvas width. */
|
|
41
99
|
width: number;
|
|
100
|
+
/** The canvas height. */
|
|
42
101
|
height: number;
|
|
102
|
+
/** Always 0. */
|
|
43
103
|
top: number;
|
|
104
|
+
/** Always 0. */
|
|
44
105
|
left: number;
|
|
45
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Stores an attribute on the canvas.
|
|
109
|
+
*
|
|
110
|
+
* @param name - Attribute name.
|
|
111
|
+
* @param value - Attribute value.
|
|
112
|
+
*/
|
|
46
113
|
setAttribute(name: string, value: string): void;
|
|
114
|
+
/**
|
|
115
|
+
* Reads an attribute stored with `setAttribute()`.
|
|
116
|
+
*
|
|
117
|
+
* @param name - Attribute name.
|
|
118
|
+
* @returns The stored value, or `null` if none was set.
|
|
119
|
+
*/
|
|
47
120
|
getAttribute(name: string): string | null;
|
|
121
|
+
/** Does nothing; present so libraries that register listeners keep working. */
|
|
48
122
|
addEventListener(): void;
|
|
123
|
+
/** Does nothing; present so libraries that remove listeners keep working. */
|
|
49
124
|
removeEventListener(): void;
|
|
125
|
+
/** Does nothing and returns `true`. */
|
|
50
126
|
dispatchEvent(): boolean;
|
|
51
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Creates a headless canvas for WebGPU tests.
|
|
130
|
+
*
|
|
131
|
+
* @param width - Canvas width in pixels.
|
|
132
|
+
* @param height - Canvas height in pixels.
|
|
133
|
+
* @returns A {@link HeadlessCanvas}; pass `canvas.asElement()` to libraries that expect an `HTMLCanvasElement`.
|
|
134
|
+
*/
|
|
52
135
|
export declare const createCanvas: (width: number, height: number) => HeadlessCanvas;
|
|
53
136
|
export {};
|
|
54
137
|
//# sourceMappingURL=canvas.d.ts.map
|
package/dist/canvas.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.d.ts","
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sources":["../src/canvas.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,MAAM,SAAS,GAAG;IACtB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,KAAK,oBAAoB,GAAG,OAAO,UAAU,SAAS;IACpD,iBAAiB,EAAE;QAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;KAAE,CAAC;CAClD,GACG,QAAQ,GACR,cAAc,CAAC;AAanB,iIAAiI;AACjI,qBAAa,qBAAqB;;IAU9B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,cAAc;IAPjC;;;;OAIG;IACH;IACE,0CAA0C;IACjC,MAAM,EAAE,cAAc,EAC7B;IAEJ;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAG9C;IAED,iEAAiE;IACjE,WAAW,IAAI,IAAI,CAGlB;IAED,yGAAyG;IACzG,gBAAgB,IAAI,sBAAsB,GAAG,IAAI,CAEhD;IAED;;;OAGG;IACH,iBAAiB,IAAI,UAAU,CAY9B;IAED,gFAAgF;IAC1E,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC,CAmCrC;CAMF;AAED,6FAA6F;AAC7F,qBAAa,cAAc;;IAavB,8BAA8B;IACvB,KAAK;IACZ,+BAA+B;IACxB,MAAM;IAdf,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAG5C;;;;;OAKG;IACH;IACE,8BAA8B;IACvB,KAAK,SAAM;IAClB,+BAA+B;IACxB,MAAM,SAAM,EACjB;IAEJ,mDAAmD;IACnD,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,oDAAoD;IACpD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;;;OAKG;IACH,UAAU,CAAC,EAAE,EAAE,QAAQ,GAAG,qBAAqB,CAAC;IAChD;;;;;OAKG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAAC;IAKrD,oGAAoG;IACpG,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC,CAE/B;IAED;;;;OAIG;IACH,SAAS,CAAC,QAAQ,GAAG,oBAAoB,KAAK,QAAQ,CAErD;IAED,oEAAoE;IACpE,qBAAqB,IAAI;QACvB,gBAAgB;QAChB,CAAC,EAAE,MAAM,CAAC;QACV,gBAAgB;QAChB,CAAC,EAAE,MAAM,CAAC;QACV,wBAAwB;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,yBAAyB;QACzB,MAAM,EAAE,MAAM,CAAC;QACf,gBAAgB;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,gBAAgB;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAEA;IAED;;;;;OAKG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAE9C;IAED;;;;;OAKG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAExC;IAED,+EAA+E;IAC/E,gBAAgB,IAAI,IAAI,CAAG;IAC3B,6EAA6E;IAC7E,mBAAmB,IAAI,IAAI,CAAG;IAC9B,uCAAuC;IACvC,aAAa,IAAI,OAAO,CAEvB;CACF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,UAAW,MAAM,UAAU,MAAM,KAAG,cAAmD,CAAC"}
|
package/dist/canvas.js
CHANGED
|
@@ -13,20 +13,38 @@ export class HeadlessCanvasContext {
|
|
|
13
13
|
canvas;
|
|
14
14
|
#config;
|
|
15
15
|
#texture;
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Creates the context for a canvas. Canvases create their own; you rarely call this.
|
|
18
|
+
*
|
|
19
|
+
* @param canvas - The canvas whose size this context's texture follows.
|
|
20
|
+
*/
|
|
21
|
+
constructor(
|
|
22
|
+
/** The canvas this context belongs to. */
|
|
23
|
+
canvas) {
|
|
17
24
|
this.canvas = canvas;
|
|
18
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Stores the configuration and discards any texture created under the previous one, as `GPUCanvasContext.configure()` does.
|
|
28
|
+
*
|
|
29
|
+
* @param config - Device, format and usage for the canvas texture.
|
|
30
|
+
*/
|
|
19
31
|
configure(config) {
|
|
20
32
|
this.#config = config;
|
|
21
33
|
this.#drop();
|
|
22
34
|
}
|
|
35
|
+
/** Clears the configuration and discards the current texture. */
|
|
23
36
|
unconfigure() {
|
|
24
37
|
this.#config = undefined;
|
|
25
38
|
this.#drop();
|
|
26
39
|
}
|
|
40
|
+
/** The configuration passed to `configure()`, or `null` before it is called or after `unconfigure()`. */
|
|
27
41
|
getConfiguration() {
|
|
28
42
|
return this.#config ?? null;
|
|
29
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The texture the canvas renders into. It is created on first use at the canvas size and reused until the context
|
|
46
|
+
* is reconfigured or the canvas is resized. Throws if `configure()` has not been called.
|
|
47
|
+
*/
|
|
30
48
|
getCurrentTexture() {
|
|
31
49
|
const config = this.#config;
|
|
32
50
|
if (!config)
|
|
@@ -92,42 +110,83 @@ export class HeadlessCanvas {
|
|
|
92
110
|
width;
|
|
93
111
|
height;
|
|
94
112
|
#context = new HeadlessCanvasContext(this);
|
|
113
|
+
/** A plain object libraries can write CSS properties to; it has no effect on rendering. */
|
|
95
114
|
style = {};
|
|
96
115
|
#attributes = new Map();
|
|
97
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Creates a canvas of the given size. {@link createCanvas} is the usual entry point.
|
|
118
|
+
*
|
|
119
|
+
* @param width - Canvas width in pixels.
|
|
120
|
+
* @param height - Canvas height in pixels.
|
|
121
|
+
*/
|
|
122
|
+
constructor(
|
|
123
|
+
/** Canvas width in pixels. */
|
|
124
|
+
width = 300,
|
|
125
|
+
/** Canvas height in pixels. */
|
|
126
|
+
height = 150) {
|
|
98
127
|
this.width = width;
|
|
99
128
|
this.height = height;
|
|
100
129
|
}
|
|
130
|
+
/** Same as `width`; there is no layout in Node. */
|
|
101
131
|
get clientWidth() {
|
|
102
132
|
return this.width;
|
|
103
133
|
}
|
|
134
|
+
/** Same as `height`; there is no layout in Node. */
|
|
104
135
|
get clientHeight() {
|
|
105
136
|
return this.height;
|
|
106
137
|
}
|
|
107
138
|
getContext(id) {
|
|
108
139
|
return id === 'webgpu' ? this.#context : null;
|
|
109
140
|
}
|
|
141
|
+
/** Reads the current texture back as RGBA8 pixels; see {@link HeadlessCanvasContext.readPixels}. */
|
|
110
142
|
readPixels() {
|
|
111
143
|
return this.#context.readPixels();
|
|
112
144
|
}
|
|
113
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* This same object, cast to the element type a rendering library expects.
|
|
147
|
+
*
|
|
148
|
+
* @typeParam TElement - The element type to cast to. Defaults to `HTMLCanvasElement` when DOM types are available.
|
|
149
|
+
*/
|
|
114
150
|
asElement() {
|
|
115
151
|
return this;
|
|
116
152
|
}
|
|
153
|
+
/** A rectangle at the origin with the canvas's width and height. */
|
|
117
154
|
getBoundingClientRect() {
|
|
118
155
|
return { x: 0, y: 0, top: 0, left: 0, width: this.width, height: this.height };
|
|
119
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Stores an attribute on the canvas.
|
|
159
|
+
*
|
|
160
|
+
* @param name - Attribute name.
|
|
161
|
+
* @param value - Attribute value.
|
|
162
|
+
*/
|
|
120
163
|
setAttribute(name, value) {
|
|
121
164
|
this.#attributes.set(name, value);
|
|
122
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Reads an attribute stored with `setAttribute()`.
|
|
168
|
+
*
|
|
169
|
+
* @param name - Attribute name.
|
|
170
|
+
* @returns The stored value, or `null` if none was set.
|
|
171
|
+
*/
|
|
123
172
|
getAttribute(name) {
|
|
124
173
|
return this.#attributes.get(name) ?? null;
|
|
125
174
|
}
|
|
175
|
+
/** Does nothing; present so libraries that register listeners keep working. */
|
|
126
176
|
addEventListener() { }
|
|
177
|
+
/** Does nothing; present so libraries that remove listeners keep working. */
|
|
127
178
|
removeEventListener() { }
|
|
179
|
+
/** Does nothing and returns `true`. */
|
|
128
180
|
dispatchEvent() {
|
|
129
181
|
return true;
|
|
130
182
|
}
|
|
131
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Creates a headless canvas for WebGPU tests.
|
|
186
|
+
*
|
|
187
|
+
* @param width - Canvas width in pixels.
|
|
188
|
+
* @param height - Canvas height in pixels.
|
|
189
|
+
* @returns A {@link HeadlessCanvas}; pass `canvas.asElement()` to libraries that expect an `HTMLCanvasElement`.
|
|
190
|
+
*/
|
|
132
191
|
export const createCanvas = (width, height) => new HeadlessCanvas(width, height);
|
|
133
192
|
//# sourceMappingURL=canvas.js.map
|
package/dist/canvas.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.js","
|
|
1
|
+
{"version":3,"file":"canvas.js","sources":["../src/canvas.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,13 +4,33 @@ export type WebgpuNodeOptions = {
|
|
|
4
4
|
/** Dawn options, e.g. `['backend=vulkan', 'enable-dawn-features=allow_unsafe_apis']`. */
|
|
5
5
|
dawnOptions?: string[];
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* The Vitest environment registered as `webgpu-node`. Before each test file it puts `navigator.gpu`, the `GPU*`
|
|
9
|
+
* classes and constants, and minimal `window`, `document`, `HTMLCanvasElement` and `requestAnimationFrame` shims
|
|
10
|
+
* on the global object (only where missing), and it removes them afterwards.
|
|
11
|
+
*/
|
|
7
12
|
declare const _default: {
|
|
13
|
+
/** The name Vitest uses to select this environment: `test.environment: 'webgpu-node'`. */
|
|
8
14
|
name: string;
|
|
15
|
+
/** Runs test files through Vite's `ssr` environment, as Node code. */
|
|
9
16
|
viteEnvironment: "ssr";
|
|
17
|
+
/**
|
|
18
|
+
* Creates the Dawn-backed `navigator.gpu` and installs the DOM shims on the test file's global object.
|
|
19
|
+
*
|
|
20
|
+
* @param global - The global object of the test file's context.
|
|
21
|
+
* @param __namedParameters - The environment options from the Vitest config; `webgpuNode` holds {@link WebgpuNodeOptions}.
|
|
22
|
+
* @returns A `teardown` hook that destroys the devices this environment created and restores the globals.
|
|
23
|
+
*/
|
|
10
24
|
setup(global: Record<string, unknown>, { webgpuNode }: {
|
|
11
25
|
webgpuNode?: WebgpuNodeOptions;
|
|
12
26
|
}): {
|
|
13
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Destroys the GPU devices requested through this environment, cancels pending animation-frame callbacks and
|
|
29
|
+
* removes the globals that `setup` added.
|
|
30
|
+
*
|
|
31
|
+
* @param _global - Unused; teardown restores the global object it captured in `setup`.
|
|
32
|
+
*/
|
|
33
|
+
teardown: (_global?: Record<string, unknown>) => void;
|
|
14
34
|
};
|
|
15
35
|
};
|
|
16
36
|
export default _default;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","
|
|
1
|
+
{"version":3,"file":"index.d.ts","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAElG,4EAA4E;AAC5E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAwDF;;;;GAIG;;IAED,0FAA0F;;IAE1F,sEAAsE;;IAEtE;;;;;;OAMG;kBACW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAuB;QAAE,UAAU,CAAC,EAAE,iBAAiB,CAAA;KAAE;QA6D1F;;;;;WAKG;6BACkB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;AA/ElD,wBA0FwB"}
|
package/dist/index.js
CHANGED
|
@@ -55,11 +55,26 @@ const restore = (properties) => {
|
|
|
55
55
|
Reflect.deleteProperty(target, key);
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* The Vitest environment registered as `webgpu-node`. Before each test file it puts `navigator.gpu`, the `GPU*`
|
|
60
|
+
* classes and constants, and minimal `window`, `document`, `HTMLCanvasElement` and `requestAnimationFrame` shims
|
|
61
|
+
* on the global object (only where missing), and it removes them afterwards.
|
|
62
|
+
*/
|
|
58
63
|
export default {
|
|
64
|
+
/** The name Vitest uses to select this environment: `test.environment: 'webgpu-node'`. */
|
|
59
65
|
name: 'webgpu-node',
|
|
66
|
+
/** Runs test files through Vite's `ssr` environment, as Node code. */
|
|
60
67
|
viteEnvironment: 'ssr',
|
|
68
|
+
/**
|
|
69
|
+
* Creates the Dawn-backed `navigator.gpu` and installs the DOM shims on the test file's global object.
|
|
70
|
+
*
|
|
71
|
+
* @param global - The global object of the test file's context.
|
|
72
|
+
* @param __namedParameters - The environment options from the Vitest config; `webgpuNode` holds {@link WebgpuNodeOptions}.
|
|
73
|
+
* @returns A `teardown` hook that destroys the devices this environment created and restores the globals.
|
|
74
|
+
*/
|
|
61
75
|
setup(global, { webgpuNode = {} }) {
|
|
62
76
|
const dom = domShims();
|
|
77
|
+
// oxlint-disable-next-line typescript/no-misused-spread -- intentionally copies the own enumerable GPU* globals
|
|
63
78
|
const shims = { ...globals, ...dom.shims };
|
|
64
79
|
const devices = new Set();
|
|
65
80
|
let disposed = false;
|
|
@@ -123,7 +138,13 @@ export default {
|
|
|
123
138
|
throw error;
|
|
124
139
|
}
|
|
125
140
|
return {
|
|
126
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Destroys the GPU devices requested through this environment, cancels pending animation-frame callbacks and
|
|
143
|
+
* removes the globals that `setup` added.
|
|
144
|
+
*
|
|
145
|
+
* @param _global - Unused; teardown restores the global object it captured in `setup`.
|
|
146
|
+
*/
|
|
147
|
+
teardown: (_global) => {
|
|
127
148
|
if (disposed)
|
|
128
149
|
return;
|
|
129
150
|
try {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-environment-webgpu-node",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Vitest environment for real headless WebGPU tests in Node (Dawn via the webgpu package): no browser, Playwright or mocks",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|
|
@@ -50,7 +50,10 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"webgpu": "^0.6.
|
|
53
|
+
"webgpu": "^0.6.2"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"typescript": "7.1.0-beta"
|
|
54
57
|
},
|
|
55
58
|
"peerDependencies": {
|
|
56
59
|
"typescript": ">=6",
|
|
@@ -65,7 +68,7 @@
|
|
|
65
68
|
"node": ">=22"
|
|
66
69
|
},
|
|
67
70
|
"scripts": {
|
|
68
|
-
"build": "tsc",
|
|
69
|
-
"tsc": "tsc"
|
|
71
|
+
"build": "tsc -p tsconfig.build.json",
|
|
72
|
+
"tsc": "tsc --noEmit"
|
|
70
73
|
}
|
|
71
74
|
}
|
package/src/canvas.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/** RGBA8 pixels, top row first. Feed it to `vitest-screenshot`'s `toMatchScreenshot`. */
|
|
2
|
-
export type RgbaImage = {
|
|
2
|
+
export type RgbaImage = {
|
|
3
|
+
/** Width in pixels. */
|
|
4
|
+
width: number;
|
|
5
|
+
/** Height in pixels. */
|
|
6
|
+
height: number;
|
|
7
|
+
/** RGBA8 bytes, four per pixel, row by row from the top. */
|
|
8
|
+
data: Uint8Array;
|
|
9
|
+
};
|
|
3
10
|
|
|
4
11
|
type DefaultCanvasElement = typeof globalThis extends {
|
|
5
12
|
HTMLCanvasElement: { prototype: infer TElement };
|
|
@@ -23,22 +30,41 @@ export class HeadlessCanvasContext {
|
|
|
23
30
|
#config: GPUCanvasConfiguration | undefined;
|
|
24
31
|
#texture: GPUTexture | undefined;
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Creates the context for a canvas. Canvases create their own; you rarely call this.
|
|
35
|
+
*
|
|
36
|
+
* @param canvas - The canvas whose size this context's texture follows.
|
|
37
|
+
*/
|
|
38
|
+
constructor(
|
|
39
|
+
/** The canvas this context belongs to. */
|
|
40
|
+
readonly canvas: HeadlessCanvas,
|
|
41
|
+
) {}
|
|
27
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Stores the configuration and discards any texture created under the previous one, as `GPUCanvasContext.configure()` does.
|
|
45
|
+
*
|
|
46
|
+
* @param config - Device, format and usage for the canvas texture.
|
|
47
|
+
*/
|
|
28
48
|
configure(config: GPUCanvasConfiguration): void {
|
|
29
49
|
this.#config = config;
|
|
30
50
|
this.#drop();
|
|
31
51
|
}
|
|
32
52
|
|
|
53
|
+
/** Clears the configuration and discards the current texture. */
|
|
33
54
|
unconfigure(): void {
|
|
34
55
|
this.#config = undefined;
|
|
35
56
|
this.#drop();
|
|
36
57
|
}
|
|
37
58
|
|
|
59
|
+
/** The configuration passed to `configure()`, or `null` before it is called or after `unconfigure()`. */
|
|
38
60
|
getConfiguration(): GPUCanvasConfiguration | null {
|
|
39
61
|
return this.#config ?? null;
|
|
40
62
|
}
|
|
41
63
|
|
|
64
|
+
/**
|
|
65
|
+
* The texture the canvas renders into. It is created on first use at the canvas size and reused until the context
|
|
66
|
+
* is reconfigured or the canvas is resized. Throws if `configure()` has not been called.
|
|
67
|
+
*/
|
|
42
68
|
getCurrentTexture(): GPUTexture {
|
|
43
69
|
const config = this.#config;
|
|
44
70
|
if (!config) throw new Error('getCurrentTexture() called before configure()');
|
|
@@ -100,54 +126,118 @@ export class HeadlessCanvasContext {
|
|
|
100
126
|
/** Enough of `HTMLCanvasElement` for three.js `WebGPURenderer`, Babylon Lite and friends. */
|
|
101
127
|
export class HeadlessCanvas {
|
|
102
128
|
readonly #context = new HeadlessCanvasContext(this);
|
|
129
|
+
/** A plain object libraries can write CSS properties to; it has no effect on rendering. */
|
|
103
130
|
readonly style: Record<string, string> = {};
|
|
104
131
|
readonly #attributes = new Map<string, string>();
|
|
105
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Creates a canvas of the given size. {@link createCanvas} is the usual entry point.
|
|
135
|
+
*
|
|
136
|
+
* @param width - Canvas width in pixels.
|
|
137
|
+
* @param height - Canvas height in pixels.
|
|
138
|
+
*/
|
|
106
139
|
constructor(
|
|
140
|
+
/** Canvas width in pixels. */
|
|
107
141
|
public width = 300,
|
|
142
|
+
/** Canvas height in pixels. */
|
|
108
143
|
public height = 150,
|
|
109
144
|
) {}
|
|
110
145
|
|
|
146
|
+
/** Same as `width`; there is no layout in Node. */
|
|
111
147
|
get clientWidth(): number {
|
|
112
148
|
return this.width;
|
|
113
149
|
}
|
|
114
150
|
|
|
151
|
+
/** Same as `height`; there is no layout in Node. */
|
|
115
152
|
get clientHeight(): number {
|
|
116
153
|
return this.height;
|
|
117
154
|
}
|
|
118
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Returns the canvas's `GPUCanvasContext` for `'webgpu'`.
|
|
158
|
+
*
|
|
159
|
+
* @param id - The context type. Only `'webgpu'` is supported.
|
|
160
|
+
* @returns The context for `'webgpu'`, or `null` for any other type.
|
|
161
|
+
*/
|
|
119
162
|
getContext(id: 'webgpu'): HeadlessCanvasContext;
|
|
163
|
+
/**
|
|
164
|
+
* Returns the canvas's `GPUCanvasContext` for `'webgpu'`.
|
|
165
|
+
*
|
|
166
|
+
* @param id - The context type. Only `'webgpu'` is supported.
|
|
167
|
+
* @returns The context for `'webgpu'`, or `null` for any other type.
|
|
168
|
+
*/
|
|
120
169
|
getContext(id: string): HeadlessCanvasContext | null;
|
|
121
170
|
getContext(id: string): HeadlessCanvasContext | null {
|
|
122
171
|
return id === 'webgpu' ? this.#context : null;
|
|
123
172
|
}
|
|
124
173
|
|
|
174
|
+
/** Reads the current texture back as RGBA8 pixels; see {@link HeadlessCanvasContext.readPixels}. */
|
|
125
175
|
readPixels(): Promise<RgbaImage> {
|
|
126
176
|
return this.#context.readPixels();
|
|
127
177
|
}
|
|
128
178
|
|
|
129
|
-
/**
|
|
179
|
+
/**
|
|
180
|
+
* This same object, cast to the element type a rendering library expects.
|
|
181
|
+
*
|
|
182
|
+
* @typeParam TElement - The element type to cast to. Defaults to `HTMLCanvasElement` when DOM types are available.
|
|
183
|
+
*/
|
|
130
184
|
asElement<TElement = DefaultCanvasElement>(): TElement {
|
|
131
185
|
return this as unknown as TElement;
|
|
132
186
|
}
|
|
133
187
|
|
|
134
|
-
|
|
188
|
+
/** A rectangle at the origin with the canvas's width and height. */
|
|
189
|
+
getBoundingClientRect(): {
|
|
190
|
+
/** Always 0. */
|
|
191
|
+
x: number;
|
|
192
|
+
/** Always 0. */
|
|
193
|
+
y: number;
|
|
194
|
+
/** The canvas width. */
|
|
195
|
+
width: number;
|
|
196
|
+
/** The canvas height. */
|
|
197
|
+
height: number;
|
|
198
|
+
/** Always 0. */
|
|
199
|
+
top: number;
|
|
200
|
+
/** Always 0. */
|
|
201
|
+
left: number;
|
|
202
|
+
} {
|
|
135
203
|
return { x: 0, y: 0, top: 0, left: 0, width: this.width, height: this.height };
|
|
136
204
|
}
|
|
137
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Stores an attribute on the canvas.
|
|
208
|
+
*
|
|
209
|
+
* @param name - Attribute name.
|
|
210
|
+
* @param value - Attribute value.
|
|
211
|
+
*/
|
|
138
212
|
setAttribute(name: string, value: string): void {
|
|
139
213
|
this.#attributes.set(name, value);
|
|
140
214
|
}
|
|
141
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Reads an attribute stored with `setAttribute()`.
|
|
218
|
+
*
|
|
219
|
+
* @param name - Attribute name.
|
|
220
|
+
* @returns The stored value, or `null` if none was set.
|
|
221
|
+
*/
|
|
142
222
|
getAttribute(name: string): string | null {
|
|
143
223
|
return this.#attributes.get(name) ?? null;
|
|
144
224
|
}
|
|
145
225
|
|
|
226
|
+
/** Does nothing; present so libraries that register listeners keep working. */
|
|
146
227
|
addEventListener(): void {}
|
|
228
|
+
/** Does nothing; present so libraries that remove listeners keep working. */
|
|
147
229
|
removeEventListener(): void {}
|
|
230
|
+
/** Does nothing and returns `true`. */
|
|
148
231
|
dispatchEvent(): boolean {
|
|
149
232
|
return true;
|
|
150
233
|
}
|
|
151
234
|
}
|
|
152
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Creates a headless canvas for WebGPU tests.
|
|
238
|
+
*
|
|
239
|
+
* @param width - Canvas width in pixels.
|
|
240
|
+
* @param height - Canvas height in pixels.
|
|
241
|
+
* @returns A {@link HeadlessCanvas}; pass `canvas.asElement()` to libraries that expect an `HTMLCanvasElement`.
|
|
242
|
+
*/
|
|
153
243
|
export const createCanvas = (width: number, height: number): HeadlessCanvas => new HeadlessCanvas(width, height);
|
package/src/index.ts
CHANGED
|
@@ -64,11 +64,26 @@ const restore = (properties: SavedProperty[]): void => {
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* The Vitest environment registered as `webgpu-node`. Before each test file it puts `navigator.gpu`, the `GPU*`
|
|
69
|
+
* classes and constants, and minimal `window`, `document`, `HTMLCanvasElement` and `requestAnimationFrame` shims
|
|
70
|
+
* on the global object (only where missing), and it removes them afterwards.
|
|
71
|
+
*/
|
|
67
72
|
export default {
|
|
73
|
+
/** The name Vitest uses to select this environment: `test.environment: 'webgpu-node'`. */
|
|
68
74
|
name: 'webgpu-node',
|
|
75
|
+
/** Runs test files through Vite's `ssr` environment, as Node code. */
|
|
69
76
|
viteEnvironment: 'ssr',
|
|
77
|
+
/**
|
|
78
|
+
* Creates the Dawn-backed `navigator.gpu` and installs the DOM shims on the test file's global object.
|
|
79
|
+
*
|
|
80
|
+
* @param global - The global object of the test file's context.
|
|
81
|
+
* @param __namedParameters - The environment options from the Vitest config; `webgpuNode` holds {@link WebgpuNodeOptions}.
|
|
82
|
+
* @returns A `teardown` hook that destroys the devices this environment created and restores the globals.
|
|
83
|
+
*/
|
|
70
84
|
setup(global: Record<string, unknown>, { webgpuNode = {} }: { webgpuNode?: WebgpuNodeOptions }) {
|
|
71
85
|
const dom = domShims();
|
|
86
|
+
// oxlint-disable-next-line typescript/no-misused-spread -- intentionally copies the own enumerable GPU* globals
|
|
72
87
|
const shims = { ...globals, ...dom.shims } as Record<string, unknown>;
|
|
73
88
|
const devices = new Set<GPUDevice>();
|
|
74
89
|
let disposed = false;
|
|
@@ -127,7 +142,13 @@ export default {
|
|
|
127
142
|
throw error;
|
|
128
143
|
}
|
|
129
144
|
return {
|
|
130
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Destroys the GPU devices requested through this environment, cancels pending animation-frame callbacks and
|
|
147
|
+
* removes the globals that `setup` added.
|
|
148
|
+
*
|
|
149
|
+
* @param _global - Unused; teardown restores the global object it captured in `setup`.
|
|
150
|
+
*/
|
|
151
|
+
teardown: (_global?: Record<string, unknown>) => {
|
|
131
152
|
if (disposed) return;
|
|
132
153
|
try {
|
|
133
154
|
dispose();
|