@quake2ts/test-utils 0.0.761 → 0.0.764
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/dist/index.cjs +6007 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +6022 -0
- package/dist/index.js.map +1 -1
- package/package.json +16 -11
- package/src/index.ts +2 -0
- package/src/setup/headless-webgl.ts +112 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quake2ts/test-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.764",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"pngjs": "^7.0.0",
|
|
40
40
|
"vitest": "^1.6.0",
|
|
41
41
|
"webgpu": "^0.3.8",
|
|
42
|
-
"@quake2ts/engine": "^0.0.
|
|
43
|
-
"@quake2ts/
|
|
44
|
-
"@quake2ts/
|
|
45
|
-
"@quake2ts/shared": "0.0.
|
|
42
|
+
"@quake2ts/engine": "^0.0.764",
|
|
43
|
+
"@quake2ts/server": "0.0.764",
|
|
44
|
+
"@quake2ts/game": "0.0.764",
|
|
45
|
+
"@quake2ts/shared": "0.0.764"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"@quake2ts/engine": {
|
|
@@ -77,13 +77,14 @@
|
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"gl-matrix": "^3.4.4",
|
|
81
80
|
"@napi-rs/canvas": "^0.1.84",
|
|
81
|
+
"@types/gl": "^6.0.5",
|
|
82
82
|
"@types/jsdom": "^27.0.0",
|
|
83
83
|
"@types/pixelmatch": "^5.2.6",
|
|
84
84
|
"@types/pngjs": "^6.0.5",
|
|
85
85
|
"@webgpu/types": "^0.1.68",
|
|
86
86
|
"fake-indexeddb": "^6.0.0",
|
|
87
|
+
"gl-matrix": "^3.4.4",
|
|
87
88
|
"jsdom": "^27.3.0",
|
|
88
89
|
"pixelmatch": "^7.1.0",
|
|
89
90
|
"playwright": "^1.57.0",
|
|
@@ -92,14 +93,18 @@
|
|
|
92
93
|
"typescript": "^5.4.5",
|
|
93
94
|
"vitest": "^1.6.0",
|
|
94
95
|
"webgpu": "^0.3.8",
|
|
95
|
-
"@quake2ts/engine": "^0.0.
|
|
96
|
-
"@quake2ts/
|
|
97
|
-
"@quake2ts/server": "0.0.
|
|
98
|
-
"@quake2ts/
|
|
96
|
+
"@quake2ts/engine": "^0.0.764",
|
|
97
|
+
"@quake2ts/game": "0.0.764",
|
|
98
|
+
"@quake2ts/server": "0.0.764",
|
|
99
|
+
"@quake2ts/shared": "0.0.764"
|
|
100
|
+
},
|
|
101
|
+
"optionalDependencies": {
|
|
102
|
+
"gl": "^8.1.6"
|
|
99
103
|
},
|
|
100
104
|
"scripts": {
|
|
101
105
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
102
106
|
"test": "vitest run --passWithNoTests",
|
|
103
|
-
"test:webgpu": "cross-env TEST_TYPE=webgpu vitest run"
|
|
107
|
+
"test:webgpu": "cross-env TEST_TYPE=webgpu vitest run",
|
|
108
|
+
"test:webgl": "cross-env TEST_TYPE=webgl vitest run"
|
|
104
109
|
}
|
|
105
110
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ export * from './server/helpers/bandwidth.js';
|
|
|
29
29
|
export * from './setup/browser.js';
|
|
30
30
|
export * from './setup/canvas.js';
|
|
31
31
|
export * from './setup/webgpu.js';
|
|
32
|
+
export * from './setup/headless-webgl.js'; // Added
|
|
32
33
|
export * from './engine/mocks/webgpu.js';
|
|
33
34
|
export * from './setup/timing.js';
|
|
34
35
|
export * from './setup/node.js';
|
|
@@ -71,6 +72,7 @@ export type { StorageScenario } from './setup/storage.js';
|
|
|
71
72
|
export type { NetworkSimulator, NetworkCondition } from './e2e/network.js';
|
|
72
73
|
export type { VisualScenario, VisualDiff } from './e2e/visual.js';
|
|
73
74
|
export type { HeadlessWebGPUSetup, WebGPUContextState } from './setup/webgpu.js';
|
|
75
|
+
export type { HeadlessWebGLContext, HeadlessWebGLOptions } from './setup/headless-webgl.js'; // Added
|
|
74
76
|
export type { RenderTestSetup, ComputeTestSetup } from './engine/helpers/webgpu-rendering.js';
|
|
75
77
|
export type { GeometryBuffers } from './engine/helpers/pipeline-test-template.js';
|
|
76
78
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export interface HeadlessWebGLOptions {
|
|
2
|
+
width?: number;
|
|
3
|
+
height?: number;
|
|
4
|
+
antialias?: boolean;
|
|
5
|
+
preserveDrawingBuffer?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface HeadlessWebGLContext {
|
|
9
|
+
gl: WebGL2RenderingContext;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
cleanup: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a headless WebGL2 context using the 'gl' package.
|
|
17
|
+
* Note: 'gl' is lazy-loaded to avoid issues in environments where it's not supported/needed.
|
|
18
|
+
*/
|
|
19
|
+
export async function createHeadlessWebGL(
|
|
20
|
+
options: HeadlessWebGLOptions = {}
|
|
21
|
+
): Promise<HeadlessWebGLContext> {
|
|
22
|
+
const width = options.width ?? 256;
|
|
23
|
+
const height = options.height ?? 256;
|
|
24
|
+
|
|
25
|
+
// Dynamically import gl
|
|
26
|
+
// @ts-ignore - gl package might not be typed correctly for dynamic import or TS config
|
|
27
|
+
const { default: gl } = await import('gl');
|
|
28
|
+
|
|
29
|
+
// The 'gl' function signature is gl(width, height, options)
|
|
30
|
+
const context = gl(width, height, {
|
|
31
|
+
antialias: options.antialias ?? false, // Default to false for determinism
|
|
32
|
+
preserveDrawingBuffer: options.preserveDrawingBuffer ?? true, // Default to true for readback
|
|
33
|
+
stencil: true,
|
|
34
|
+
alpha: true,
|
|
35
|
+
depth: true,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!context) {
|
|
39
|
+
throw new Error('Failed to create headless WebGL context');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Cast to WebGL2RenderingContext
|
|
43
|
+
const glContext = context as unknown as WebGL2RenderingContext;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
gl: glContext,
|
|
47
|
+
width,
|
|
48
|
+
height,
|
|
49
|
+
cleanup: () => {
|
|
50
|
+
// gl package extension to destroy context
|
|
51
|
+
const ext = glContext.getExtension('STACKGL_destroy_context');
|
|
52
|
+
if (ext) {
|
|
53
|
+
ext.destroy();
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Captures the current framebuffer content as a Uint8ClampedArray (RGBA).
|
|
61
|
+
* Flips the pixels vertically to match standard image orientation (top-left origin).
|
|
62
|
+
*/
|
|
63
|
+
export function captureWebGLFramebuffer(
|
|
64
|
+
glContext: WebGL2RenderingContext,
|
|
65
|
+
width: number,
|
|
66
|
+
height: number
|
|
67
|
+
): Uint8ClampedArray {
|
|
68
|
+
const pixels = new Uint8ClampedArray(width * height * 4);
|
|
69
|
+
|
|
70
|
+
// readPixels reads from bottom-left
|
|
71
|
+
glContext.readPixels(
|
|
72
|
+
0,
|
|
73
|
+
0,
|
|
74
|
+
width,
|
|
75
|
+
height,
|
|
76
|
+
glContext.RGBA,
|
|
77
|
+
glContext.UNSIGNED_BYTE,
|
|
78
|
+
pixels
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return flipPixelsVertically(pixels, width, height);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Flips pixel data vertically in-place.
|
|
86
|
+
*/
|
|
87
|
+
export function flipPixelsVertically(
|
|
88
|
+
pixels: Uint8ClampedArray,
|
|
89
|
+
width: number,
|
|
90
|
+
height: number
|
|
91
|
+
): Uint8ClampedArray {
|
|
92
|
+
const rowSize = width * 4;
|
|
93
|
+
const halfHeight = Math.floor(height / 2);
|
|
94
|
+
const tempRow = new Uint8Array(rowSize);
|
|
95
|
+
|
|
96
|
+
// Swap rows
|
|
97
|
+
for (let y = 0; y < halfHeight; y++) {
|
|
98
|
+
const topOffset = y * rowSize;
|
|
99
|
+
const bottomOffset = (height - 1 - y) * rowSize;
|
|
100
|
+
|
|
101
|
+
// Copy top to temp
|
|
102
|
+
tempRow.set(pixels.subarray(topOffset, topOffset + rowSize));
|
|
103
|
+
|
|
104
|
+
// Copy bottom to top
|
|
105
|
+
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + rowSize);
|
|
106
|
+
|
|
107
|
+
// Copy temp to bottom
|
|
108
|
+
pixels.set(tempRow, bottomOffset);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return pixels;
|
|
112
|
+
}
|