@flighthq/scene2d-gl 0.3.0-next.906.07cea63 → 0.3.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/dist/contract.d.ts +9 -6
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +9 -6
- package/dist/contract.js.map +1 -1
- package/dist/enableGlColorAdjustmentGuards.js +2 -2
- package/dist/enableGlColorAdjustmentGuards.js.map +1 -1
- package/dist/enableGlStrokePathTessellation.d.ts +3 -0
- package/dist/enableGlStrokePathTessellation.d.ts.map +1 -0
- package/dist/enableGlStrokePathTessellation.js +8 -0
- package/dist/enableGlStrokePathTessellation.js.map +1 -0
- package/dist/explainGlScene2DCoverage.d.ts +4 -0
- package/dist/explainGlScene2DCoverage.d.ts.map +1 -0
- package/dist/explainGlScene2DCoverage.js +63 -0
- package/dist/explainGlScene2DCoverage.js.map +1 -0
- package/dist/glBitmapText.d.ts.map +1 -1
- package/dist/glBitmapText.js +20 -15
- package/dist/glBitmapText.js.map +1 -1
- package/dist/glCache.d.ts +2 -2
- package/dist/glCache.d.ts.map +1 -1
- package/dist/glCache.js +24 -92
- package/dist/glCache.js.map +1 -1
- package/dist/glClipContours.js +3 -3
- package/dist/glClipContours.js.map +1 -1
- package/dist/glClipRectangle.js +10 -8
- package/dist/glClipRectangle.js.map +1 -1
- package/dist/glColorAdjustmentMaterialFeature.d.ts.map +1 -1
- package/dist/glColorAdjustmentMaterialFeature.js +79 -76
- package/dist/glColorAdjustmentMaterialFeature.js.map +1 -1
- package/dist/glMeshShapeRenderer.d.ts +4 -0
- package/dist/glMeshShapeRenderer.d.ts.map +1 -0
- package/dist/glMeshShapeRenderer.js +89 -0
- package/dist/glMeshShapeRenderer.js.map +1 -0
- package/dist/glNode2D.js +2 -2
- package/dist/glNode2D.js.map +1 -1
- package/dist/glParticleEmitter2D.js +10 -9
- package/dist/glParticleEmitter2D.js.map +1 -1
- package/dist/glQuadBatch.d.ts.map +1 -1
- package/dist/glQuadBatch.js +18 -12
- package/dist/glQuadBatch.js.map +1 -1
- package/dist/{glSpriteBatch.d.ts → glQuadBatchWriter.d.ts} +6 -6
- package/dist/glQuadBatchWriter.d.ts.map +1 -0
- package/dist/{glSpriteBatch.js → glQuadBatchWriter.js} +84 -73
- package/dist/glQuadBatchWriter.js.map +1 -0
- package/dist/glRasterShapeRenderer.d.ts +4 -0
- package/dist/glRasterShapeRenderer.d.ts.map +1 -0
- package/dist/glRasterShapeRenderer.js +103 -0
- package/dist/glRasterShapeRenderer.js.map +1 -0
- package/dist/glRichText.js +2 -2
- package/dist/glRichText.js.map +1 -1
- package/dist/glScale9Shape.d.ts +1 -3
- package/dist/glScale9Shape.d.ts.map +1 -1
- package/dist/glScale9Shape.js +27 -29
- package/dist/glScale9Shape.js.map +1 -1
- package/dist/glShape.d.ts +2 -1
- package/dist/glShape.d.ts.map +1 -1
- package/dist/glShape.js +19 -135
- package/dist/glShape.js.map +1 -1
- package/dist/glShapeData.d.ts +7 -0
- package/dist/glShapeData.d.ts.map +1 -0
- package/dist/glShapeData.js +57 -0
- package/dist/glShapeData.js.map +1 -0
- package/dist/glShapeMesh.js +5 -5
- package/dist/glShapeMesh.js.map +1 -1
- package/dist/glShapeRasterizer.d.ts +4 -0
- package/dist/glShapeRasterizer.d.ts.map +1 -0
- package/dist/glShapeRasterizer.js +11 -0
- package/dist/glShapeRasterizer.js.map +1 -0
- package/dist/glSprite.d.ts +3 -2
- package/dist/glSprite.d.ts.map +1 -1
- package/dist/glSprite.js +64 -24
- package/dist/glSprite.js.map +1 -1
- package/dist/glStandardMaterial.d.ts +1 -1
- package/dist/glStandardMaterial.js +2 -2
- package/dist/glStandardMaterial.js.map +1 -1
- package/dist/glTestHelper.d.ts +1 -0
- package/dist/glTestHelper.d.ts.map +1 -1
- package/dist/glTestHelper.js +1 -0
- package/dist/glTestHelper.js.map +1 -1
- package/dist/glTextLabel.d.ts.map +1 -1
- package/dist/glTextLabel.js +14 -12
- package/dist/glTextLabel.js.map +1 -1
- package/dist/glTilemap.d.ts.map +1 -1
- package/dist/glTilemap.js +19 -17
- package/dist/glTilemap.js.map +1 -1
- package/dist/glVelocity.js +2 -2
- package/dist/glVelocity.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +22 -19
- package/src/enableGlColorAdjustmentGuards.test.ts +4 -4
- package/src/enableGlStrokePathTessellation.test.ts +16 -0
- package/src/explainGlScene2DCoverage.test.ts +99 -0
- package/src/glBitmapText.test.ts +18 -17
- package/src/glCache.test.ts +24 -11
- package/src/glClipContours.test.ts +57 -2
- package/src/glClipRectangle.test.ts +9 -0
- package/src/glColorAdjustmentMaterialFeature.test.ts +74 -71
- package/src/glMeshShapeRenderer.test.ts +112 -0
- package/src/glParticleEmitter2D.test.ts +38 -22
- package/src/glQuadBatch.test.ts +40 -28
- package/src/{glSpriteBatch.test.ts → glQuadBatchWriter.test.ts} +74 -53
- package/src/glRasterShapeRenderer.test.ts +109 -0
- package/src/glScale9Shape.test.ts +0 -13
- package/src/glShape.test.ts +166 -21
- package/src/glShapeData.test.ts +74 -0
- package/src/glShapeMesh.test.ts +1 -1
- package/src/glShapeRasterizer.test.ts +32 -0
- package/src/glSprite.test.ts +71 -62
- package/src/glStandardMaterial.test.ts +3 -3
- package/src/glTextLabel.test.ts +16 -16
- package/src/glTilemap.test.ts +44 -47
- package/src/glVelocity.test.ts +4 -4
- package/dist/glBitmap.d.ts +0 -4
- package/dist/glBitmap.d.ts.map +0 -1
- package/dist/glBitmap.js +0 -93
- package/dist/glBitmap.js.map +0 -1
- package/dist/glRenderTargetNode2D.d.ts +0 -11
- package/dist/glRenderTargetNode2D.d.ts.map +0 -1
- package/dist/glRenderTargetNode2D.js +0 -78
- package/dist/glRenderTargetNode2D.js.map +0 -1
- package/dist/glSpriteBatch.d.ts.map +0 -1
- package/dist/glSpriteBatch.js.map +0 -1
- package/dist/glSpriteRenderer.d.ts +0 -3
- package/dist/glSpriteRenderer.d.ts.map +0 -1
- package/dist/glSpriteRenderer.js +0 -54
- package/dist/glSpriteRenderer.js.map +0 -1
- package/dist/glVideo.d.ts +0 -6
- package/dist/glVideo.d.ts.map +0 -1
- package/dist/glVideo.js +0 -63
- package/dist/glVideo.js.map +0 -1
- package/src/glBitmap.test.ts +0 -136
- package/src/glRenderTargetNode2D.test.ts +0 -328
- package/src/glSpriteRenderer.test.ts +0 -89
- package/src/glVideo.test.ts +0 -99
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { createImageResource } from '@flighthq/image/contract';
|
|
2
1
|
import { getGlRenderStateRuntime } from '@flighthq/render-gl/contract';
|
|
3
|
-
import
|
|
2
|
+
import { areColorAdjustmentsEnabled } from '@flighthq/render/contract';
|
|
3
|
+
import type { ColorScaleBias } from '@flighthq/types/contract';
|
|
4
4
|
|
|
5
5
|
import { registerGlColorAdjustmentMaterialFeature } from './glColorAdjustmentMaterialFeature';
|
|
6
|
-
import {
|
|
6
|
+
import { flushGlQuadBatchWriter, prepareGlQuadBatchWrite, recordGlQuadBatchColorScaleBias } from './glQuadBatchWriter';
|
|
7
7
|
import { standardGlMaterialRenderer } from './glStandardMaterial';
|
|
8
8
|
import { createGlState } from './glTestHelper';
|
|
9
9
|
|
|
10
|
-
function makeTexture():
|
|
11
|
-
return
|
|
10
|
+
function makeTexture(): WebGLTexture {
|
|
11
|
+
return {} as WebGLTexture;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function ct(
|
|
@@ -45,6 +45,7 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
45
45
|
const { state } = createGlState();
|
|
46
46
|
const runtime = getGlRenderStateRuntime(state);
|
|
47
47
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
48
|
+
expect(areColorAdjustmentsEnabled(state)).toBe(true);
|
|
48
49
|
expect(runtime.glColorAdjustmentMaterialFeature).not.toBeNull();
|
|
49
50
|
expect(runtime.glColorAdjustmentMaterialFeature).toBeDefined();
|
|
50
51
|
});
|
|
@@ -62,11 +63,11 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
62
63
|
const { state } = createGlState();
|
|
63
64
|
const runtime = getGlRenderStateRuntime(state);
|
|
64
65
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
expect(runtime.
|
|
68
|
-
expect(runtime.
|
|
69
|
-
expect(runtime.
|
|
66
|
+
recordGlQuadBatchColorScaleBias(state, null, 0);
|
|
67
|
+
recordGlQuadBatchColorScaleBias(state, null, 1);
|
|
68
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_NONE);
|
|
69
|
+
expect(runtime.quadBatchWriterColorTintData).toBeUndefined();
|
|
70
|
+
expect(runtime.quadBatchWriterColorScaleBiasData).toBeUndefined();
|
|
70
71
|
});
|
|
71
72
|
|
|
72
73
|
it('uses one whole-batch uniform when every instance shares one tint', () => {
|
|
@@ -74,43 +75,43 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
74
75
|
const runtime = getGlRenderStateRuntime(state);
|
|
75
76
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
76
77
|
const tint = ct(0.5);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
expect(runtime.
|
|
81
|
-
expect(runtime.
|
|
78
|
+
recordGlQuadBatchColorScaleBias(state, tint, 0);
|
|
79
|
+
recordGlQuadBatchColorScaleBias(state, tint, 1);
|
|
80
|
+
recordGlQuadBatchColorScaleBias(state, tint, 2);
|
|
81
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_UNIFORM);
|
|
82
|
+
expect(runtime.quadBatchWriterUniformColorScaleBias).toBe(tint);
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
it('keeps the uniform path for a distinct-but-equal tint', () => {
|
|
85
86
|
const { state } = createGlState();
|
|
86
87
|
const runtime = getGlRenderStateRuntime(state);
|
|
87
88
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
expect(runtime.
|
|
89
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 0);
|
|
90
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 1);
|
|
91
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_UNIFORM);
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
it('promotes varying multiply-only tints to packed RGBA8 without splitting', () => {
|
|
94
95
|
const { state } = createGlState();
|
|
95
96
|
const runtime = getGlRenderStateRuntime(state);
|
|
96
97
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
expect(runtime.
|
|
100
|
-
expect(Array.from(new Uint8Array(runtime.
|
|
98
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 0);
|
|
99
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.25), 1);
|
|
100
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_PACKED_TINT);
|
|
101
|
+
expect(Array.from(new Uint8Array(runtime.quadBatchWriterColorTintData!.buffer, 0, 8))).toEqual([
|
|
101
102
|
128, 255, 255, 255, 64, 255, 255, 255,
|
|
102
103
|
]);
|
|
103
|
-
expect(runtime.
|
|
104
|
+
expect(runtime.quadBatchWriterColorScaleBiasData).toBeUndefined();
|
|
104
105
|
});
|
|
105
106
|
|
|
106
107
|
it('promotes with identity fill when a tinted instance follows an untinted one', () => {
|
|
107
108
|
const { state } = createGlState();
|
|
108
109
|
const runtime = getGlRenderStateRuntime(state);
|
|
109
110
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
expect(runtime.
|
|
113
|
-
expect(Array.from(new Uint8Array(runtime.
|
|
111
|
+
recordGlQuadBatchColorScaleBias(state, null, 0);
|
|
112
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 1);
|
|
113
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_PACKED_TINT);
|
|
114
|
+
expect(Array.from(new Uint8Array(runtime.quadBatchWriterColorTintData!.buffer, 0, 8))).toEqual([
|
|
114
115
|
255, 255, 255, 255, 128, 255, 255, 255,
|
|
115
116
|
]);
|
|
116
117
|
});
|
|
@@ -119,43 +120,45 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
119
120
|
const { state } = createGlState();
|
|
120
121
|
const runtime = getGlRenderStateRuntime(state);
|
|
121
122
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
expect(Array.from(new Uint8Array(runtime.
|
|
123
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 0);
|
|
124
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.25), 1);
|
|
125
|
+
recordGlQuadBatchColorScaleBias(state, null, 2);
|
|
126
|
+
expect(Array.from(new Uint8Array(runtime.quadBatchWriterColorTintData!.buffer, 8, 4))).toEqual([
|
|
127
|
+
255, 255, 255, 255,
|
|
128
|
+
]);
|
|
126
129
|
});
|
|
127
130
|
|
|
128
131
|
it('stores normalized-linear color biases without conversion', () => {
|
|
129
132
|
const { state } = createGlState();
|
|
130
133
|
const runtime = getGlRenderStateRuntime(state);
|
|
131
134
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
expect(runtime.
|
|
135
|
-
expect(runtime.
|
|
135
|
+
recordGlQuadBatchColorScaleBias(state, ct(1, 1, 1, 1, 1, 0, 0, 0), 0);
|
|
136
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 1);
|
|
137
|
+
expect(runtime.quadBatchWriterColorScaleBiasData![4]).toBe(1);
|
|
138
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_PER_INSTANCE);
|
|
136
139
|
});
|
|
137
140
|
|
|
138
141
|
it('widens to a full matrix stream only when channel mixing appears', () => {
|
|
139
142
|
const { state } = createGlState();
|
|
140
143
|
const runtime = getGlRenderStateRuntime(state);
|
|
141
144
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
expect(runtime.
|
|
145
|
-
expect(runtime.
|
|
146
|
-
expect(runtime.
|
|
147
|
-
expect(runtime.
|
|
148
|
-
expect(runtime.
|
|
145
|
+
recordGlQuadBatchColorScaleBias(state, { tint: 0x808080ff }, 0);
|
|
146
|
+
recordGlQuadBatchColorScaleBias(state, MIX_RED_GREEN, 1);
|
|
147
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_MATRIX);
|
|
148
|
+
expect(runtime.quadBatchWriterColorMatrixData![0]).toBeCloseTo(128 / 255);
|
|
149
|
+
expect(runtime.quadBatchWriterColorMatrixData![20]).toBe(1);
|
|
150
|
+
expect(runtime.quadBatchWriterColorMatrixData![21]).toBe(0.5);
|
|
151
|
+
expect(runtime.quadBatchWriterColorMatrixData![36]).toBe(0.25);
|
|
149
152
|
});
|
|
150
153
|
|
|
151
154
|
it('records compact per-item tint data directly as four bytes', () => {
|
|
152
155
|
const { state } = createGlState();
|
|
153
156
|
const runtime = getGlRenderStateRuntime(state);
|
|
154
157
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
expect(runtime.
|
|
158
|
-
expect(Array.from(new Uint8Array(runtime.
|
|
158
|
+
recordGlQuadBatchColorScaleBias(state, null, 0);
|
|
159
|
+
recordGlQuadBatchColorScaleBias(state, { tint: 0x12345678 }, 1);
|
|
160
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_PACKED_TINT);
|
|
161
|
+
expect(Array.from(new Uint8Array(runtime.quadBatchWriterColorTintData!.buffer, 4, 4))).toEqual([
|
|
159
162
|
0x12, 0x34, 0x56, 0x78,
|
|
160
163
|
]);
|
|
161
164
|
});
|
|
@@ -164,10 +167,10 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
164
167
|
const { state, gl } = createGlState();
|
|
165
168
|
const runtime = getGlRenderStateRuntime(state);
|
|
166
169
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
runtime.
|
|
170
|
-
|
|
170
|
+
prepareGlQuadBatchWrite(state, makeTexture(), false, null, null, null, standardGlMaterialRenderer, 1);
|
|
171
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 0);
|
|
172
|
+
runtime.quadBatchWriterCount = 1;
|
|
173
|
+
flushGlQuadBatchWriter(state);
|
|
171
174
|
expect(gl.uniform4f).toHaveBeenCalled();
|
|
172
175
|
expect(gl.drawElementsInstanced).toHaveBeenCalled();
|
|
173
176
|
});
|
|
@@ -176,12 +179,12 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
176
179
|
const { state, gl } = createGlState();
|
|
177
180
|
const runtime = getGlRenderStateRuntime(state);
|
|
178
181
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
runtime.
|
|
183
|
-
|
|
184
|
-
expect(runtime.
|
|
182
|
+
prepareGlQuadBatchWrite(state, makeTexture(), false, null, null, null, standardGlMaterialRenderer, 2);
|
|
183
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.5), 0);
|
|
184
|
+
recordGlQuadBatchColorScaleBias(state, ct(0.25), 1);
|
|
185
|
+
runtime.quadBatchWriterCount = 2;
|
|
186
|
+
flushGlQuadBatchWriter(state);
|
|
187
|
+
expect(runtime.quadBatchWriterColorScaleBiasBuffer).not.toBeNull();
|
|
185
188
|
expect(gl.vertexAttribPointer).toHaveBeenCalledWith(7, 4, gl.UNSIGNED_BYTE, true, 4, 0);
|
|
186
189
|
expect(gl.drawElementsInstanced).toHaveBeenCalledWith(expect.anything(), 6, expect.anything(), 0, 2);
|
|
187
190
|
});
|
|
@@ -190,11 +193,11 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
190
193
|
const { state, gl } = createGlState();
|
|
191
194
|
const runtime = getGlRenderStateRuntime(state);
|
|
192
195
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
runtime.
|
|
197
|
-
|
|
196
|
+
prepareGlQuadBatchWrite(state, makeTexture(), false, null, null, null, standardGlMaterialRenderer, 2);
|
|
197
|
+
recordGlQuadBatchColorScaleBias(state, null, 0);
|
|
198
|
+
recordGlQuadBatchColorScaleBias(state, MIX_RED_GREEN, 1);
|
|
199
|
+
runtime.quadBatchWriterCount = 2;
|
|
200
|
+
flushGlQuadBatchWriter(state);
|
|
198
201
|
expect(gl.vertexAttribPointer).toHaveBeenCalledWith(7, 4, gl.FLOAT, false, 80, 0);
|
|
199
202
|
expect(gl.vertexAttribPointer).toHaveBeenCalledWith(11, 4, gl.FLOAT, false, 80, 64);
|
|
200
203
|
});
|
|
@@ -203,22 +206,22 @@ describe('registerGlColorAdjustmentMaterialFeature', () => {
|
|
|
203
206
|
const { state } = createGlState();
|
|
204
207
|
const runtime = getGlRenderStateRuntime(state);
|
|
205
208
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
runtime.
|
|
209
|
-
|
|
210
|
-
expect(runtime.
|
|
209
|
+
prepareGlQuadBatchWrite(state, makeTexture(), false, null, null, null, standardGlMaterialRenderer, 1);
|
|
210
|
+
recordGlQuadBatchColorScaleBias(state, MIX_RED_GREEN, 0);
|
|
211
|
+
runtime.quadBatchWriterCount = 1;
|
|
212
|
+
flushGlQuadBatchWriter(state);
|
|
213
|
+
expect(runtime.quadBatchWriterColorScaleBiasMode).toBe(CT_MODE_NONE);
|
|
211
214
|
});
|
|
212
215
|
|
|
213
216
|
it('leaves the lean base shader untouched for an untinted batch on flush', () => {
|
|
214
217
|
const { state, gl } = createGlState();
|
|
215
218
|
const runtime = getGlRenderStateRuntime(state);
|
|
216
219
|
registerGlColorAdjustmentMaterialFeature(state);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
runtime.
|
|
220
|
-
|
|
221
|
-
expect(runtime.
|
|
220
|
+
prepareGlQuadBatchWrite(state, makeTexture(), false, null, null, null, standardGlMaterialRenderer, 1);
|
|
221
|
+
recordGlQuadBatchColorScaleBias(state, null, 0);
|
|
222
|
+
runtime.quadBatchWriterCount = 1;
|
|
223
|
+
flushGlQuadBatchWriter(state);
|
|
224
|
+
expect(runtime.quadBatchWriterColorScaleBiasBuffer ?? null).toBeNull();
|
|
222
225
|
expect(gl.drawElementsInstanced).toHaveBeenCalled();
|
|
223
226
|
});
|
|
224
227
|
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type * as FlightNodeModule from '@flighthq/node/contract';
|
|
2
|
+
import { enableRenderRegistryGuards, explainRenderRegistryMisses } from '@flighthq/render/contract';
|
|
3
|
+
import {
|
|
4
|
+
appendShapeBeginFill,
|
|
5
|
+
appendShapeBeginGradientFill,
|
|
6
|
+
appendShapeEndFill,
|
|
7
|
+
appendShapeRectangle,
|
|
8
|
+
createShape,
|
|
9
|
+
} from '@flighthq/shape/contract';
|
|
10
|
+
import type { RenderProxy2D } from '@flighthq/types/contract';
|
|
11
|
+
import { BatchFormat, RenderRegistry } from '@flighthq/types/contract';
|
|
12
|
+
|
|
13
|
+
import type * as GlMeshShapeModule from './glMeshShapeRenderer';
|
|
14
|
+
import { registerGlShapeRasterizer } from './glShapeRasterizer';
|
|
15
|
+
import { createGlState } from './glTestHelper';
|
|
16
|
+
import { scopeModuleMocks } from './moduleMockTestHelper';
|
|
17
|
+
|
|
18
|
+
let defaultGlMeshShapeRenderer: typeof GlMeshShapeModule.defaultGlMeshShapeRenderer;
|
|
19
|
+
let drawGlMeshShape: typeof GlMeshShapeModule.drawGlMeshShape;
|
|
20
|
+
|
|
21
|
+
scopeModuleMocks(['@flighthq/node']);
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
vi.doMock('@flighthq/node/contract', async (importOriginal) => ({
|
|
25
|
+
...(await importOriginal<typeof FlightNodeModule>()),
|
|
26
|
+
getNodeLocalBoundsRectangle: () => ({ x: 0, y: 0, width: 64, height: 48 }),
|
|
27
|
+
getNodeLocalContentRevision: (source: any) => source?.data?.version ?? 0,
|
|
28
|
+
}));
|
|
29
|
+
({ defaultGlMeshShapeRenderer, drawGlMeshShape } = await import('./glMeshShapeRenderer'));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function makeShapeData() {
|
|
33
|
+
return { surface: null, lastContentId: -1, lastPixelRatio: 0, lastW: 0, lastH: 0, meshVersion: -1, meshes: null };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function makeShapeNode(data: Record<string, unknown>, rendererData: unknown = makeShapeData()): RenderProxy2D {
|
|
37
|
+
return {
|
|
38
|
+
source: { data: { commands: [], version: 0, ...data } },
|
|
39
|
+
rendererData,
|
|
40
|
+
blendMode: 0,
|
|
41
|
+
alpha: 1,
|
|
42
|
+
material: null,
|
|
43
|
+
materialData: null,
|
|
44
|
+
transform2D: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 },
|
|
45
|
+
} as unknown as RenderProxy2D;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function solidShape() {
|
|
49
|
+
const shape = createShape();
|
|
50
|
+
appendShapeBeginFill(shape, 0x00cc00);
|
|
51
|
+
appendShapeRectangle(shape, 8, 8, 32, 24);
|
|
52
|
+
appendShapeEndFill(shape);
|
|
53
|
+
return shape;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function gradientShape() {
|
|
57
|
+
const shape = createShape();
|
|
58
|
+
appendShapeBeginGradientFill(shape, 'linear', [0xff0000, 0x0000ff], [1, 1], [0, 255], null);
|
|
59
|
+
appendShapeRectangle(shape, 8, 8, 32, 24);
|
|
60
|
+
appendShapeEndFill(shape);
|
|
61
|
+
return shape;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe('defaultGlMeshShapeRenderer', () => {
|
|
65
|
+
it('declares BatchFormat.Quad and the shared shape data lifecycle', () => {
|
|
66
|
+
expect(defaultGlMeshShapeRenderer.format).toBe(BatchFormat.Quad);
|
|
67
|
+
expect(typeof defaultGlMeshShapeRenderer.createData).toBe('function');
|
|
68
|
+
expect(typeof defaultGlMeshShapeRenderer.destroyData).toBe('function');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('never rasterizes, even with a rasterizer registered and a fill that cannot tessellate', () => {
|
|
72
|
+
// The whole point of pinning this strategy: a registered rasterizer is not consulted, so an app can
|
|
73
|
+
// choose the GPU path and know the canvas replay is dead weight it did not ship.
|
|
74
|
+
const { state } = createGlState();
|
|
75
|
+
const rasterizer = vi.fn();
|
|
76
|
+
registerGlShapeRasterizer(state, rasterizer);
|
|
77
|
+
defaultGlMeshShapeRenderer.submit!(state, makeShapeNode({ commands: gradientShape().data.commands, version: 1 }));
|
|
78
|
+
expect(rasterizer).not.toHaveBeenCalled();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('reports a ShapeRasterizer miss rather than silently dropping an untessellatable fill', () => {
|
|
82
|
+
const { state } = createGlState();
|
|
83
|
+
enableRenderRegistryGuards(state);
|
|
84
|
+
defaultGlMeshShapeRenderer.submit!(state, makeShapeNode({ commands: gradientShape().data.commands, version: 1 }));
|
|
85
|
+
expect(explainRenderRegistryMisses(state).misses).toContainEqual({
|
|
86
|
+
kind: 'Shape',
|
|
87
|
+
registry: RenderRegistry.ShapeRasterizer,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('drawGlMeshShape', () => {
|
|
93
|
+
it('draws a solid fill as a GPU mesh and reports that it drew', () => {
|
|
94
|
+
const { state, gl } = createGlState();
|
|
95
|
+
const drew = drawGlMeshShape(state, makeShapeNode({ commands: solidShape().data.commands, version: 1 }));
|
|
96
|
+
expect(drew).toBe(true);
|
|
97
|
+
expect(gl.drawElements).toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('reports false for a fill with no tessellated form, which is the hybrid fall-through signal', () => {
|
|
101
|
+
const { state } = createGlState();
|
|
102
|
+
expect(drawGlMeshShape(state, makeShapeNode({ commands: gradientShape().data.commands, version: 1 }))).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('reports false for an empty command list and for absent renderer data', () => {
|
|
106
|
+
const { state } = createGlState();
|
|
107
|
+
expect(drawGlMeshShape(state, makeShapeNode({ commands: [], version: 1 }))).toBe(false);
|
|
108
|
+
expect(drawGlMeshShape(state, makeShapeNode({ commands: solidShape().data.commands, version: 1 }, null))).toBe(
|
|
109
|
+
false,
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -1,17 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { createImageResource } from '@flighthq/image/contract';
|
|
2
|
+
import {
|
|
3
|
+
getGlRenderStateRuntime,
|
|
4
|
+
registerGlCompressedImageTextureResolver,
|
|
5
|
+
registerGlImageTextureResolver,
|
|
6
|
+
} from '@flighthq/render-gl/contract';
|
|
7
|
+
import { createTexture } from '@flighthq/texture/contract';
|
|
8
|
+
import type { CompressedImage, RenderProxy2D } from '@flighthq/types/contract';
|
|
9
|
+
import { CompressedImageTextureSourceKind } from '@flighthq/types/contract';
|
|
3
10
|
|
|
4
11
|
import { defaultGlParticleEmitter2DRenderer, drawGlParticleEmitter2D } from './glParticleEmitter2D';
|
|
5
12
|
import { createGlState } from './glTestHelper';
|
|
6
13
|
|
|
7
14
|
function makeAtlas() {
|
|
8
15
|
const img = document.createElement('img');
|
|
16
|
+
const image = createImageResource(img);
|
|
17
|
+
image.width = 64;
|
|
18
|
+
image.height = 64;
|
|
9
19
|
return {
|
|
10
|
-
image: { source: img, width: 64, height: 64 },
|
|
11
20
|
regions: [{ id: 0, x: 0, y: 0, width: 32, height: 32 }],
|
|
21
|
+
texture: createTexture({ dimension: '2d', source: image }),
|
|
12
22
|
};
|
|
13
23
|
}
|
|
14
24
|
|
|
25
|
+
function createAtlasGlState() {
|
|
26
|
+
const result = createGlState();
|
|
27
|
+
registerGlImageTextureResolver(result.state);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
15
31
|
function makeParticleEmitter2DNode(data: Record<string, unknown> = {}): RenderProxy2D {
|
|
16
32
|
return {
|
|
17
33
|
source: {
|
|
@@ -43,34 +59,31 @@ describe('defaultGlParticleEmitter2DRenderer', () => {
|
|
|
43
59
|
|
|
44
60
|
describe('drawGlParticleEmitter2D', () => {
|
|
45
61
|
it('returns early without drawing when atlas is null', () => {
|
|
46
|
-
const { state, gl } =
|
|
62
|
+
const { state, gl } = createAtlasGlState();
|
|
47
63
|
drawGlParticleEmitter2D(state, makeParticleEmitter2DNode({ atlas: null }));
|
|
48
64
|
expect(gl.drawElements).not.toHaveBeenCalled();
|
|
49
65
|
});
|
|
50
66
|
|
|
51
|
-
it('returns early without drawing when atlas.
|
|
52
|
-
const { state, gl } =
|
|
53
|
-
drawGlParticleEmitter2D(state, makeParticleEmitter2DNode({ atlas: {
|
|
67
|
+
it('returns early without drawing when atlas.texture is null', () => {
|
|
68
|
+
const { state, gl } = createAtlasGlState();
|
|
69
|
+
drawGlParticleEmitter2D(state, makeParticleEmitter2DNode({ atlas: { regions: [], texture: null } }));
|
|
54
70
|
expect(gl.drawElements).not.toHaveBeenCalled();
|
|
55
71
|
});
|
|
56
72
|
|
|
57
|
-
it('returns early without drawing when atlas
|
|
58
|
-
const { state, gl } =
|
|
59
|
-
drawGlParticleEmitter2D(
|
|
60
|
-
state,
|
|
61
|
-
makeParticleEmitter2DNode({ atlas: { image: { source: null, data: null, compressed: null }, regions: [] } }),
|
|
62
|
-
);
|
|
73
|
+
it('returns early without drawing when atlas Texture is unbound', () => {
|
|
74
|
+
const { state, gl } = createAtlasGlState();
|
|
75
|
+
drawGlParticleEmitter2D(state, makeParticleEmitter2DNode({ atlas: { regions: [], texture: createTexture() } }));
|
|
63
76
|
expect(gl.drawElements).not.toHaveBeenCalled();
|
|
64
77
|
});
|
|
65
78
|
|
|
66
79
|
it('returns early without drawing when particleCount is 0', () => {
|
|
67
|
-
const { state, gl } =
|
|
80
|
+
const { state, gl } = createAtlasGlState();
|
|
68
81
|
drawGlParticleEmitter2D(state, makeParticleEmitter2DNode({ particleCount: 0 }));
|
|
69
82
|
expect(gl.drawElementsInstanced).not.toHaveBeenCalled();
|
|
70
83
|
});
|
|
71
84
|
|
|
72
85
|
it('draws all live particles in a single instanced draw call', () => {
|
|
73
|
-
const { state, gl } =
|
|
86
|
+
const { state, gl } = createAtlasGlState();
|
|
74
87
|
drawGlParticleEmitter2D(
|
|
75
88
|
state,
|
|
76
89
|
makeParticleEmitter2DNode({
|
|
@@ -86,7 +99,7 @@ describe('drawGlParticleEmitter2D', () => {
|
|
|
86
99
|
});
|
|
87
100
|
|
|
88
101
|
it('skips out-of-range region ids and draws only valid particles', () => {
|
|
89
|
-
const { state, gl } =
|
|
102
|
+
const { state, gl } = createAtlasGlState();
|
|
90
103
|
drawGlParticleEmitter2D(
|
|
91
104
|
state,
|
|
92
105
|
makeParticleEmitter2DNode({
|
|
@@ -101,21 +114,24 @@ describe('drawGlParticleEmitter2D', () => {
|
|
|
101
114
|
});
|
|
102
115
|
|
|
103
116
|
it('uploads the straight-alpha flag for a compressed particle atlas', () => {
|
|
104
|
-
const { state, gl } =
|
|
117
|
+
const { state, gl } = createAtlasGlState();
|
|
118
|
+
registerGlCompressedImageTextureResolver(state);
|
|
119
|
+
getGlRenderStateRuntime(state).compressedTextureUpload = () => true;
|
|
105
120
|
const image = {
|
|
106
|
-
alphaType: 'straight',
|
|
107
121
|
compressed: { container: {}, payload: new Uint8Array() },
|
|
108
|
-
data: null,
|
|
109
122
|
height: 4,
|
|
110
|
-
|
|
123
|
+
kind: CompressedImageTextureSourceKind,
|
|
111
124
|
version: 1,
|
|
112
125
|
width: 4,
|
|
113
|
-
};
|
|
126
|
+
} as unknown as CompressedImage;
|
|
114
127
|
|
|
115
128
|
drawGlParticleEmitter2D(
|
|
116
129
|
state,
|
|
117
130
|
makeParticleEmitter2DNode({
|
|
118
|
-
atlas: {
|
|
131
|
+
atlas: {
|
|
132
|
+
regions: [{ id: 0, x: 0, y: 0, width: 4, height: 4 }],
|
|
133
|
+
texture: createTexture({ dimension: '2d', source: image }),
|
|
134
|
+
},
|
|
119
135
|
}),
|
|
120
136
|
);
|
|
121
137
|
|
package/src/glQuadBatch.test.ts
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import type { RenderProxy2D } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
3
|
import { defaultGlQuadBatchRenderer } from './glQuadBatch';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { flushGlQuadBatchWriter } from './glQuadBatchWriter';
|
|
5
|
+
import { registerGlStandardMaterial } from './glStandardMaterial';
|
|
6
6
|
import { createGlState } from './glTestHelper';
|
|
7
7
|
|
|
8
8
|
function makeAtlas() {
|
|
9
9
|
const img = document.createElement('img');
|
|
10
|
+
const image = createImageResource(img);
|
|
11
|
+
image.width = 64;
|
|
12
|
+
image.height = 64;
|
|
10
13
|
return {
|
|
11
|
-
image: { source: img, width: 64, height: 64 },
|
|
12
14
|
regions: [{ x: 0, y: 0, width: 32, height: 32 }],
|
|
15
|
+
texture: createTexture({ dimension: '2d', source: image }),
|
|
13
16
|
};
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
function createAtlasGlState() {
|
|
20
|
+
const result = createGlState();
|
|
21
|
+
registerGlImageTextureResolver(result.state);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
function makeQuadBatchNode(data: Record<string, unknown> = {}): RenderProxy2D {
|
|
17
26
|
return {
|
|
18
27
|
source: {
|
|
@@ -47,40 +56,40 @@ describe('defaultGlQuadBatchRenderer', () => {
|
|
|
47
56
|
|
|
48
57
|
describe('defaultGlQuadBatchRenderer.submit', () => {
|
|
49
58
|
it('returns early without drawing when atlas is null', () => {
|
|
50
|
-
const { state, gl } =
|
|
51
|
-
|
|
59
|
+
const { state, gl } = createAtlasGlState();
|
|
60
|
+
registerGlStandardMaterial(state);
|
|
52
61
|
defaultGlQuadBatchRenderer.submit(state, makeQuadBatchNode({ atlas: null }));
|
|
53
|
-
|
|
62
|
+
flushGlQuadBatchWriter(state as any);
|
|
54
63
|
expect(gl.drawElementsInstanced).not.toHaveBeenCalled();
|
|
55
64
|
});
|
|
56
65
|
|
|
57
|
-
it('returns early without drawing when atlas.
|
|
58
|
-
const { state, gl } =
|
|
59
|
-
|
|
60
|
-
defaultGlQuadBatchRenderer.submit(state, makeQuadBatchNode({ atlas: {
|
|
61
|
-
|
|
66
|
+
it('returns early without drawing when atlas.texture is null', () => {
|
|
67
|
+
const { state, gl } = createAtlasGlState();
|
|
68
|
+
registerGlStandardMaterial(state);
|
|
69
|
+
defaultGlQuadBatchRenderer.submit(state, makeQuadBatchNode({ atlas: { regions: [], texture: null } }));
|
|
70
|
+
flushGlQuadBatchWriter(state as any);
|
|
62
71
|
expect(gl.drawElementsInstanced).not.toHaveBeenCalled();
|
|
63
72
|
});
|
|
64
73
|
|
|
65
|
-
it('returns early without drawing when atlas
|
|
66
|
-
const { state, gl } =
|
|
67
|
-
|
|
68
|
-
defaultGlQuadBatchRenderer.submit(state, makeQuadBatchNode({ atlas: {
|
|
69
|
-
|
|
74
|
+
it('returns early without drawing when atlas Texture is unbound', () => {
|
|
75
|
+
const { state, gl } = createAtlasGlState();
|
|
76
|
+
registerGlStandardMaterial(state);
|
|
77
|
+
defaultGlQuadBatchRenderer.submit(state, makeQuadBatchNode({ atlas: { regions: [], texture: createTexture() } }));
|
|
78
|
+
flushGlQuadBatchWriter(state as any);
|
|
70
79
|
expect(gl.drawElementsInstanced).not.toHaveBeenCalled();
|
|
71
80
|
});
|
|
72
81
|
|
|
73
82
|
it('returns early without drawing when instanceCount is 0', () => {
|
|
74
|
-
const { state, gl } =
|
|
75
|
-
|
|
83
|
+
const { state, gl } = createAtlasGlState();
|
|
84
|
+
registerGlStandardMaterial(state);
|
|
76
85
|
defaultGlQuadBatchRenderer.submit(state, makeQuadBatchNode({ instanceCount: 0 }));
|
|
77
|
-
|
|
86
|
+
flushGlQuadBatchWriter(state as any);
|
|
78
87
|
expect(gl.drawElementsInstanced).not.toHaveBeenCalled();
|
|
79
88
|
});
|
|
80
89
|
|
|
81
90
|
it('draws all valid instances in a single instanced call with vector2 transform type', () => {
|
|
82
|
-
const { state, gl } =
|
|
83
|
-
|
|
91
|
+
const { state, gl } = createAtlasGlState();
|
|
92
|
+
registerGlStandardMaterial(state);
|
|
84
93
|
defaultGlQuadBatchRenderer.submit(
|
|
85
94
|
state,
|
|
86
95
|
makeQuadBatchNode({
|
|
@@ -90,14 +99,14 @@ describe('defaultGlQuadBatchRenderer.submit', () => {
|
|
|
90
99
|
transformType: 'vector2',
|
|
91
100
|
}),
|
|
92
101
|
);
|
|
93
|
-
|
|
102
|
+
flushGlQuadBatchWriter(state as any);
|
|
94
103
|
expect(gl.drawElementsInstanced).toHaveBeenCalledTimes(1);
|
|
95
104
|
expect(gl.drawElementsInstanced).toHaveBeenCalledWith(expect.anything(), 6, expect.anything(), 0, 2);
|
|
96
105
|
});
|
|
97
106
|
|
|
98
107
|
it('excludes out-of-range ids from the instanced draw count', () => {
|
|
99
|
-
const { state, gl } =
|
|
100
|
-
|
|
108
|
+
const { state, gl } = createAtlasGlState();
|
|
109
|
+
registerGlStandardMaterial(state);
|
|
101
110
|
defaultGlQuadBatchRenderer.submit(
|
|
102
111
|
state,
|
|
103
112
|
makeQuadBatchNode({
|
|
@@ -107,14 +116,14 @@ describe('defaultGlQuadBatchRenderer.submit', () => {
|
|
|
107
116
|
transformType: 'vector2',
|
|
108
117
|
}),
|
|
109
118
|
);
|
|
110
|
-
|
|
119
|
+
flushGlQuadBatchWriter(state as any);
|
|
111
120
|
expect(gl.drawElementsInstanced).toHaveBeenCalledTimes(1);
|
|
112
121
|
expect(gl.drawElementsInstanced).toHaveBeenCalledWith(expect.anything(), 6, expect.anything(), 0, 2);
|
|
113
122
|
});
|
|
114
123
|
|
|
115
124
|
it('draws all valid instances in a single instanced call with full matrix transform type', () => {
|
|
116
|
-
const { state, gl } =
|
|
117
|
-
|
|
125
|
+
const { state, gl } = createAtlasGlState();
|
|
126
|
+
registerGlStandardMaterial(state);
|
|
118
127
|
const transforms = new Float32Array([1, 0, 0, 1, 0, 0]);
|
|
119
128
|
defaultGlQuadBatchRenderer.submit(
|
|
120
129
|
state,
|
|
@@ -125,8 +134,11 @@ describe('defaultGlQuadBatchRenderer.submit', () => {
|
|
|
125
134
|
transformType: 'matrix',
|
|
126
135
|
}),
|
|
127
136
|
);
|
|
128
|
-
|
|
137
|
+
flushGlQuadBatchWriter(state as any);
|
|
129
138
|
expect(gl.drawElementsInstanced).toHaveBeenCalledTimes(1);
|
|
130
139
|
expect(gl.drawElementsInstanced).toHaveBeenCalledWith(expect.anything(), 6, expect.anything(), 0, 1);
|
|
131
140
|
});
|
|
132
141
|
});
|
|
142
|
+
import { createImageResource } from '@flighthq/image/contract';
|
|
143
|
+
import { registerGlImageTextureResolver } from '@flighthq/render-gl/contract';
|
|
144
|
+
import { createTexture } from '@flighthq/texture/contract';
|