@flighthq/application-gl 0.3.0-edge.1458.0c01b63

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.
@@ -0,0 +1,2 @@
1
+ export * from './glApplicationRenderView';
2
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './glApplicationRenderView';
2
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ApplicationWindow, GlApplicationRenderView, GlApplicationRenderViewOptions } from '@flighthq/types/contract';
2
+ export declare function createGlApplicationRenderView(window: ApplicationWindow, canvas: HTMLCanvasElement, options?: Readonly<GlApplicationRenderViewOptions>): GlApplicationRenderView;
3
+ export declare function destroyGlApplicationRenderView(view: GlApplicationRenderView): void;
4
+ //# sourceMappingURL=glApplicationRenderView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glApplicationRenderView.d.ts","sourceRoot":"","sources":["../src/glApplicationRenderView.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,8BAA8B,EAG/B,MAAM,0BAA0B,CAAC;AAMlC,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,QAAQ,CAAC,8BAA8B,CAAM,GACrD,uBAAuB,CAoBzB;AAID,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAIlF"}
@@ -0,0 +1,57 @@
1
+ import { createApplicationRenderView, detachApplicationRenderView } from '@flighthq/application/contract';
2
+ import { createViewport } from '@flighthq/node/contract';
3
+ import { createGlRenderState, createGlRenderTarget, destroyGlRenderState, destroyGlRenderTarget, invalidateGlRenderStateCache, resizeGlRenderTarget, } from '@flighthq/render-gl/contract';
4
+ // Allocates the GL realization of an ApplicationRenderView. Creation does not attach the resize signal;
5
+ // call attachApplicationRenderView explicitly when the window should drive subsequent synchronization.
6
+ // The returned view owns the state and target allocated here; destroy it with
7
+ // destroyGlApplicationRenderView.
8
+ export function createGlApplicationRenderView(window, canvas, options = {}) {
9
+ const width = Math.max(0, Math.round(window.width * window.devicePixelRatio));
10
+ const height = Math.max(0, Math.round(window.height * window.devicePixelRatio));
11
+ synchronizeGlCanvasBackingStore(canvas, width, height);
12
+ const renderState = createGlRenderState(canvas, {
13
+ ...options.render,
14
+ pixelRatio: window.devicePixelRatio,
15
+ });
16
+ const renderTarget = createGlRenderTarget(renderState, {
17
+ ...options.target,
18
+ height,
19
+ width,
20
+ });
21
+ const viewport = createViewport({
22
+ devicePixelRatio: window.devicePixelRatio,
23
+ height,
24
+ width,
25
+ });
26
+ return createApplicationRenderView(window, renderState, renderTarget, viewport, resizeGlApplicationRenderView);
27
+ }
28
+ // Detaches window observation and deterministically frees the target and state owned by
29
+ // createGlApplicationRenderView. The canvas and ApplicationWindow remain caller-owned.
30
+ export function destroyGlApplicationRenderView(view) {
31
+ detachApplicationRenderView(view);
32
+ destroyGlRenderTarget(view.renderState, view.renderTarget);
33
+ destroyGlRenderState(view.renderState);
34
+ }
35
+ function resizeGlApplicationRenderView(renderState, renderTarget, width, height) {
36
+ if (synchronizeGlCanvasBackingStore(renderState.canvas, width, height)) {
37
+ invalidateGlRenderStateCache(renderState);
38
+ }
39
+ const storageWidth = Math.max(1, Math.ceil(width));
40
+ const storageHeight = Math.max(1, Math.ceil(height));
41
+ if (renderTarget.width !== storageWidth || renderTarget.height !== storageHeight) {
42
+ resizeGlRenderTarget(renderState, renderTarget, width, height);
43
+ }
44
+ }
45
+ function synchronizeGlCanvasBackingStore(canvas, width, height) {
46
+ let changed = false;
47
+ if (canvas.width !== width) {
48
+ canvas.width = width;
49
+ changed = true;
50
+ }
51
+ if (canvas.height !== height) {
52
+ canvas.height = height;
53
+ changed = true;
54
+ }
55
+ return changed;
56
+ }
57
+ //# sourceMappingURL=glApplicationRenderView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glApplicationRenderView.js","sourceRoot":"","sources":["../src/glApplicationRenderView.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { createGlApplicationRenderView, destroyGlApplicationRenderView } from './contract';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createGlApplicationRenderView, destroyGlApplicationRenderView } from './contract';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@flighthq/application-gl",
3
+ "version": "0.3.0-edge.1458.0c01b63",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/flighthq/flight.git",
7
+ "directory": "packages/application-gl"
8
+ },
9
+ "type": "module",
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./contract": {
18
+ "types": "./dist/contract.d.ts",
19
+ "default": "./dist/contract.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src/**/*.test.ts",
25
+ "!dist/**/*.test.js",
26
+ "!dist/**/*.test.d.ts",
27
+ "!dist/**/*.test.js.map",
28
+ "!dist/**/*.test.d.ts.map"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc -b",
32
+ "clean": "tsc -b --clean",
33
+ "test": "vitest run --config vitest.config.ts",
34
+ "test:watch": "vitest --watch --config vitest.config.ts",
35
+ "prepack": "npm run clean && npm run clean:dist && npm run build",
36
+ "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
37
+ },
38
+ "dependencies": {
39
+ "@flighthq/application": "0.3.0-edge.1458.0c01b63",
40
+ "@flighthq/node": "0.3.0-edge.1458.0c01b63",
41
+ "@flighthq/render-gl": "0.3.0-edge.1458.0c01b63",
42
+ "@flighthq/types": "0.3.0-edge.1458.0c01b63"
43
+ },
44
+ "devDependencies": {
45
+ "@flighthq/signals": "*",
46
+ "typescript": "^5.3.0"
47
+ },
48
+ "description": "WebGL application render-view assembly linking windows, targets, state, and viewport",
49
+ "sideEffects": false
50
+ }
@@ -0,0 +1,118 @@
1
+ import {
2
+ attachApplicationRenderView,
3
+ createApplicationWindow,
4
+ synchronizeApplicationRenderView,
5
+ } from '@flighthq/application/contract';
6
+ import { emitSignal } from '@flighthq/signals/contract';
7
+ import type { GlRenderState, GlRenderTarget } from '@flighthq/types/contract';
8
+
9
+ import { createGlApplicationRenderView, destroyGlApplicationRenderView } from './glApplicationRenderView';
10
+
11
+ const mocks = vi.hoisted(() => ({
12
+ createGlRenderState: vi.fn(),
13
+ createGlRenderTarget: vi.fn(),
14
+ createViewport: vi.fn(),
15
+ destroyGlRenderState: vi.fn(),
16
+ destroyGlRenderTarget: vi.fn(),
17
+ invalidateGlRenderStateCache: vi.fn(),
18
+ resizeGlRenderTarget: vi.fn(),
19
+ }));
20
+
21
+ vi.mock('@flighthq/node/contract', () => ({
22
+ createViewport: mocks.createViewport,
23
+ }));
24
+
25
+ vi.mock('@flighthq/render-gl/contract', () => ({
26
+ createGlRenderState: mocks.createGlRenderState,
27
+ createGlRenderTarget: mocks.createGlRenderTarget,
28
+ destroyGlRenderState: mocks.destroyGlRenderState,
29
+ destroyGlRenderTarget: mocks.destroyGlRenderTarget,
30
+ invalidateGlRenderStateCache: mocks.invalidateGlRenderStateCache,
31
+ resizeGlRenderTarget: mocks.resizeGlRenderTarget,
32
+ }));
33
+
34
+ beforeEach(() => {
35
+ mocks.createGlRenderState.mockImplementation((canvas: HTMLCanvasElement, options: { pixelRatio: number }) => ({
36
+ canvas,
37
+ pixelRatio: options.pixelRatio,
38
+ renderTransform2D: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 },
39
+ }));
40
+ mocks.createGlRenderTarget.mockImplementation(
41
+ (_state: GlRenderState, descriptor: { height: number; width: number }) => ({
42
+ height: Math.max(1, descriptor.height),
43
+ width: Math.max(1, descriptor.width),
44
+ }),
45
+ );
46
+ mocks.createViewport.mockImplementation((options) => ({ ...options, x: 0, y: 0 }));
47
+ mocks.resizeGlRenderTarget.mockImplementation(
48
+ (_state: GlRenderState, target: GlRenderTarget, width: number, height: number) => {
49
+ target.width = Math.max(1, width);
50
+ target.height = Math.max(1, height);
51
+ },
52
+ );
53
+ });
54
+
55
+ describe('createGlApplicationRenderView', () => {
56
+ it('allocates and links the GL state, target, and device-pixel viewport', () => {
57
+ const window = createApplicationWindow();
58
+ window.width = 320;
59
+ window.height = 180;
60
+ window.devicePixelRatio = 2;
61
+ const canvas = document.createElement('canvas');
62
+
63
+ const view = createGlApplicationRenderView(window, canvas, {
64
+ render: { antialias: false },
65
+ target: { colorSpace: 'linear', depth: 'depth-stencil' },
66
+ });
67
+
68
+ expect(canvas.width).toBe(640);
69
+ expect(canvas.height).toBe(360);
70
+ expect(mocks.createGlRenderState).toHaveBeenCalledWith(canvas, { antialias: false, pixelRatio: 2 });
71
+ expect(mocks.createGlRenderTarget).toHaveBeenCalledWith(view.renderState, {
72
+ colorSpace: 'linear',
73
+ depth: 'depth-stencil',
74
+ height: 360,
75
+ width: 640,
76
+ });
77
+ expect(view.viewport).toMatchObject({ devicePixelRatio: 2, height: 360, width: 640, x: 0, y: 0 });
78
+ });
79
+
80
+ it('changes the backing store and target only when a synchronized window extent changes', () => {
81
+ const window = createApplicationWindow();
82
+ window.width = 20;
83
+ window.height = 10;
84
+ const canvas = document.createElement('canvas');
85
+ const view = createGlApplicationRenderView(window, canvas);
86
+ mocks.invalidateGlRenderStateCache.mockClear();
87
+ mocks.resizeGlRenderTarget.mockClear();
88
+
89
+ synchronizeApplicationRenderView(view);
90
+ expect(mocks.invalidateGlRenderStateCache).not.toHaveBeenCalled();
91
+ expect(mocks.resizeGlRenderTarget).not.toHaveBeenCalled();
92
+
93
+ window.width = 30;
94
+ synchronizeApplicationRenderView(view);
95
+ expect(canvas.width).toBe(30);
96
+ expect(mocks.invalidateGlRenderStateCache).toHaveBeenCalledWith(view.renderState);
97
+ expect(mocks.resizeGlRenderTarget).toHaveBeenCalledWith(view.renderState, view.renderTarget, 30, 10);
98
+ });
99
+ });
100
+
101
+ describe('destroyGlApplicationRenderView', () => {
102
+ it('detaches resize observation and destroys the owned target before the state', () => {
103
+ const window = createApplicationWindow();
104
+ window.width = 20;
105
+ window.height = 10;
106
+ const view = createGlApplicationRenderView(window, document.createElement('canvas'));
107
+ attachApplicationRenderView(view);
108
+ mocks.resizeGlRenderTarget.mockClear();
109
+
110
+ destroyGlApplicationRenderView(view);
111
+ window.width = 30;
112
+ emitSignal(window.onResize);
113
+
114
+ expect(mocks.resizeGlRenderTarget).not.toHaveBeenCalled();
115
+ expect(mocks.destroyGlRenderTarget).toHaveBeenCalledWith(view.renderState, view.renderTarget);
116
+ expect(mocks.destroyGlRenderState).toHaveBeenCalledWith(view.renderState);
117
+ });
118
+ });