@flighthq/render-gl 0.5.1-next.1069.554a2ac → 0.5.1-next.1201.9b49dee

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.
Files changed (82) hide show
  1. package/README.md +2 -2
  2. package/dist/contract.d.ts +1 -3
  3. package/dist/contract.d.ts.map +1 -1
  4. package/dist/contract.js +1 -3
  5. package/dist/contract.js.map +1 -1
  6. package/dist/enableGlRenderStateGuards.js +2 -2
  7. package/dist/enableGlRenderStateGuards.js.map +1 -1
  8. package/dist/glCubeRenderTarget.d.ts +5 -5
  9. package/dist/glCubeRenderTarget.d.ts.map +1 -1
  10. package/dist/glCubeRenderTarget.js +16 -13
  11. package/dist/glCubeRenderTarget.js.map +1 -1
  12. package/dist/glFullscreenPass.d.ts +4 -3
  13. package/dist/glFullscreenPass.d.ts.map +1 -1
  14. package/dist/glFullscreenPass.js +152 -23
  15. package/dist/glFullscreenPass.js.map +1 -1
  16. package/dist/glLinearToSrgbPass.d.ts +2 -2
  17. package/dist/glLinearToSrgbPass.d.ts.map +1 -1
  18. package/dist/glLinearToSrgbPass.js.map +1 -1
  19. package/dist/glPresentRenderTarget.d.ts +2 -2
  20. package/dist/glPresentRenderTarget.d.ts.map +1 -1
  21. package/dist/glPresentRenderTarget.js.map +1 -1
  22. package/dist/glReadback.d.ts +2 -2
  23. package/dist/glReadback.d.ts.map +1 -1
  24. package/dist/glReadback.js +1 -1
  25. package/dist/glReadback.js.map +1 -1
  26. package/dist/glRenderPass.d.ts +6 -3
  27. package/dist/glRenderPass.d.ts.map +1 -1
  28. package/dist/glRenderPass.js +105 -86
  29. package/dist/glRenderPass.js.map +1 -1
  30. package/dist/glRenderState.d.ts +1 -2
  31. package/dist/glRenderState.d.ts.map +1 -1
  32. package/dist/glRenderState.js +53 -46
  33. package/dist/glRenderState.js.map +1 -1
  34. package/dist/glRenderTarget.d.ts +17 -16
  35. package/dist/glRenderTarget.d.ts.map +1 -1
  36. package/dist/glRenderTarget.js +38 -26
  37. package/dist/glRenderTarget.js.map +1 -1
  38. package/dist/glRenderTargetPool.d.ts +9 -9
  39. package/dist/glRenderTargetPool.d.ts.map +1 -1
  40. package/dist/glRenderTargetPool.js +10 -12
  41. package/dist/glRenderTargetPool.js.map +1 -1
  42. package/dist/glRenderTexture.d.ts +2 -2
  43. package/dist/glRenderTexture.d.ts.map +1 -1
  44. package/dist/glRenderTexture.js +11 -13
  45. package/dist/glRenderTexture.js.map +1 -1
  46. package/dist/glRenderTexturePool.js +3 -5
  47. package/dist/glRenderTexturePool.js.map +1 -1
  48. package/dist/glSkinPaletteTexture.js +8 -2
  49. package/dist/glSkinPaletteTexture.js.map +1 -1
  50. package/dist/glTestHelper.d.ts +0 -1
  51. package/dist/glTestHelper.d.ts.map +1 -1
  52. package/dist/glTestHelper.js +0 -1
  53. package/dist/glTestHelper.js.map +1 -1
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +1 -1
  57. package/dist/index.js.map +1 -1
  58. package/package.json +11 -11
  59. package/src/enableGlRenderStateGuards.test.ts +2 -4
  60. package/src/glContext.test.ts +3 -52
  61. package/src/glCubeRenderTarget.test.ts +10 -8
  62. package/src/glDraw.test.ts +4 -16
  63. package/src/glFullscreenPass.test.ts +145 -41
  64. package/src/glLinearToSrgbPass.test.ts +8 -5
  65. package/src/glPresentRenderTarget.test.ts +3 -5
  66. package/src/glReadback.test.ts +3 -3
  67. package/src/glRenderPass.test.ts +228 -55
  68. package/src/glRenderState.test.ts +106 -165
  69. package/src/glRenderTarget.test.ts +134 -99
  70. package/src/glRenderTargetPool.test.ts +69 -76
  71. package/src/glRenderTexture.test.ts +58 -31
  72. package/src/glSkinPaletteTexture.test.ts +9 -0
  73. package/src/glTextureResolver.test.ts +5 -1
  74. package/dist/glBackground.d.ts +0 -3
  75. package/dist/glBackground.d.ts.map +0 -1
  76. package/dist/glBackground.js +0 -19
  77. package/dist/glBackground.js.map +0 -1
  78. package/dist/glContext.d.ts +0 -3
  79. package/dist/glContext.d.ts.map +0 -1
  80. package/dist/glContext.js +0 -16
  81. package/dist/glContext.js.map +0 -1
  82. package/src/glBackground.test.ts +0 -68
@@ -6,18 +6,19 @@ import { EntityRuntimeKey } from '@flighthq/types/contract';
6
6
  import { beginGlRenderPass, endGlRenderPass } from './glRenderPass';
7
7
  import { getGlRenderStateRuntime } from './glRenderState';
8
8
  import {
9
- createGlRenderTarget,
9
+ createGlScreenRenderTarget,
10
+ createGlTextureRenderTarget,
10
11
  declareGlRenderTargetColorSpace,
11
- destroyGlRenderTarget,
12
- drawGlRenderTargetResult,
13
- explainGlRenderTarget,
14
- initializeGlRenderTarget,
12
+ destroyGlTextureRenderTarget,
13
+ drawGlTextureRenderTargetResult,
14
+ explainGlTextureRenderTarget,
15
+ initializeGlTextureRenderTarget,
15
16
  isGlRenderTargetFormatSupported,
16
- resizeGlRenderTarget,
17
- resolveGlRenderTarget,
17
+ resizeGlTextureRenderTarget,
18
+ resolveGlTextureRenderTarget,
18
19
  resolveGlRenderTargetAxes,
19
20
  } from './glRenderTarget';
20
- import { createGlState } from './glTestHelper';
21
+ import { createGlState, makeGL } from './glTestHelper';
21
22
 
22
23
  function makeState() {
23
24
  const { state: _state, gl: _gl } = createGlState();
@@ -44,38 +45,66 @@ function makeState() {
44
45
  return { state, gl };
45
46
  }
46
47
 
47
- describe('createGlRenderTarget', () => {
48
+ describe('createGlScreenRenderTarget', () => {
49
+ it('returns a target with framebuffer null', () => {
50
+ const gl = makeGL(320, 240);
51
+ const target = createGlScreenRenderTarget(gl);
52
+ expect(target.framebuffer).toBeNull();
53
+ });
54
+
55
+ it('reads dimensions from drawingBufferWidth and drawingBufferHeight', () => {
56
+ const gl = makeGL(320, 240);
57
+ const target = createGlScreenRenderTarget(gl);
58
+ expect(target.width).toBe(320);
59
+ expect(target.height).toBe(240);
60
+ });
61
+
62
+ it('returns an Entity-backed target', () => {
63
+ const gl = makeGL();
64
+ const target = createGlScreenRenderTarget(gl);
65
+ expect(Object.hasOwn(target, EntityRuntimeKey)).toBe(true);
66
+ });
67
+
68
+ it('sets colorAttachments to 1 and colorSpace to srgb', () => {
69
+ const gl = makeGL();
70
+ const target = createGlScreenRenderTarget(gl);
71
+ expect(target.colorAttachments).toBe(1);
72
+ expect(target.colorSpace).toBe('srgb');
73
+ });
74
+ });
75
+
76
+ describe('createGlTextureRenderTarget', () => {
48
77
  it('returns a render target with the requested dimensions', () => {
49
78
  const { state } = makeState();
50
- const target = createGlRenderTarget(state, { width: 128, height: 64 });
79
+ const target = createGlTextureRenderTarget(state, { width: 128, height: 64 });
51
80
  expect(target.width).toBe(128);
52
81
  expect(target.height).toBe(64);
53
82
  });
54
83
 
55
84
  it('returns an Entity-backed render target', () => {
56
85
  const { state } = makeState();
57
- const target = createGlRenderTarget(state, { width: 128, height: 64 });
86
+ const target = createGlTextureRenderTarget(state, { width: 128, height: 64 });
58
87
  expect(Object.hasOwn(target, EntityRuntimeKey)).toBe(true);
59
88
  expect(target[EntityRuntimeKey]).toBeUndefined();
60
89
  });
61
90
 
62
91
  it('enforces a minimum size of 1', () => {
63
92
  const { state } = makeState();
64
- const target = createGlRenderTarget(state, { width: 0, height: 0 });
93
+ const target = createGlTextureRenderTarget(state, { width: 0, height: 0 });
65
94
  expect(target.width).toBe(1);
66
95
  expect(target.height).toBe(1);
67
96
  });
68
97
 
69
98
  it('ceils fractional dimensions', () => {
70
99
  const { state } = makeState();
71
- const target = createGlRenderTarget(state, { width: 10.3, height: 20.9 });
100
+ const target = createGlTextureRenderTarget(state, { width: 10.3, height: 20.9 });
72
101
  expect(target.width).toBe(11);
73
102
  expect(target.height).toBe(21);
74
103
  });
75
104
 
76
105
  it('calls createFramebuffer and createTexture', () => {
77
106
  const { state, gl } = makeState();
78
- createGlRenderTarget(state, { width: 64, height: 64 });
107
+ createGlTextureRenderTarget(state, { width: 64, height: 64 });
79
108
  expect(vi.mocked(gl.createFramebuffer)).toHaveBeenCalled();
80
109
  expect(vi.mocked(gl.createTexture)).toHaveBeenCalled();
81
110
  });
@@ -84,26 +113,26 @@ describe('createGlRenderTarget', () => {
84
113
  const { state } = makeState();
85
114
  const runtime = getGlRenderStateRuntime(state);
86
115
  runtime.context.currentTextureRealization = { straightAlpha: false, texture: {} as WebGLTexture };
87
- createGlRenderTarget(state, { width: 32, height: 32 });
116
+ createGlTextureRenderTarget(state, { width: 32, height: 32 });
88
117
  expect(runtime.context.currentTextureRealization).toBeNull();
89
118
  });
90
119
 
91
120
  it("defaults colorSpace to 'srgb' when the descriptor omits it", () => {
92
121
  const { state } = makeState();
93
- const target = createGlRenderTarget(state, { width: 32, height: 32 });
122
+ const target = createGlTextureRenderTarget(state, { width: 32, height: 32 });
94
123
  expect(target.colorSpace).toBe('srgb');
95
124
  });
96
125
 
97
126
  it('honors an explicit colorSpace', () => {
98
127
  const { state } = makeState();
99
- const target = createGlRenderTarget(state, { width: 32, height: 32, colorSpace: 'linear' });
128
+ const target = createGlTextureRenderTarget(state, { width: 32, height: 32, colorSpace: 'linear' });
100
129
  expect(target.colorSpace).toBe('linear');
101
130
  });
102
131
 
103
132
  it('retains canonical requested axes separately from effective storage', () => {
104
133
  const { state, gl } = makeState();
105
134
  vi.mocked(gl.getParameter).mockImplementation((parameter) => (parameter === gl.MAX_SAMPLES ? 4 : null));
106
- const target = createGlRenderTarget(state, {
135
+ const target = createGlTextureRenderTarget(state, {
107
136
  width: 32,
108
137
  height: 16,
109
138
  colorAttachments: 2,
@@ -134,7 +163,7 @@ describe('createGlRenderTarget', () => {
134
163
  (gl as unknown as { getExtension: (n: string) => unknown }).getExtension = (name: string) =>
135
164
  name === 'EXT_color_buffer_float' ? null : {};
136
165
  for (const format of ['rgba16f', 'rgba32f'] as const) {
137
- const target = createGlRenderTarget(state, { width: 32, height: 32, format }, 'preferred');
166
+ const target = createGlTextureRenderTarget(state, { width: 32, height: 32, format }, 'preferred');
138
167
  expect(target.format).toBe('rgba8');
139
168
  expect(target.colorFormats).toEqual(['rgba8']);
140
169
  }
@@ -143,7 +172,7 @@ describe('createGlRenderTarget', () => {
143
172
  it('keeps a float format when EXT_color_buffer_float is available', () => {
144
173
  const { state, gl } = makeState();
145
174
  (gl as unknown as { getExtension: (n: string) => unknown }).getExtension = () => ({});
146
- const target = createGlRenderTarget(state, { width: 32, height: 32, format: 'rgba16f' });
175
+ const target = createGlTextureRenderTarget(state, { width: 32, height: 32, format: 'rgba16f' });
147
176
  expect(target.format).toBe('rgba16f');
148
177
  });
149
178
 
@@ -153,7 +182,7 @@ describe('createGlRenderTarget', () => {
153
182
  name === 'EXT_color_buffer_float' ? null : {};
154
183
  vi.clearAllMocks();
155
184
 
156
- const target = createGlRenderTarget(state, { width: 32, height: 32, format: 'rgba32f' }, 'required');
185
+ const target = createGlTextureRenderTarget(state, { width: 32, height: 32, format: 'rgba32f' }, 'required');
157
186
 
158
187
  expect(target).toBeNull();
159
188
  expect(gl.createFramebuffer).not.toHaveBeenCalled();
@@ -165,7 +194,7 @@ describe('createGlRenderTarget', () => {
165
194
  (gl as unknown as { getExtension: (n: string) => unknown }).getExtension = (name: string) =>
166
195
  name === 'EXT_color_buffer_float' ? null : {};
167
196
 
168
- const target = createGlRenderTarget(
197
+ const target = createGlTextureRenderTarget(
169
198
  state,
170
199
  {
171
200
  width: 32,
@@ -183,13 +212,13 @@ describe('createGlRenderTarget', () => {
183
212
  describe('declareGlRenderTargetColorSpace', () => {
184
213
  it('stamps the currently bound target and returns true', () => {
185
214
  const { state } = makeState();
186
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
215
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
187
216
  expect(target.colorSpace).toBe('srgb');
188
- beginGlRenderPass(state, target);
217
+ const pass = beginGlRenderPass(state, target);
189
218
  expect(declareGlRenderTargetColorSpace(state, 'linear')).toBe(true);
190
219
  expect(target.colorSpace).toBe('linear');
191
220
  expect(target.requestedAxes.colorSpace).toBe('linear');
192
- endGlRenderPass(state);
221
+ endGlRenderPass(pass);
193
222
  });
194
223
 
195
224
  it('returns false when no target is bound (rendering to the canvas)', () => {
@@ -199,62 +228,68 @@ describe('declareGlRenderTargetColorSpace', () => {
199
228
 
200
229
  it('is restored to the outer target when a nested target ends', () => {
201
230
  const { state } = makeState();
202
- const outer = createGlRenderTarget(state, { width: 64, height: 48 });
203
- const inner = createGlRenderTarget(state, { width: 32, height: 32 });
204
- beginGlRenderPass(state, outer);
205
- beginGlRenderPass(state, inner);
231
+ const outer = createGlTextureRenderTarget(state, { width: 64, height: 48 });
232
+ const inner = createGlTextureRenderTarget(state, { width: 32, height: 32 });
233
+ const outerPass = beginGlRenderPass(state, outer);
234
+ const innerPass = beginGlRenderPass(state, inner);
206
235
  declareGlRenderTargetColorSpace(state, 'linear');
207
236
  expect(inner.colorSpace).toBe('linear');
208
- endGlRenderPass(state);
237
+ endGlRenderPass(innerPass);
209
238
  // Back on `outer`: a declare now stamps it, not the popped inner target.
210
239
  declareGlRenderTargetColorSpace(state, 'linear');
211
240
  expect(outer.colorSpace).toBe('linear');
212
- endGlRenderPass(state);
241
+ endGlRenderPass(outerPass);
213
242
  });
214
243
  });
215
244
 
216
- describe('destroyGlRenderTarget', () => {
245
+ describe('destroyGlTextureRenderTarget', () => {
217
246
  it('deletes the framebuffer and texture', () => {
218
247
  const { state, gl } = makeState();
219
- const target = createGlRenderTarget(state, { width: 32, height: 32 });
248
+ const target = createGlTextureRenderTarget(state, { width: 32, height: 32 });
220
249
  const { framebuffer, texture } = target;
221
250
 
222
- destroyGlRenderTarget(state, target);
251
+ destroyGlTextureRenderTarget(target);
223
252
 
224
253
  expect(vi.mocked(gl.deleteFramebuffer)).toHaveBeenCalledWith(framebuffer);
225
254
  expect(vi.mocked(gl.deleteTexture)).toHaveBeenCalledWith(texture);
226
255
  });
227
256
  });
228
257
 
229
- describe('drawGlRenderTargetResult', () => {
258
+ describe('drawGlTextureRenderTargetResult', () => {
230
259
  it('is a no-op when target dimensions are zero', () => {
231
260
  const { state, gl } = makeState();
261
+ const screenTarget = createGlScreenRenderTarget(gl);
262
+ const pass = beginGlRenderPass(state, screenTarget);
232
263
  const node = getOrCreateRenderProxy2D(state, createDisplayObject());
233
- const target = createGlRenderTarget(state, { width: 1, height: 1 });
264
+ const target = createGlTextureRenderTarget(state, { width: 1, height: 1 });
234
265
  target.width = 0;
235
266
  vi.clearAllMocks();
236
267
 
237
- drawGlRenderTargetResult(state, node, target, createMatrix());
268
+ drawGlTextureRenderTargetResult(pass, node, target, createMatrix());
238
269
 
239
270
  expect(vi.mocked(gl.bindTexture)).not.toHaveBeenCalled();
240
271
  });
241
272
 
242
273
  it('composites a valid target without throwing', () => {
243
- const { state } = makeState();
274
+ const { state, gl } = makeState();
275
+ const screenTarget = createGlScreenRenderTarget(gl);
276
+ const pass = beginGlRenderPass(state, screenTarget);
244
277
  const node = getOrCreateRenderProxy2D(state, createDisplayObject());
245
278
  node.alpha = 1;
246
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
279
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
247
280
 
248
- expect(() => drawGlRenderTargetResult(state, node, target, createMatrix())).not.toThrow();
281
+ expect(() => drawGlTextureRenderTargetResult(pass, node, target, createMatrix())).not.toThrow();
249
282
  });
250
283
 
251
284
  it('binds the target texture', () => {
252
285
  const { state, gl } = makeState();
286
+ const screenTarget = createGlScreenRenderTarget(gl);
287
+ const pass = beginGlRenderPass(state, screenTarget);
253
288
  const node = getOrCreateRenderProxy2D(state, createDisplayObject());
254
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
289
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
255
290
  vi.clearAllMocks();
256
291
 
257
- drawGlRenderTargetResult(state, node, target, createMatrix());
292
+ drawGlTextureRenderTargetResult(pass, node, target, createMatrix());
258
293
 
259
294
  expect(vi.mocked(gl.bindTexture)).toHaveBeenCalledWith(
260
295
  (gl as unknown as { TEXTURE_2D: number }).TEXTURE_2D,
@@ -263,13 +298,13 @@ describe('drawGlRenderTargetResult', () => {
263
298
  });
264
299
  });
265
300
 
266
- describe('explainGlRenderTarget', () => {
301
+ describe('explainGlTextureRenderTarget', () => {
267
302
  it('reports every GL capability substitution without losing the request', () => {
268
303
  const { state, gl } = makeState();
269
304
  vi.mocked(gl.getParameter).mockImplementation((parameter) => (parameter === gl.MAX_SAMPLES ? 4 : null));
270
305
  (gl as unknown as { getExtension: (name: string) => unknown }).getExtension = (name: string) =>
271
306
  name === 'EXT_color_buffer_float' ? null : {};
272
- const target = createGlRenderTarget(state, {
307
+ const target = createGlTextureRenderTarget(state, {
273
308
  width: 32,
274
309
  height: 16,
275
310
  format: 'rgba16f',
@@ -277,7 +312,7 @@ describe('explainGlRenderTarget', () => {
277
312
  depth: 'depth-stencil-sampled',
278
313
  });
279
314
 
280
- expect(explainGlRenderTarget(target).differences).toEqual([
315
+ expect(explainGlTextureRenderTarget(target).differences).toEqual([
281
316
  { axis: 'format', effective: 'rgba8', requested: 'rgba16f' },
282
317
  { axis: 'colorFormats', effective: ['rgba8'], requested: ['rgba16f'] },
283
318
  { axis: 'sampleCount', effective: 4, requested: 8 },
@@ -287,14 +322,14 @@ describe('explainGlRenderTarget', () => {
287
322
 
288
323
  it('reports no differences when GL realizes every requested axis', () => {
289
324
  const { state } = makeState();
290
- const target = createGlRenderTarget(state, { width: 32, height: 16 });
291
- expect(explainGlRenderTarget(target).differences).toEqual([]);
325
+ const target = createGlTextureRenderTarget(state, { width: 32, height: 16 });
326
+ expect(explainGlTextureRenderTarget(target).differences).toEqual([]);
292
327
  });
293
328
  });
294
329
 
295
- describe('initializeGlRenderTarget', () => {
296
- it('is the construction initializer of createGlRenderTarget', () => {
297
- expect(typeof initializeGlRenderTarget).toBe('function');
330
+ describe('initializeGlTextureRenderTarget', () => {
331
+ it('is the construction initializer of createGlTextureRenderTarget', () => {
332
+ expect(typeof initializeGlTextureRenderTarget).toBe('function');
298
333
  });
299
334
  });
300
335
 
@@ -320,12 +355,12 @@ describe('isGlRenderTargetFormatSupported', () => {
320
355
  });
321
356
  });
322
357
 
323
- describe('resizeGlRenderTarget', () => {
358
+ describe('resizeGlTextureRenderTarget', () => {
324
359
  it('updates the target dimensions', () => {
325
360
  const { state } = makeState();
326
- const target = createGlRenderTarget(state, { width: 64, height: 64 });
361
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 64 });
327
362
 
328
- resizeGlRenderTarget(state, target, 256, 128);
363
+ resizeGlTextureRenderTarget(state, target, 256, 128);
329
364
 
330
365
  expect(target.width).toBe(256);
331
366
  expect(target.height).toBe(128);
@@ -333,9 +368,9 @@ describe('resizeGlRenderTarget', () => {
333
368
 
334
369
  it('enforces a minimum size of 1', () => {
335
370
  const { state } = makeState();
336
- const target = createGlRenderTarget(state, { width: 64, height: 64 });
371
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 64 });
337
372
 
338
- resizeGlRenderTarget(state, target, 0, 0);
373
+ resizeGlTextureRenderTarget(state, target, 0, 0);
339
374
 
340
375
  expect(target.width).toBe(1);
341
376
  expect(target.height).toBe(1);
@@ -343,10 +378,10 @@ describe('resizeGlRenderTarget', () => {
343
378
 
344
379
  it('reallocates the texture storage', () => {
345
380
  const { state, gl } = makeState();
346
- const target = createGlRenderTarget(state, { width: 64, height: 64 });
381
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 64 });
347
382
  vi.clearAllMocks();
348
383
 
349
- resizeGlRenderTarget(state, target, 128, 128);
384
+ resizeGlTextureRenderTarget(state, target, 128, 128);
350
385
 
351
386
  expect(vi.mocked(gl.bindTexture)).toHaveBeenCalledWith(
352
387
  (gl as unknown as { TEXTURE_2D: number }).TEXTURE_2D,
@@ -357,12 +392,12 @@ describe('resizeGlRenderTarget', () => {
357
392
 
358
393
  it('restores the tracked framebuffer after reallocating storage', () => {
359
394
  const { state, gl } = makeState();
360
- const target = createGlRenderTarget(state, { width: 64, height: 64 });
395
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 64 });
361
396
  const previous = { name: 'previous-framebuffer' } as unknown as WebGLFramebuffer;
362
397
  getGlRenderStateRuntime(state).currentFramebuffer = previous;
363
398
  vi.clearAllMocks();
364
399
 
365
- resizeGlRenderTarget(state, target, 128, 96);
400
+ resizeGlTextureRenderTarget(state, target, 128, 96);
366
401
 
367
402
  expect(vi.mocked(gl.bindFramebuffer)).toHaveBeenLastCalledWith(gl.FRAMEBUFFER, previous);
368
403
  expect(getGlRenderStateRuntime(state).currentFramebuffer).toBe(previous);
@@ -371,7 +406,7 @@ describe('resizeGlRenderTarget', () => {
371
406
  it('preserves heterogeneous color formats and sampled depth across resize', () => {
372
407
  const { state, gl } = makeState();
373
408
  (gl as unknown as { getExtension: (name: string) => unknown }).getExtension = () => ({});
374
- const target = createGlRenderTarget(state, {
409
+ const target = createGlTextureRenderTarget(state, {
375
410
  width: 64,
376
411
  height: 64,
377
412
  colorAttachments: 2,
@@ -380,7 +415,7 @@ describe('resizeGlRenderTarget', () => {
380
415
  });
381
416
  vi.clearAllMocks();
382
417
 
383
- resizeGlRenderTarget(state, target, 128, 96);
418
+ resizeGlTextureRenderTarget(state, target, 128, 96);
384
419
 
385
420
  expect(target.colorAttachments).toBe(2);
386
421
  expect(target.colorFormats).toEqual(['rgba8', 'rgba32f']);
@@ -394,34 +429,61 @@ describe('resizeGlRenderTarget', () => {
394
429
  });
395
430
  });
396
431
 
397
- describe('resolveGlRenderTarget', () => {
432
+ describe('resolveGlRenderTargetAxes', () => {
433
+ it('queries effective axes without allocating target storage', () => {
434
+ const { state, gl } = makeState();
435
+ vi.mocked(gl.getParameter).mockImplementation((parameter) => (parameter === gl.MAX_SAMPLES ? 2 : null));
436
+ vi.clearAllMocks();
437
+
438
+ const axes = resolveGlRenderTargetAxes(state, { width: 32, height: 16, sampleCount: 4 });
439
+
440
+ expect(axes.sampleCount).toBe(2);
441
+ expect(gl.createFramebuffer).not.toHaveBeenCalled();
442
+ expect(gl.createTexture).not.toHaveBeenCalled();
443
+ });
444
+
445
+ it('applies preferred fallback and required refusal without allocating storage', () => {
446
+ const { state, gl } = makeState();
447
+ (gl as unknown as { getExtension: (name: string) => unknown }).getExtension = () => null;
448
+ vi.clearAllMocks();
449
+
450
+ expect(resolveGlRenderTargetAxes(state, { width: 32, height: 16, format: 'rgba32f' }, 'preferred')).toMatchObject({
451
+ format: 'rgba8',
452
+ colorFormats: ['rgba8'],
453
+ });
454
+ expect(resolveGlRenderTargetAxes(state, { width: 32, height: 16, format: 'rgba32f' }, 'required')).toBeNull();
455
+ expect(gl.createFramebuffer).not.toHaveBeenCalled();
456
+ expect(gl.createTexture).not.toHaveBeenCalled();
457
+ });
458
+ });
459
+ describe('resolveGlTextureRenderTarget', () => {
398
460
  it('is a no-op for a single-sample target', () => {
399
461
  const { state, gl } = makeState();
400
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
462
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
401
463
  expect(target.sampleCount).toBe(1);
402
464
  vi.clearAllMocks();
403
465
 
404
- resolveGlRenderTarget(state, target);
466
+ resolveGlTextureRenderTarget(state, target);
405
467
 
406
468
  expect(vi.mocked(gl.bindFramebuffer)).not.toHaveBeenCalled();
407
469
  });
408
470
 
409
471
  it('is a no-op when the target has no resolve framebuffer', () => {
410
472
  const { state, gl } = makeState();
411
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
473
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
412
474
  // Force the MSAA sample-count gate open but leave resolveFramebuffer null.
413
475
  target.sampleCount = 4;
414
476
  target.resolveFramebuffer = null;
415
477
  vi.clearAllMocks();
416
478
 
417
- resolveGlRenderTarget(state, target);
479
+ resolveGlTextureRenderTarget(state, target);
418
480
 
419
481
  expect(vi.mocked(gl.bindFramebuffer)).not.toHaveBeenCalled();
420
482
  });
421
483
 
422
484
  it('blits each color attachment and flushes for an MSAA target', () => {
423
485
  const { state, gl } = makeState();
424
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
486
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
425
487
  target.sampleCount = 4;
426
488
  target.resolveFramebuffer = {} as WebGLFramebuffer;
427
489
  // Two color attachments resolve as two separate blits.
@@ -443,7 +505,7 @@ describe('resolveGlRenderTarget', () => {
443
505
  flush,
444
506
  });
445
507
 
446
- resolveGlRenderTarget(state, target);
508
+ resolveGlTextureRenderTarget(state, target);
447
509
 
448
510
  expect(blit).toHaveBeenCalledTimes(2);
449
511
  expect(readBuffer).toHaveBeenCalledTimes(2);
@@ -452,7 +514,7 @@ describe('resolveGlRenderTarget', () => {
452
514
 
453
515
  it('disables an active scissor for the storage-wide resolve and restores it afterward', () => {
454
516
  const { state, gl } = makeState();
455
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
517
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
456
518
  target.sampleCount = 4;
457
519
  target.resolveFramebuffer = {} as WebGLFramebuffer;
458
520
  const runtime = getGlRenderStateRuntime(state);
@@ -470,7 +532,7 @@ describe('resolveGlRenderTarget', () => {
470
532
  flush: vi.fn(),
471
533
  });
472
534
 
473
- resolveGlRenderTarget(state, target);
535
+ resolveGlTextureRenderTarget(state, target);
474
536
 
475
537
  expect(gl.disable).toHaveBeenCalledWith(gl.SCISSOR_TEST);
476
538
  expect(vi.mocked(gl.disable).mock.invocationCallOrder.at(-1)).toBeLessThan(blit.mock.invocationCallOrder[0]);
@@ -480,7 +542,7 @@ describe('resolveGlRenderTarget', () => {
480
542
 
481
543
  it('restores framebuffer and scissor state when an MSAA blit throws', () => {
482
544
  const { state, gl } = makeState();
483
- const target = createGlRenderTarget(state, { width: 64, height: 48 });
545
+ const target = createGlTextureRenderTarget(state, { width: 64, height: 48 });
484
546
  target.sampleCount = 4;
485
547
  target.resolveFramebuffer = { id: 'resolve' } as unknown as WebGLFramebuffer;
486
548
  const runtime = getGlRenderStateRuntime(state);
@@ -501,37 +563,10 @@ describe('resolveGlRenderTarget', () => {
501
563
  flush: vi.fn(),
502
564
  });
503
565
 
504
- expect(() => resolveGlRenderTarget(state, target)).toThrow('blit failed');
566
+ expect(() => resolveGlTextureRenderTarget(state, target)).toThrow('blit failed');
505
567
  expect(gl.bindFramebuffer).toHaveBeenCalledWith(gl.READ_FRAMEBUFFER, enclosingFramebuffer);
506
568
  expect(gl.bindFramebuffer).toHaveBeenLastCalledWith(gl.DRAW_FRAMEBUFFER, enclosingFramebuffer);
507
569
  expect(gl.enable).toHaveBeenLastCalledWith(gl.SCISSOR_TEST);
508
570
  expect(gl.scissor).toHaveBeenLastCalledWith(3, 4, 16, 12);
509
571
  });
510
572
  });
511
- describe('resolveGlRenderTargetAxes', () => {
512
- it('queries effective axes without allocating target storage', () => {
513
- const { state, gl } = makeState();
514
- vi.mocked(gl.getParameter).mockImplementation((parameter) => (parameter === gl.MAX_SAMPLES ? 2 : null));
515
- vi.clearAllMocks();
516
-
517
- const axes = resolveGlRenderTargetAxes(state, { width: 32, height: 16, sampleCount: 4 });
518
-
519
- expect(axes.sampleCount).toBe(2);
520
- expect(gl.createFramebuffer).not.toHaveBeenCalled();
521
- expect(gl.createTexture).not.toHaveBeenCalled();
522
- });
523
-
524
- it('applies preferred fallback and required refusal without allocating storage', () => {
525
- const { state, gl } = makeState();
526
- (gl as unknown as { getExtension: (name: string) => unknown }).getExtension = () => null;
527
- vi.clearAllMocks();
528
-
529
- expect(resolveGlRenderTargetAxes(state, { width: 32, height: 16, format: 'rgba32f' }, 'preferred')).toMatchObject({
530
- format: 'rgba8',
531
- colorFormats: ['rgba8'],
532
- });
533
- expect(resolveGlRenderTargetAxes(state, { width: 32, height: 16, format: 'rgba32f' }, 'required')).toBeNull();
534
- expect(gl.createFramebuffer).not.toHaveBeenCalled();
535
- expect(gl.createTexture).not.toHaveBeenCalled();
536
- });
537
- });