@gjsify/webgl 0.3.12 → 0.3.14

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 (55) hide show
  1. package/lib/esm/conformance/attribs.spec.js +312 -293
  2. package/lib/esm/conformance/buffers.spec.js +217 -200
  3. package/lib/esm/conformance/context.spec.js +295 -295
  4. package/lib/esm/conformance/programs.spec.js +458 -445
  5. package/lib/esm/conformance/rendering-basic.spec.js +134 -128
  6. package/lib/esm/conformance/rendering.spec.js +502 -400
  7. package/lib/esm/conformance/setup.js +42 -31
  8. package/lib/esm/conformance/state.spec.js +360 -343
  9. package/lib/esm/conformance/textures.spec.js +330 -338
  10. package/lib/esm/conformance/uniforms.spec.js +465 -309
  11. package/lib/esm/conformance-test.js +24 -22
  12. package/lib/esm/extensions/ext-blend-minmax.js +16 -16
  13. package/lib/esm/extensions/ext-color-buffer-float.js +10 -11
  14. package/lib/esm/extensions/ext-color-buffer-half-float.js +10 -11
  15. package/lib/esm/extensions/ext-texture-filter-anisotropic.js +16 -16
  16. package/lib/esm/extensions/oes-element-index-unit.js +11 -12
  17. package/lib/esm/extensions/oes-standard-derivatives.js +15 -15
  18. package/lib/esm/extensions/oes-texture-float-linear.js +11 -12
  19. package/lib/esm/extensions/oes-texture-float.js +11 -12
  20. package/lib/esm/extensions/oes-texture-half-float.js +17 -17
  21. package/lib/esm/extensions/stackgl-destroy-context.js +10 -10
  22. package/lib/esm/extensions/stackgl-resize-drawing-buffer.js +10 -10
  23. package/lib/esm/html-canvas-element.js +64 -64
  24. package/lib/esm/index.js +29 -26
  25. package/lib/esm/linkable.js +49 -49
  26. package/lib/esm/test-utils.js +158 -107
  27. package/lib/esm/test.js +8 -4
  28. package/lib/esm/types/index.js +5 -5
  29. package/lib/esm/utils.js +164 -187
  30. package/lib/esm/webgl-active-info.js +10 -9
  31. package/lib/esm/webgl-bridge.js +162 -147
  32. package/lib/esm/webgl-buffer.js +17 -15
  33. package/lib/esm/webgl-context-attributes.js +23 -22
  34. package/lib/esm/webgl-context-base.js +3039 -3351
  35. package/lib/esm/webgl-drawing-buffer-wrapper.js +10 -9
  36. package/lib/esm/webgl-framebuffer.js +108 -106
  37. package/lib/esm/webgl-program.js +25 -23
  38. package/lib/esm/webgl-query.js +15 -13
  39. package/lib/esm/webgl-renderbuffer.js +23 -21
  40. package/lib/esm/webgl-rendering-context.js +173 -187
  41. package/lib/esm/webgl-sampler.js +15 -13
  42. package/lib/esm/webgl-shader-precision-format.js +10 -9
  43. package/lib/esm/webgl-shader.js +23 -21
  44. package/lib/esm/webgl-sync.js +15 -13
  45. package/lib/esm/webgl-texture-unit.js +12 -11
  46. package/lib/esm/webgl-texture.js +21 -19
  47. package/lib/esm/webgl-transform-feedback.js +15 -13
  48. package/lib/esm/webgl-uniform-location.js +14 -13
  49. package/lib/esm/webgl-vertex-array-object.js +20 -18
  50. package/lib/esm/webgl-vertex-attribute.js +149 -145
  51. package/lib/esm/webgl1.spec.js +1039 -650
  52. package/lib/esm/webgl2-rendering-context.js +1210 -1273
  53. package/lib/esm/webgl2.spec.js +1284 -1252
  54. package/package.json +9 -9
  55. package/lib/esm/@types/glsl-tokenizer/index.d.js +0 -0
@@ -1,1281 +1,1218 @@
1
- import Gwebgl from "@girs/gwebgl-0.1";
2
- import GdkPixbuf from "gi://GdkPixbuf?version=2.0";
1
+ import { Uint8ArrayToVariant, arrayToUint8Array, checkObject, convertPixels, extractImageData, premultiplyAlpha, vertexCount } from "./utils.js";
2
+ import { WebGLActiveInfo } from "./webgl-active-info.js";
3
+ import { WebGLFramebuffer } from "./webgl-framebuffer.js";
4
+ import { WebGLRenderbuffer } from "./webgl-renderbuffer.js";
5
+ import { WebGLTexture } from "./webgl-texture.js";
3
6
  import { WebGLContextBase } from "./webgl-context-base.js";
4
7
  import { WebGLQuery } from "./webgl-query.js";
5
8
  import { WebGLSampler } from "./webgl-sampler.js";
6
9
  import { WebGLSync } from "./webgl-sync.js";
7
10
  import { WebGLTransformFeedback } from "./webgl-transform-feedback.js";
8
11
  import { WebGLVertexArrayObject } from "./webgl-vertex-array-object.js";
9
- import { WebGLActiveInfo } from "./webgl-active-info.js";
10
- import { WebGLTexture } from "./webgl-texture.js";
11
- import { WebGLRenderbuffer } from "./webgl-renderbuffer.js";
12
- import { WebGLFramebuffer } from "./webgl-framebuffer.js";
13
- import { Uint8ArrayToVariant, arrayToUint8Array, vertexCount, convertPixels, extractImageData, checkObject, premultiplyAlpha } from "./utils.js";
12
+ import Gwebgl from "@girs/gwebgl-0.1";
13
+ import GdkPixbuf from "gi://GdkPixbuf?version=2.0";
14
14
  import { warnNotImplemented } from "@gjsify/utils";
15
- class WebGL2RenderingContext extends WebGLContextBase {
16
- constructor(canvas, options = {}) {
17
- super(canvas, options);
18
- this._queries = {};
19
- this._samplers = {};
20
- this._transformFeedbacks = {};
21
- this._vertexArrayObjects = {};
22
- this._syncs = {};
23
- this._activeReadFramebuffer = null;
24
- this._activeDrawFramebuffer = null;
25
- this._native2 = new Gwebgl.WebGL2RenderingContext({});
26
- this._init();
27
- }
28
- get _gl() {
29
- return this._native2;
30
- }
31
- _getGlslVersion(es) {
32
- return es ? "300 es" : "130";
33
- }
34
- // ─── WebGL2 overrides for WebGL1 validation that's too strict ─────────
35
- /**
36
- * WebGL2 delegates framebuffer completeness to the native GL driver.
37
- * Called by _framebufferOk() before draw calls and by _updateFramebufferAttachments.
38
- * The base class version uses JS-side format whitelists that reject valid WebGL2 formats.
39
- */
40
- /** WebGL2 allows COLOR_ATTACHMENT1–15 as framebuffer attachment points. */
41
- _validFramebufferAttachment(attachment) {
42
- if (super._validFramebufferAttachment(attachment)) return true;
43
- return attachment >= 36065 && attachment <= 36079;
44
- }
45
- static {
46
- // ─── MRT: native COLOR_ATTACHMENT0–15 support ────────────────────────
47
- this._WGL2_ALL_COLOR_ATTACHMENTS = [
48
- 36064,
49
- 36065,
50
- 36066,
51
- 36067,
52
- 36068,
53
- 36069,
54
- 36070,
55
- 36071,
56
- 36072,
57
- 36073,
58
- 36074,
59
- 36075,
60
- 36076,
61
- 36077,
62
- 36078,
63
- 36079
64
- ];
65
- }
66
- _getColorAttachments() {
67
- return WebGL2RenderingContext._WGL2_ALL_COLOR_ATTACHMENTS;
68
- }
69
- /**
70
- * WebGL2 extends the base-class framebuffer completeness pre-check to
71
- * accept WebGL2-specific formats that the WebGL1 whitelist rejects.
72
- *
73
- * NOTE: This is called by _updateFramebufferAttachments BEFORE the native
74
- * GL attachments are set, so we must NOT query glCheckFramebufferStatus
75
- * here (it would see an empty FBO and always return INCOMPLETE).
76
- * Instead we extend the JS-side format whitelist to cover WebGL2 formats.
77
- */
78
- _preCheckFramebufferStatus(framebuffer) {
79
- const attachments = framebuffer._attachments;
80
- let bestWidth = 0;
81
- let bestHeight = 0;
82
- const allEnums = [
83
- this.COLOR_ATTACHMENT0,
84
- this.DEPTH_ATTACHMENT,
85
- this.STENCIL_ATTACHMENT,
86
- this.DEPTH_STENCIL_ATTACHMENT,
87
- ...WebGL2RenderingContext._WGL2_ALL_COLOR_ATTACHMENTS
88
- ];
89
- for (const enumVal of allEnums) {
90
- const attach = attachments[enumVal];
91
- if (!attach) continue;
92
- if (attach instanceof WebGLTexture) {
93
- const level = framebuffer._attachmentLevel[enumVal] ?? 0;
94
- const w = attach._levelWidth[level] ?? 0;
95
- const h = attach._levelHeight[level] ?? 0;
96
- if (w > 0 && h > 0) {
97
- bestWidth = w;
98
- bestHeight = h;
99
- break;
100
- }
101
- } else if (attach instanceof WebGLRenderbuffer) {
102
- if (attach._width > 0 && attach._height > 0) {
103
- bestWidth = attach._width;
104
- bestHeight = attach._height;
105
- break;
106
- }
107
- }
108
- }
109
- if (bestWidth > 0 && bestHeight > 0) {
110
- framebuffer._width = bestWidth;
111
- framebuffer._height = bestHeight;
112
- return this.FRAMEBUFFER_COMPLETE;
113
- }
114
- return this.FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
115
- }
116
- /**
117
- * WebGL2 completely replaces the base class framebuffer attachment flow.
118
- *
119
- * The base class flow is: (1) pre-check formats in JS → (2) set native
120
- * attachments only if pre-check passes. This is wrong for WebGL2 because
121
- * the JS pre-check uses WebGL1 format whitelists that reject valid WebGL2
122
- * formats.
123
- *
124
- * WebGL2 flow: (1) always set native attachments first → (2) query native
125
- * glCheckFramebufferStatus to determine completeness. This delegates all
126
- * format validation to the native GL driver, matching browser behavior.
127
- *
128
- * Also handles COLOR_ATTACHMENT1–15 (WebGL2 MRT) that the base class
129
- * doesn't know about.
130
- */
131
- /**
132
- * Apply COLOR_ATTACHMENT1–15 to the native GL FBO (WebGL2 MRT).
133
- * The base class handles CA0, DEPTH, STENCIL, DEPTH_STENCIL and calls
134
- * _preCheckFramebufferStatus (which we override to query native GL).
135
- */
136
- _updateFramebufferAttachments(framebuffer) {
137
- super._updateFramebufferAttachments(framebuffer);
138
- if (!framebuffer) return;
139
- for (let i = 1; i <= 15; i++) {
140
- const attachmentEnum = 36064 + i;
141
- if (!(attachmentEnum in framebuffer._attachments)) continue;
142
- const attachment = framebuffer._attachments[attachmentEnum];
143
- if (attachment instanceof WebGLTexture) {
144
- const face = framebuffer._attachmentFace[attachmentEnum] || this.TEXTURE_2D;
145
- const level = framebuffer._attachmentLevel[attachmentEnum] ?? 0;
146
- this._gl.framebufferTexture2D(this.FRAMEBUFFER, attachmentEnum, face, attachment._ | 0, level | 0);
147
- } else if (attachment instanceof WebGLRenderbuffer) {
148
- this._gl.framebufferRenderbuffer(this.FRAMEBUFFER, attachmentEnum, this.RENDERBUFFER, attachment._ | 0);
149
- } else {
150
- this._gl.framebufferTexture2D(this.FRAMEBUFFER, attachmentEnum, this.TEXTURE_2D, 0, 0);
151
- }
152
- }
153
- }
154
- /** WebGL2 adds UNIFORM_BUFFER, TRANSFORM_FEEDBACK_BUFFER, etc. targets. */
155
- bindBuffer(target, buffer) {
156
- const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
157
- if (isWebGL2Target) {
158
- const id = buffer ? buffer._ | 0 : 0;
159
- this._gl.bindBuffer(target, id);
160
- return;
161
- }
162
- super.bindBuffer(target, buffer);
163
- }
164
- /**
165
- * WebGL2 adds READ_FRAMEBUFFER (0x8CA8) and DRAW_FRAMEBUFFER (0x8CA9) targets.
166
- * The base class only accepts FRAMEBUFFER; this override handles the two new targets
167
- * and keeps _activeReadFramebuffer / _activeDrawFramebuffer in sync.
168
- */
169
- bindFramebuffer(target, framebuffer) {
170
- if (target === 36008 || target === 36009) {
171
- if (!checkObject(framebuffer)) {
172
- throw new TypeError("bindFramebuffer(GLenum, WebGLFramebuffer)");
173
- }
174
- if (framebuffer && framebuffer._pendingDelete) return;
175
- if (framebuffer && !this._checkWrapper(framebuffer, WebGLFramebuffer)) return;
176
- const id = framebuffer ? framebuffer._ | 0 : this._gtkFboId;
177
- this._gl.bindFramebuffer(target, id);
178
- if (target === 36008) {
179
- const prev = this._activeReadFramebuffer;
180
- if (prev !== framebuffer) {
181
- if (prev) {
182
- prev._refCount -= 1;
183
- prev._checkDelete();
184
- }
185
- if (framebuffer) framebuffer._refCount += 1;
186
- }
187
- this._activeReadFramebuffer = framebuffer;
188
- } else {
189
- const prev = this._activeDrawFramebuffer;
190
- if (prev !== framebuffer) {
191
- if (prev) {
192
- prev._refCount -= 1;
193
- prev._checkDelete();
194
- }
195
- if (framebuffer) framebuffer._refCount += 1;
196
- }
197
- this._activeDrawFramebuffer = framebuffer;
198
- this._activeFramebuffer = framebuffer;
199
- }
200
- return;
201
- }
202
- super.bindFramebuffer(this.FRAMEBUFFER, framebuffer);
203
- this._activeReadFramebuffer = framebuffer;
204
- this._activeDrawFramebuffer = framebuffer;
205
- }
206
- /** WebGL2 also unbinds from read/draw framebuffer slots when deleting. */
207
- deleteFramebuffer(framebuffer) {
208
- if (this._activeReadFramebuffer === framebuffer) {
209
- this.bindFramebuffer(36008, null);
210
- }
211
- if (this._activeDrawFramebuffer === framebuffer) {
212
- this.bindFramebuffer(36009, null);
213
- }
214
- super.deleteFramebuffer(framebuffer);
215
- }
216
- /** WebGL2 adds READ/COPY buffer usages and additional buffer targets. */
217
- bufferData(target, dataOrSize, usage) {
218
- const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
219
- const isReadOrCopy = usage === 35041 || usage === 35043 || usage === 35045 || usage === 35042 || usage === 35044 || usage === 35046;
220
- const remappedUsage = isReadOrCopy ? this.STATIC_DRAW : usage;
221
- if (isWebGL2Target) {
222
- if (typeof dataOrSize === "number") {
223
- if (dataOrSize >= 0) this._gl.bufferDataSizeOnly(target, dataOrSize, remappedUsage);
224
- } else if (dataOrSize !== null && typeof dataOrSize === "object") {
225
- const u8Data = arrayToUint8Array(dataOrSize);
226
- this._gl.bufferData(target, Uint8ArrayToVariant(u8Data), remappedUsage);
227
- }
228
- return;
229
- }
230
- super.bufferData(target, dataOrSize, remappedUsage);
231
- }
232
- /** WebGL2 adds UNIFORM_BUFFER, TRANSFORM_FEEDBACK_BUFFER, COPY_READ/WRITE targets. */
233
- bufferSubData(target, offset, data) {
234
- const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
235
- if (isWebGL2Target) {
236
- if (offset < 0) {
237
- this.setError(this.INVALID_VALUE);
238
- return;
239
- }
240
- if (!data) {
241
- this.setError(this.INVALID_VALUE);
242
- return;
243
- }
244
- const u8Data = arrayToUint8Array(data);
245
- this._gl.bufferSubData(target, offset, Uint8ArrayToVariant(u8Data));
246
- return;
247
- }
248
- super.bufferSubData(target, offset, data);
249
- }
250
- /** WebGL2 adds TEXTURE_3D and TEXTURE_2D_ARRAY target support. */
251
- bindTexture(target, texture) {
252
- if (target === 32879 || target === 35866) {
253
- const id = texture ? texture._ | 0 : 0;
254
- this._gl.bindTexture(target, id);
255
- if (texture) texture._binding = target;
256
- return;
257
- }
258
- super.bindTexture(target, texture);
259
- }
260
- /** WebGL2 adds TEXTURE_3D/TEXTURE_2D_ARRAY targets and many new pnames. */
261
- texParameteri(target, pname, param) {
262
- if (target === 32879 || target === 35866) {
263
- this._gl.texParameteri(target, pname, param);
264
- return;
265
- }
266
- const isWebGL2Pname = pname === 32882 || pname === 34892 || pname === 34893 || pname === 33084 || pname === 33085 || pname === 33083 || pname === 33082;
267
- if (isWebGL2Pname) {
268
- this._gl.texParameteri(target, pname, param);
269
- return;
270
- }
271
- super.texParameteri(target, pname, param);
272
- }
273
- /**
274
- * In WebGL2/GLES3 the attribute-0 requirement from WebGL1 does not apply.
275
- * Override drawArrays to skip the attrib0 hack and call glDrawArrays directly.
276
- */
277
- /**
278
- * In WebGL2/GLES3 the attribute-0 requirement from WebGL1 does not apply.
279
- * Override drawArrays to skip the attrib0 hack and call glDrawArrays directly.
280
- */
281
- drawArrays(mode, first, count) {
282
- if (first < 0 || count < 0) {
283
- this.setError(this.INVALID_VALUE);
284
- return;
285
- }
286
- if (!this._checkStencilState()) return;
287
- const rc = vertexCount(this, mode, count);
288
- if (rc < 0) {
289
- this.setError(this.INVALID_ENUM);
290
- return;
291
- }
292
- if (!this._framebufferOk()) return;
293
- if (count === 0) return;
294
- if (!this._checkVertexAttribState(count + first - 1 >>> 0)) return;
295
- this._native2.drawArrays(mode, first, rc);
296
- }
297
- /**
298
- * In WebGL2, UNSIGNED_INT element indices are a core feature — no extension needed.
299
- * Override drawElements to skip the oes_element_index_uint extension check.
300
- */
301
- drawElements(mode = 0, count = 0, type = 0, offset = 0) {
302
- if (count < 0 || offset < 0) {
303
- this.setError(this.INVALID_VALUE);
304
- return;
305
- }
306
- if (!this._checkStencilState()) return;
307
- const elementBuffer = this._vertexObjectState._elementArrayBufferBinding;
308
- if (!elementBuffer) {
309
- this.setError(this.INVALID_OPERATION);
310
- return;
311
- }
312
- let elementData = null;
313
- let adjustedOffset = offset;
314
- if (type === this.UNSIGNED_SHORT) {
315
- if (adjustedOffset % 2) {
316
- this.setError(this.INVALID_OPERATION);
317
- return;
318
- }
319
- adjustedOffset >>= 1;
320
- elementData = new Uint16Array(elementBuffer._elements.buffer);
321
- } else if (type === this.UNSIGNED_INT) {
322
- if (adjustedOffset % 4) {
323
- this.setError(this.INVALID_OPERATION);
324
- return;
325
- }
326
- adjustedOffset >>= 2;
327
- elementData = new Uint32Array(elementBuffer._elements.buffer);
328
- } else if (type === this.UNSIGNED_BYTE) {
329
- elementData = elementBuffer._elements;
330
- } else {
331
- this.setError(this.INVALID_ENUM);
332
- return;
333
- }
334
- let reducedCount = count;
335
- switch (mode) {
336
- case this.TRIANGLES:
337
- if (count % 3) reducedCount -= count % 3;
338
- break;
339
- case this.LINES:
340
- if (count % 2) reducedCount -= count % 2;
341
- break;
342
- case this.POINTS:
343
- break;
344
- case this.LINE_LOOP:
345
- case this.LINE_STRIP:
346
- if (count < 2) {
347
- this.setError(this.INVALID_OPERATION);
348
- return;
349
- }
350
- break;
351
- case this.TRIANGLE_FAN:
352
- case this.TRIANGLE_STRIP:
353
- if (count < 3) {
354
- this.setError(this.INVALID_OPERATION);
355
- return;
356
- }
357
- break;
358
- default:
359
- this.setError(this.INVALID_ENUM);
360
- return;
361
- }
362
- if (!this._framebufferOk()) return;
363
- if (count === 0) return;
364
- let maxIndex = 0;
365
- for (let i = adjustedOffset; i < adjustedOffset + reducedCount; ++i) {
366
- if (i < elementData.length && elementData[i] > maxIndex) maxIndex = elementData[i];
367
- }
368
- if (this._checkVertexAttribState(maxIndex)) {
369
- this._native2.drawElements(mode, reducedCount, type, offset);
370
- }
371
- }
372
- // ─── Vertex Array Objects ─────────────────────────────────────────────
373
- createVertexArray() {
374
- const id = this._native2.createVertexArray();
375
- if (!id) return null;
376
- const vao = new WebGLVertexArrayObject(id, this);
377
- this._vertexArrayObjects[id] = vao;
378
- return vao;
379
- }
380
- deleteVertexArray(vertexArray) {
381
- if (!vertexArray || !(vertexArray instanceof WebGLVertexArrayObject)) return;
382
- vertexArray._pendingDelete = true;
383
- vertexArray._checkDelete();
384
- }
385
- isVertexArray(vertexArray) {
386
- if (!vertexArray || !(vertexArray instanceof WebGLVertexArrayObject)) return false;
387
- return this._native2.isVertexArray(vertexArray._);
388
- }
389
- bindVertexArray(array) {
390
- if (array === null) {
391
- this._native2.bindVertexArray(0);
392
- this._vertexObjectState = this._defaultVertexObjectState;
393
- } else if (array instanceof WebGLVertexArrayObject) {
394
- this._native2.bindVertexArray(array._);
395
- this._vertexObjectState = array._objectState;
396
- } else {
397
- this.setError(this.INVALID_OPERATION);
398
- }
399
- }
400
- // ─── Query Objects ────────────────────────────────────────────────────
401
- createQuery() {
402
- const id = this._native2.createQuery();
403
- if (!id) return null;
404
- const query = new WebGLQuery(id, this);
405
- this._queries[id] = query;
406
- return query;
407
- }
408
- deleteQuery(query) {
409
- if (!query || !(query instanceof WebGLQuery)) return;
410
- query._pendingDelete = true;
411
- query._checkDelete();
412
- }
413
- isQuery(query) {
414
- if (!query || !(query instanceof WebGLQuery)) return false;
415
- return this._native2.isQuery(query._);
416
- }
417
- beginQuery(target, query) {
418
- if (!(query instanceof WebGLQuery)) return;
419
- this._native2.beginQuery(target, query._);
420
- }
421
- endQuery(target) {
422
- this._native2.endQuery(target);
423
- }
424
- getQuery(_target, _pname) {
425
- warnNotImplemented("WebGL2RenderingContext.getQuery");
426
- return null;
427
- }
428
- getQueryParameter(query, pname) {
429
- if (!(query instanceof WebGLQuery)) return null;
430
- return this._native2.getQueryParameter(query._, pname);
431
- }
432
- // ─── Sampler Objects ──────────────────────────────────────────────────
433
- createSampler() {
434
- const id = this._native2.createSampler();
435
- if (!id) return null;
436
- const sampler = new WebGLSampler(id, this);
437
- this._samplers[id] = sampler;
438
- return sampler;
439
- }
440
- deleteSampler(sampler) {
441
- if (!sampler || !(sampler instanceof WebGLSampler)) return;
442
- sampler._pendingDelete = true;
443
- sampler._checkDelete();
444
- }
445
- isSampler(sampler) {
446
- if (!sampler || !(sampler instanceof WebGLSampler)) return false;
447
- return this._native2.isSampler(sampler._);
448
- }
449
- bindSampler(unit, sampler) {
450
- this._native2.bindSampler(unit, sampler ? sampler._ : 0);
451
- }
452
- samplerParameteri(sampler, pname, param) {
453
- if (!(sampler instanceof WebGLSampler)) return;
454
- this._native2.samplerParameteri(sampler._, pname, param);
455
- }
456
- samplerParameterf(sampler, pname, param) {
457
- if (!(sampler instanceof WebGLSampler)) return;
458
- this._native2.samplerParameterf(sampler._, pname, param);
459
- }
460
- getSamplerParameter(sampler, pname) {
461
- if (!(sampler instanceof WebGLSampler)) return null;
462
- if (pname === 33082 || pname === 33083) {
463
- return this._native2.getSamplerParameterf(sampler._, pname);
464
- }
465
- return this._native2.getSamplerParameteri(sampler._, pname);
466
- }
467
- // ─── Sync Objects ─────────────────────────────────────────────────────
468
- fenceSync(condition, flags) {
469
- const id = this._native2.fenceSync(condition, flags);
470
- if (!id) return null;
471
- const sync = new WebGLSync(id, this);
472
- this._syncs[id] = sync;
473
- return sync;
474
- }
475
- isSync(sync) {
476
- if (!sync || !(sync instanceof WebGLSync)) return false;
477
- return this._native2.isSync(sync._);
478
- }
479
- deleteSync(sync) {
480
- if (!sync || !(sync instanceof WebGLSync)) return;
481
- sync._pendingDelete = true;
482
- sync._checkDelete();
483
- }
484
- clientWaitSync(sync, flags, timeout) {
485
- if (!(sync instanceof WebGLSync)) return 37148;
486
- return this._native2.clientWaitSync(sync._, flags, timeout);
487
- }
488
- waitSync(sync, flags, timeout) {
489
- if (!(sync instanceof WebGLSync)) return;
490
- this._native2.waitSync(sync._, flags, timeout);
491
- }
492
- getSyncParameter(sync, pname) {
493
- if (!(sync instanceof WebGLSync)) return null;
494
- return this._native2.getSyncParameter(sync._, pname);
495
- }
496
- // ─── Transform Feedback ───────────────────────────────────────────────
497
- createTransformFeedback() {
498
- const id = this._native2.createTransformFeedback();
499
- if (!id) return null;
500
- const tf = new WebGLTransformFeedback(id, this);
501
- this._transformFeedbacks[id] = tf;
502
- return tf;
503
- }
504
- deleteTransformFeedback(tf) {
505
- if (!tf || !(tf instanceof WebGLTransformFeedback)) return;
506
- tf._pendingDelete = true;
507
- tf._checkDelete();
508
- }
509
- isTransformFeedback(tf) {
510
- if (!tf || !(tf instanceof WebGLTransformFeedback)) return false;
511
- return this._native2.isTransformFeedback(tf._);
512
- }
513
- bindTransformFeedback(target, tf) {
514
- this._native2.bindTransformFeedback(target, tf ? tf._ : 0);
515
- }
516
- beginTransformFeedback(primitiveMode) {
517
- this._native2.beginTransformFeedback(primitiveMode);
518
- }
519
- endTransformFeedback() {
520
- this._native2.endTransformFeedback();
521
- }
522
- pauseTransformFeedback() {
523
- this._native2.pauseTransformFeedback();
524
- }
525
- resumeTransformFeedback() {
526
- this._native2.resumeTransformFeedback();
527
- }
528
- transformFeedbackVaryings(program, varyings, bufferMode) {
529
- this._native2.transformFeedbackVaryings(program._, varyings, bufferMode);
530
- }
531
- getTransformFeedbackVarying(program, index) {
532
- const result = this._native2.getTransformFeedbackVarying(program._, index).deepUnpack();
533
- return new WebGLActiveInfo({ size: result.size, type: result.type, name: result.name });
534
- }
535
- // ─── Indexed Buffer Binding ───────────────────────────────────────────
536
- bindBufferBase(target, index, buffer) {
537
- this._native2.bindBufferBase(target, index, buffer ? buffer._ : 0);
538
- }
539
- bindBufferRange(target, index, buffer, offset, size) {
540
- this._native2.bindBufferRange(target, index, buffer ? buffer._ : 0, offset, size);
541
- }
542
- copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size) {
543
- this._native2.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
544
- }
545
- getBufferSubData(target, srcByteOffset, dstBuffer, dstOffset, length) {
546
- const byteLength = length !== void 0 ? length : dstBuffer.byteLength - (dstOffset ?? 0);
547
- const data = this._native2.getBufferSubData(target, srcByteOffset, byteLength);
548
- const dst = new Uint8Array(dstBuffer.buffer, dstBuffer.byteOffset + (dstOffset ?? 0) * (dstBuffer instanceof Uint8Array ? 1 : dstBuffer.BYTES_PER_ELEMENT ?? 1));
549
- dst.set(data.subarray(0, dst.byteLength));
550
- }
551
- // ─── 3D Textures ──────────────────────────────────────────────────────
552
- texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels) {
553
- if (pixels === null) {
554
- this._native2.texImage3DNull(target, level, internalformat, width, height, depth, border, format, type);
555
- } else {
556
- this._native2.texImage3D(target, level, internalformat, width, height, depth, border, format, type, Uint8ArrayToVariant(new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength)));
557
- }
558
- }
559
- texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) {
560
- if (pixels === null) return;
561
- this._native2.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, Uint8ArrayToVariant(new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength)));
562
- }
563
- compressedTexImage3D(target, level, internalformat, width, height, depth, border, _imageSize, data) {
564
- this._native2.compressedTexImage3D(target, level, internalformat, width, height, depth, border, Uint8ArrayToVariant(new Uint8Array(data.buffer, data.byteOffset, data.byteLength)));
565
- }
566
- compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, _imageSize, data) {
567
- this._native2.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, Uint8ArrayToVariant(new Uint8Array(data.buffer, data.byteOffset, data.byteLength)));
568
- }
569
- copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height) {
570
- this._native2.copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);
571
- }
572
- texStorage2D(target, levels, internalformat, width, height) {
573
- this._native2.texStorage2D(target, levels, internalformat, width, height);
574
- const texture = this._getTexImage(target);
575
- if (texture) {
576
- for (let lvl = 0; lvl < levels; lvl++) {
577
- texture._levelWidth[lvl] = Math.max(1, width >> lvl);
578
- texture._levelHeight[lvl] = Math.max(1, height >> lvl);
579
- }
580
- texture._format = this.RGBA;
581
- texture._type = this.UNSIGNED_BYTE;
582
- }
583
- }
584
- texStorage3D(target, levels, internalformat, width, height, depth) {
585
- this._native2.texStorage3D(target, levels, internalformat, width, height, depth);
586
- }
587
- texImage2D(target = 0, level = 0, internalFormat = 0, formatOrWidth = 0, typeOrHeight = 0, sourceOrBorder = 0, _format = 0, type = 0, pixels) {
588
- let width = 0;
589
- let height = 0;
590
- let format = 0;
591
- let border = 0;
592
- if (arguments.length === 6) {
593
- type = typeOrHeight;
594
- format = formatOrWidth;
595
- if (sourceOrBorder instanceof GdkPixbuf.Pixbuf) {
596
- const pixbuf = sourceOrBorder;
597
- width = pixbuf.get_width();
598
- height = pixbuf.get_height();
599
- pixels = pixbuf.get_pixels();
600
- } else {
601
- const imageData = extractImageData(sourceOrBorder);
602
- if (imageData == null) {
603
- throw new TypeError("texImage2D(GLenum, GLint, GLenum, GLint, GLenum, GLenum, ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement)");
604
- }
605
- width = imageData.width;
606
- height = imageData.height;
607
- pixels = imageData.data;
608
- }
609
- } else if (arguments.length >= 9) {
610
- width = formatOrWidth;
611
- height = typeOrHeight;
612
- border = sourceOrBorder;
613
- format = _format;
614
- }
615
- const texture = this._getTexImage(target);
616
- if (!texture) {
617
- this.setError(this.INVALID_OPERATION);
618
- return;
619
- }
620
- let data = convertPixels(pixels);
621
- if (this._unpackPremultAlpha && data && format === this.RGBA) {
622
- data = premultiplyAlpha(data);
623
- }
624
- if (this._unpackFlipY && data && width > 0 && height > 0) {
625
- const pixelSize = this._computePixelSize(type, format);
626
- if (pixelSize > 0) {
627
- const rowStride = this._computeRowStride(width, pixelSize);
628
- const flipped = new Uint8Array(data.length);
629
- for (let row = 0; row < height; row++) {
630
- const srcOffset = row * rowStride;
631
- const dstOffset = (height - 1 - row) * rowStride;
632
- flipped.set(data.subarray(srcOffset, srcOffset + rowStride), dstOffset);
633
- }
634
- data = flipped;
635
- }
636
- }
637
- this._saveError();
638
- this._gl.texImage2D(target, level, internalFormat, width, height, border, format, type, Uint8ArrayToVariant(data));
639
- const error = this.getError();
640
- this._restoreError(error);
641
- if (error !== this.NO_ERROR) return;
642
- texture._levelWidth[level] = width;
643
- texture._levelHeight[level] = height;
644
- texture._format = format;
645
- texture._type = type;
646
- const activeFramebuffer = this._activeFramebuffer;
647
- if (activeFramebuffer) {
648
- let needsUpdate = false;
649
- const attachments = this._getAttachments();
650
- for (let i = 0; i < attachments.length; ++i) {
651
- if (activeFramebuffer._attachments[attachments[i]] === texture) {
652
- needsUpdate = true;
653
- break;
654
- }
655
- }
656
- if (needsUpdate && this._activeFramebuffer) {
657
- this._updateFramebufferAttachments(this._activeFramebuffer);
658
- }
659
- }
660
- }
661
- texSubImage2D(target = 0, level = 0, xoffset = 0, yoffset = 0, formatOrWidth = 0, typeOrHeight = 0, sourceOrFormat = 0, type = 0, pixels) {
662
- let width = 0;
663
- let height = 0;
664
- let format = 0;
665
- if (arguments.length === 7) {
666
- type = typeOrHeight;
667
- format = formatOrWidth;
668
- if (sourceOrFormat instanceof GdkPixbuf.Pixbuf) {
669
- const pixbuf = sourceOrFormat;
670
- width = pixbuf.get_width();
671
- height = pixbuf.get_height();
672
- pixels = pixbuf.get_pixels();
673
- } else {
674
- const imageData = extractImageData(sourceOrFormat);
675
- if (imageData == null) {
676
- throw new TypeError("texSubImage2D(GLenum, GLint, GLint, GLint, GLenum, GLenum, ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement)");
677
- }
678
- width = imageData.width;
679
- height = imageData.height;
680
- pixels = imageData.data;
681
- }
682
- } else {
683
- width = formatOrWidth;
684
- height = typeOrHeight;
685
- format = sourceOrFormat;
686
- }
687
- const texture = this._getTexImage(target);
688
- if (!texture) {
689
- this.setError(this.INVALID_OPERATION);
690
- return;
691
- }
692
- let data = convertPixels(pixels);
693
- if (!data) {
694
- this.setError(this.INVALID_OPERATION);
695
- return;
696
- }
697
- if (this._unpackPremultAlpha && data && format === this.RGBA) {
698
- data = premultiplyAlpha(data);
699
- }
700
- if (this._unpackFlipY && data && width > 0 && height > 0) {
701
- const pixelSize = this._computePixelSize(type, format);
702
- if (pixelSize > 0) {
703
- const rowStride = this._computeRowStride(width, pixelSize);
704
- const flipped = new Uint8Array(data.length);
705
- for (let row = 0; row < height; row++) {
706
- const srcOffset = row * rowStride;
707
- const dstOffset = (height - 1 - row) * rowStride;
708
- flipped.set(data.subarray(srcOffset, srcOffset + rowStride), dstOffset);
709
- }
710
- data = flipped;
711
- }
712
- }
713
- this._gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, Uint8ArrayToVariant(data));
714
- }
715
- framebufferTextureLayer(target, attachment, texture, level, layer) {
716
- this._native2.framebufferTextureLayer(target, attachment, texture ? texture._ : 0, level, layer);
717
- }
718
- // ─── Instancing & Advanced Draw ───────────────────────────────────────
719
- drawArraysInstanced(mode, first, count, instanceCount) {
720
- if (first < 0 || count < 0 || instanceCount < 0) {
721
- this.setError(this.INVALID_VALUE);
722
- return;
723
- }
724
- if (!this._checkStencilState()) return;
725
- const rc = vertexCount(this, mode, count);
726
- if (rc < 0) {
727
- this.setError(this.INVALID_ENUM);
728
- return;
729
- }
730
- if (!this._framebufferOk()) return;
731
- if (count === 0 || instanceCount === 0) return;
732
- if (!this._checkVertexAttribState(count + first - 1 >>> 0)) return;
733
- if (globalThis.__GJSIFY_DEBUG_GL) {
734
- const n = this.__drawInstCount = (this.__drawInstCount | 0) + 1;
735
- if (n <= 5 || n % 100 === 0) console.log(`[WebGL] drawArraysInstanced #${n} count=${rc} instances=${instanceCount} fbo=${this._activeFramebuffer?._ ?? "_gtkFbo"}`);
736
- }
737
- this._native2.drawArraysInstanced(mode, first, rc, instanceCount);
738
- }
739
- drawElementsInstanced(mode, count, type, offset, instanceCount) {
740
- if (count < 0 || offset < 0 || instanceCount < 0) {
741
- this.setError(this.INVALID_VALUE);
742
- return;
743
- }
744
- if (!this._checkStencilState()) return;
745
- const elementBuffer = this._vertexObjectState._elementArrayBufferBinding;
746
- if (!elementBuffer) {
747
- this.setError(this.INVALID_OPERATION);
748
- return;
749
- }
750
- let elementData = null;
751
- let adjustedOffset = offset;
752
- if (type === this.UNSIGNED_SHORT) {
753
- if (adjustedOffset % 2) {
754
- this.setError(this.INVALID_OPERATION);
755
- return;
756
- }
757
- adjustedOffset >>= 1;
758
- elementData = new Uint16Array(elementBuffer._elements.buffer);
759
- } else if (type === this.UNSIGNED_INT) {
760
- if (adjustedOffset % 4) {
761
- this.setError(this.INVALID_OPERATION);
762
- return;
763
- }
764
- adjustedOffset >>= 2;
765
- elementData = new Uint32Array(elementBuffer._elements.buffer);
766
- } else if (type === this.UNSIGNED_BYTE) {
767
- elementData = elementBuffer._elements;
768
- } else {
769
- this.setError(this.INVALID_ENUM);
770
- return;
771
- }
772
- let reducedCount = count;
773
- switch (mode) {
774
- case this.TRIANGLES:
775
- if (count % 3) reducedCount -= count % 3;
776
- break;
777
- case this.LINES:
778
- if (count % 2) reducedCount -= count % 2;
779
- break;
780
- case this.POINTS:
781
- break;
782
- case this.LINE_LOOP:
783
- case this.LINE_STRIP:
784
- if (count < 2) {
785
- this.setError(this.INVALID_OPERATION);
786
- return;
787
- }
788
- break;
789
- case this.TRIANGLE_FAN:
790
- case this.TRIANGLE_STRIP:
791
- if (count < 3) {
792
- this.setError(this.INVALID_OPERATION);
793
- return;
794
- }
795
- break;
796
- default:
797
- this.setError(this.INVALID_ENUM);
798
- return;
799
- }
800
- if (!this._framebufferOk()) return;
801
- if (reducedCount === 0 || instanceCount === 0) {
802
- this._checkVertexAttribState(0);
803
- return;
804
- }
805
- if (reducedCount + adjustedOffset >>> 0 > elementData.length) {
806
- this.setError(this.INVALID_OPERATION);
807
- return;
808
- }
809
- let maxIndex = 0;
810
- for (let i = adjustedOffset; i < adjustedOffset + reducedCount; ++i) {
811
- if (elementData[i] > maxIndex) maxIndex = elementData[i];
812
- }
813
- if (this._checkVertexAttribState(maxIndex)) {
814
- this._native2.drawElementsInstanced(mode, reducedCount, type, offset, instanceCount);
815
- }
816
- }
817
- vertexAttribDivisor(index, divisor) {
818
- this._native2.vertexAttribDivisor(index, divisor);
819
- }
820
- vertexAttribIPointer(index, size, type, stride, offset) {
821
- this._native2.vertexAttribIPointer(index, size, type, stride, offset);
822
- }
823
- drawBuffers(buffers) {
824
- let hasBack = false;
825
- for (let i = 0; i < buffers.length; i++) {
826
- if (buffers[i] === 1029) {
827
- hasBack = true;
828
- break;
829
- }
830
- }
831
- if (!hasBack) {
832
- this._native2.drawBuffers(buffers);
833
- return;
834
- }
835
- this._native2.drawBuffers(buffers.map((b) => b === 1029 ? this.COLOR_ATTACHMENT0 : b));
836
- }
837
- drawRangeElements(mode, start, end, count, type, offset) {
838
- if (count < 0 || offset < 0) {
839
- this.setError(this.INVALID_VALUE);
840
- return;
841
- }
842
- if (end < start) {
843
- this.setError(this.INVALID_VALUE);
844
- return;
845
- }
846
- this.drawElements(mode, count, type, offset);
847
- }
848
- blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) {
849
- if (globalThis.__GJSIFY_DEBUG_GL) {
850
- const errBefore = this._gl.getError();
851
- if (errBefore !== 0) console.log(`[WebGL] blitFramebuffer PRE-ERROR 0x${errBefore.toString(16)}`);
852
- }
853
- this._native2.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
854
- if (globalThis.__GJSIFY_DEBUG_GL) {
855
- const err = this._gl.getError();
856
- const n = this.__blitCount = (this.__blitCount | 0) + 1;
857
- if (n <= 5) console.log(`[WebGL] blitFramebuffer #${n} src=(${srcX0},${srcY0},${srcX1},${srcY1}) readFbo=${this._activeReadFramebuffer?._ ?? "_gtkFbo"} err=${err === 0 ? "OK" : "0x" + err.toString(16)}`);
858
- }
859
- }
860
- // clearBuffer{fv,iv,uiv,fi} WebGL2 methods for clearing specific
861
- // framebuffer attachments. The native Vala binding does not expose the
862
- // glClearBuffer* entry points yet, so we emulate the common cases via
863
- // glClearColor/glClearDepth/glClearStencil + glClear. This is equivalent
864
- // when the DRAW_FRAMEBUFFER has a single attachment per buffer type,
865
- // which matches Excalibur's ExcaliburGraphicsContextWebGL.blitToScreen.
866
- //
867
- // Buffer target constants per WebGL2 spec (not on our class):
868
- // COLOR = 0x1800
869
- // DEPTH = 0x1801
870
- // STENCIL = 0x1802
871
- // DEPTH_STENCIL = 0x84F9
872
- clearBufferfv(buffer, drawbuffer, values, _srcOffset) {
873
- const n2 = this._native2;
874
- if (typeof n2.clearBufferfv === "function") {
875
- n2.clearBufferfv(buffer, drawbuffer, Array.from(values));
876
- return;
877
- }
878
- const v = values;
879
- if (buffer === 6144) {
880
- const prev = this.getParameter(this.COLOR_CLEAR_VALUE);
881
- this.clearColor(v[0] ?? 0, v[1] ?? 0, v[2] ?? 0, v[3] ?? 0);
882
- this.clear(this.COLOR_BUFFER_BIT);
883
- if (prev) this.clearColor(prev[0], prev[1], prev[2], prev[3]);
884
- } else if (buffer === 6145) {
885
- const prev = this.getParameter(this.DEPTH_CLEAR_VALUE);
886
- this.clearDepth(v[0] ?? 1);
887
- this.clear(this.DEPTH_BUFFER_BIT);
888
- if (prev !== null) this.clearDepth(prev);
889
- }
890
- }
891
- clearBufferiv(buffer, drawbuffer, values, _srcOffset) {
892
- const n2 = this._native2;
893
- if (typeof n2.clearBufferiv === "function") {
894
- n2.clearBufferiv(buffer, drawbuffer, Array.from(values));
895
- return;
896
- }
897
- if (buffer === 6146) {
898
- const v = values;
899
- const prev = this.getParameter(this.STENCIL_CLEAR_VALUE);
900
- this.clearStencil(v[0] ?? 0);
901
- this.clear(this.STENCIL_BUFFER_BIT);
902
- if (prev !== null) this.clearStencil(prev);
903
- }
904
- }
905
- clearBufferuiv(buffer, drawbuffer, values, _srcOffset) {
906
- const n2 = this._native2;
907
- if (typeof n2.clearBufferuiv === "function") {
908
- n2.clearBufferuiv(buffer, drawbuffer, Array.from(values));
909
- return;
910
- }
911
- void buffer;
912
- void drawbuffer;
913
- }
914
- clearBufferfi(buffer, drawbuffer, depth, stencil) {
915
- const n2 = this._native2;
916
- if (typeof n2.clearBufferfi === "function") {
917
- n2.clearBufferfi(buffer, drawbuffer, depth, stencil);
918
- return;
919
- }
920
- if (buffer === 34041) {
921
- const prevDepth = this.getParameter(this.DEPTH_CLEAR_VALUE);
922
- const prevStencil = this.getParameter(this.STENCIL_CLEAR_VALUE);
923
- this.clearDepth(depth);
924
- this.clearStencil(stencil);
925
- this.clear(this.DEPTH_BUFFER_BIT | this.STENCIL_BUFFER_BIT);
926
- if (prevDepth !== null) this.clearDepth(prevDepth);
927
- if (prevStencil !== null) this.clearStencil(prevStencil);
928
- }
929
- void drawbuffer;
930
- }
931
- invalidateFramebuffer(target, attachments) {
932
- this._native2.invalidateFramebuffer(target, attachments);
933
- }
934
- invalidateSubFramebuffer(target, attachments, x, y, width, height) {
935
- this._native2.invalidateSubFramebuffer(target, attachments, x, y, width, height);
936
- }
937
- readBuffer(src) {
938
- this._native2.readBuffer(src);
939
- }
940
- renderbufferStorageMultisample(target, samples, internalFormat, width, height) {
941
- if (target !== this.RENDERBUFFER) {
942
- this.setError(this.INVALID_ENUM);
943
- return;
944
- }
945
- const renderbuffer = this._activeRenderbuffer;
946
- if (!renderbuffer) {
947
- this.setError(this.INVALID_OPERATION);
948
- return;
949
- }
950
- this._saveError();
951
- this._native2.renderbufferStorageMultisample(target, samples, internalFormat, width, height);
952
- const error = this.getError();
953
- this._restoreError(error);
954
- if (error !== this.NO_ERROR) return;
955
- renderbuffer._width = width;
956
- renderbuffer._height = height;
957
- renderbuffer._format = internalFormat;
958
- }
959
- // ─── Unsigned Integer Uniforms ────────────────────────────────────────
960
- uniform1ui(location, v0) {
961
- if (!location) return;
962
- this._native2.uniform1ui(location._, v0);
963
- }
964
- uniform2ui(location, v0, v1) {
965
- if (!location) return;
966
- this._native2.uniform2ui(location._, v0, v1);
967
- }
968
- uniform3ui(location, v0, v1, v2) {
969
- if (!location) return;
970
- this._native2.uniform3ui(location._, v0, v1, v2);
971
- }
972
- uniform4ui(location, v0, v1, v2, v3) {
973
- if (!location) return;
974
- this._native2.uniform4ui(location._, v0, v1, v2, v3);
975
- }
976
- uniform1uiv(location, data, _srcOffset, _srcLength) {
977
- if (!location) return;
978
- const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
979
- this._native2.uniform1uiv(location._, arr.length, arr);
980
- }
981
- uniform2uiv(location, data, _srcOffset, _srcLength) {
982
- if (!location) return;
983
- const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
984
- this._native2.uniform2uiv(location._, arr.length / 2, arr);
985
- }
986
- uniform3uiv(location, data, _srcOffset, _srcLength) {
987
- if (!location) return;
988
- const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
989
- this._native2.uniform3uiv(location._, arr.length / 3, arr);
990
- }
991
- uniform4uiv(location, data, _srcOffset, _srcLength) {
992
- if (!location) return;
993
- const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
994
- this._native2.uniform4uiv(location._, arr.length / 4, arr);
995
- }
996
- // ─── Non-square Matrix Uniforms ───────────────────────────────────────
997
- uniformMatrix2x3fv(location, transpose, data, _srcOffset, _srcLength) {
998
- if (!location) return;
999
- const arr = data instanceof Float32Array ? data : new Float32Array(data);
1000
- this._native2.uniformMatrix2x3fv(location._, transpose, arr);
1001
- }
1002
- uniformMatrix3x2fv(location, transpose, data, _srcOffset, _srcLength) {
1003
- if (!location) return;
1004
- const arr = data instanceof Float32Array ? data : new Float32Array(data);
1005
- this._native2.uniformMatrix3x2fv(location._, transpose, arr);
1006
- }
1007
- uniformMatrix2x4fv(location, transpose, data, _srcOffset, _srcLength) {
1008
- if (!location) return;
1009
- const arr = data instanceof Float32Array ? data : new Float32Array(data);
1010
- this._native2.uniformMatrix2x4fv(location._, transpose, arr);
1011
- }
1012
- uniformMatrix4x2fv(location, transpose, data, _srcOffset, _srcLength) {
1013
- if (!location) return;
1014
- const arr = data instanceof Float32Array ? data : new Float32Array(data);
1015
- this._native2.uniformMatrix4x2fv(location._, transpose, arr);
1016
- }
1017
- uniformMatrix3x4fv(location, transpose, data, _srcOffset, _srcLength) {
1018
- if (!location) return;
1019
- const arr = data instanceof Float32Array ? data : new Float32Array(data);
1020
- this._native2.uniformMatrix3x4fv(location._, transpose, arr);
1021
- }
1022
- uniformMatrix4x3fv(location, transpose, data, _srcOffset, _srcLength) {
1023
- if (!location) return;
1024
- const arr = data instanceof Float32Array ? data : new Float32Array(data);
1025
- this._native2.uniformMatrix4x3fv(location._, transpose, arr);
1026
- }
1027
- // ─── getUniform — WebGL2 uint type support ────────────────────────────
1028
- /** WebGL1 getUniform falls to default:null for UNSIGNED_INT types. Handle them here. */
1029
- getUniform(program, location) {
1030
- const type = location?._activeInfo?.type;
1031
- const UINT = 5125, UVEC2 = 36294, UVEC3 = 36295, UVEC4 = 36296;
1032
- const isUintType = type === UINT || type === UVEC2 || type === UVEC3 || type === UVEC4;
1033
- if (!isUintType) return super.getUniform(program, location);
1034
- if (!program || !location) return null;
1035
- const data = this._gl.getUniformi(program._ | 0, location._ | 0);
1036
- if (!data) return null;
1037
- if (type === UINT) return data[0] >>> 0;
1038
- if (type === UVEC2) return new Uint32Array([data[0] >>> 0, data[1] >>> 0]);
1039
- if (type === UVEC3) return new Uint32Array([data[0] >>> 0, data[1] >>> 0, data[2] >>> 0]);
1040
- return new Uint32Array([data[0] >>> 0, data[1] >>> 0, data[2] >>> 0, data[3] >>> 0]);
1041
- }
1042
- // ─── Uniform Blocks ───────────────────────────────────────────────────
1043
- getUniformBlockIndex(program, uniformBlockName) {
1044
- return this._native2.getUniformBlockIndex(program._, uniformBlockName);
1045
- }
1046
- uniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding) {
1047
- this._native2.uniformBlockBinding(program._, uniformBlockIndex, uniformBlockBinding);
1048
- }
1049
- getActiveUniformBlockName(program, uniformBlockIndex) {
1050
- const name = this._native2.getActiveUniformBlockName(program._, uniformBlockIndex);
1051
- return name.length > 0 ? name : null;
1052
- }
1053
- getActiveUniformBlockParameter(program, uniformBlockIndex, pname) {
1054
- return this._native2.getActiveUniformBlockParameter(program._, uniformBlockIndex, pname);
1055
- }
1056
- getActiveUniforms(program, uniformIndices, pname) {
1057
- const result = this._native2.getActiveUniforms(program._, uniformIndices, pname);
1058
- return result;
1059
- }
1060
- // ─── Program Queries ──────────────────────────────────────────────────
1061
- getFragDataLocation(program, name) {
1062
- return this._native2.getFragDataLocation(program._, name);
1063
- }
1064
- // ─── Indexed Parameter Queries ────────────────────────────────────────
1065
- getIndexedParameter(target, index) {
1066
- return this._native2.getIndexedParameteri(target, index);
1067
- }
1068
- getInternalformatParameter(target, internalformat, pname) {
1069
- return this._native2.getInternalformatParameter(target, internalformat, pname);
1070
- }
1071
- getParameter(pname) {
1072
- if (pname === 7938) return "WebGL 2.0";
1073
- if (pname === 35724) return "WebGL GLSL ES 3.00";
1074
- if (pname === 7939) {
1075
- warnNotImplemented("WebGL2RenderingContext.getParameter(GL_EXTENSIONS)");
1076
- return "";
1077
- }
1078
- if (pname === 36006) return this._activeDrawFramebuffer;
1079
- if (pname === 36010) return this._activeReadFramebuffer;
1080
- switch (pname) {
1081
- case 36183:
1082
- // MAX_SAMPLES
1083
- case 35071:
1084
- // MAX_ARRAY_TEXTURE_LAYERS
1085
- case 32883:
1086
- // MAX_3D_TEXTURE_SIZE
1087
- case 36063:
1088
- // MAX_COLOR_ATTACHMENTS
1089
- case 34852:
1090
- // MAX_DRAW_BUFFERS
1091
- case 36203:
1092
- // MAX_ELEMENT_INDEX
1093
- case 33001:
1094
- // MAX_ELEMENTS_INDICES
1095
- case 33e3:
1096
- // MAX_ELEMENTS_VERTICES
1097
- case 37157:
1098
- // MAX_FRAGMENT_INPUT_COMPONENTS
1099
- case 35373:
1100
- // MAX_FRAGMENT_UNIFORM_BLOCKS
1101
- case 35657:
1102
- // MAX_FRAGMENT_UNIFORM_COMPONENTS
1103
- case 35077:
1104
- // MAX_PROGRAM_TEXEL_OFFSET
1105
- case 35978:
1106
- // MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS
1107
- case 35979:
1108
- // MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS
1109
- case 35968:
1110
- // MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS
1111
- case 35376:
1112
- // MAX_UNIFORM_BLOCK_SIZE
1113
- case 35375:
1114
- // MAX_UNIFORM_BUFFER_BINDINGS
1115
- case 35659:
1116
- // MAX_VARYING_COMPONENTS
1117
- case 37154:
1118
- // MAX_VERTEX_OUTPUT_COMPONENTS
1119
- case 35371:
1120
- // MAX_VERTEX_UNIFORM_BLOCKS
1121
- case 35658:
1122
- // MAX_VERTEX_UNIFORM_COMPONENTS
1123
- case 35379:
1124
- // MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS
1125
- case 35374:
1126
- // MAX_COMBINED_UNIFORM_BLOCKS
1127
- case 35377:
1128
- // MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS
1129
- case 35076:
1130
- // MIN_PROGRAM_TEXEL_OFFSET
1131
- case 3330:
1132
- // PACK_ROW_LENGTH
1133
- case 3332:
1134
- // PACK_SKIP_PIXELS
1135
- case 3331:
1136
- // PACK_SKIP_ROWS
1137
- case 35053:
1138
- // PIXEL_PACK_BUFFER_BINDING
1139
- case 35055:
1140
- // PIXEL_UNPACK_BUFFER_BINDING
1141
- case 3074:
1142
- // READ_BUFFER
1143
- case 32874:
1144
- // TEXTURE_BINDING_3D
1145
- case 35869:
1146
- // TEXTURE_BINDING_2D_ARRAY
1147
- case 36389:
1148
- // TRANSFORM_FEEDBACK_BINDING
1149
- case 35983:
1150
- // TRANSFORM_FEEDBACK_BUFFER_BINDING
1151
- case 35368:
1152
- // UNIFORM_BUFFER_BINDING
1153
- case 35380:
1154
- // UNIFORM_BUFFER_OFFSET_ALIGNMENT
1155
- case 32878:
1156
- // UNPACK_IMAGE_HEIGHT
1157
- case 3314:
1158
- // UNPACK_ROW_LENGTH
1159
- case 32877:
1160
- // UNPACK_SKIP_IMAGES
1161
- case 3316:
1162
- // UNPACK_SKIP_PIXELS
1163
- case 3315:
1164
- // UNPACK_SKIP_ROWS
1165
- case 34045:
1166
- return this._native2.getParameterx(pname)?.deepUnpack() | 0;
1167
- case 35977:
1168
- // RASTERIZER_DISCARD
1169
- case 36388:
1170
- // TRANSFORM_FEEDBACK_ACTIVE
1171
- case 36387:
1172
- return !!this._native2.getParameterx(pname)?.deepUnpack();
1173
- }
1174
- return super.getParameter(pname);
1175
- }
1176
- // ─── Misc ─────────────────────────────────────────────────────────────
1177
- getStringi(name, index) {
1178
- const s = this._native2.getStringi(name, index);
1179
- return s.length > 0 ? s : null;
1180
- }
1181
- // ─── WebGL2 overrides for format validation ────────────────────────────
1182
- /**
1183
- * WebGL2 supports ~30+ renderbuffer formats (R8, RG8, RGBA8, RGBA16F,
1184
- * DEPTH_COMPONENT24, DEPTH32F_STENCIL8, etc.). The WebGL1 base class
1185
- * only allows 7 formats. Delegate format validation to native GL.
1186
- */
1187
- renderbufferStorage(target, internalFormat, width, height) {
1188
- if (target !== this.RENDERBUFFER) {
1189
- this.setError(this.INVALID_ENUM);
1190
- return;
1191
- }
1192
- const renderbuffer = this._activeRenderbuffer;
1193
- if (!renderbuffer) {
1194
- this.setError(this.INVALID_OPERATION);
1195
- return;
1196
- }
1197
- if (width < 0 || height < 0) {
1198
- this.setError(this.INVALID_VALUE);
1199
- return;
1200
- }
1201
- while (this._gl.getError() !== this.NO_ERROR) {
1202
- }
1203
- this._gl.renderbufferStorage(target, internalFormat, width, height);
1204
- if (this._gl.getError() !== this.NO_ERROR) return;
1205
- renderbuffer._width = width;
1206
- renderbuffer._height = height;
1207
- renderbuffer._format = internalFormat;
1208
- const activeFramebuffer = this._activeFramebuffer;
1209
- if (activeFramebuffer) {
1210
- const attachments = this._getAttachments();
1211
- let needsUpdate = false;
1212
- for (let i = 0; i < attachments.length; ++i) {
1213
- if (activeFramebuffer._attachments[attachments[i]] === renderbuffer) {
1214
- needsUpdate = true;
1215
- break;
1216
- }
1217
- }
1218
- if (needsUpdate) this._updateFramebufferAttachments(activeFramebuffer);
1219
- }
1220
- }
1221
- /**
1222
- * WebGL2 makes several WebGL1 extensions part of the core spec.
1223
- * EXT_color_buffer_float and EXT_color_buffer_half_float are always
1224
- * available in WebGL2 contexts. Append them if the base class didn't.
1225
- */
1226
- getSupportedExtensions() {
1227
- const exts = super.getSupportedExtensions();
1228
- const ensure = ["EXT_color_buffer_float", "EXT_color_buffer_half_float", "OES_texture_half_float"];
1229
- for (const ext of ensure) {
1230
- if (exts.indexOf(ext) === -1) exts.push(ext);
1231
- }
1232
- return exts;
1233
- }
1234
- /**
1235
- * WebGL2 allows reading pixels in many more format/type combinations
1236
- * than WebGL1's strict RGBA/UNSIGNED_BYTE. Delegate validation to native.
1237
- */
1238
- readPixels(x, y, width, height, format, type, pixels) {
1239
- if (!pixels) return;
1240
- if (width < 0 || height < 0) {
1241
- this.setError(this.INVALID_VALUE);
1242
- return;
1243
- }
1244
- if (!this._framebufferOk()) return;
1245
- const componentCount = format === 6408 || format === 32856 ? 4 : format === 6407 ? 3 : format === 33319 ? 2 : 1;
1246
- const bytesPerComponent = type === 5126 ? 4 : type === 5131 || type === 36193 ? 2 : type === 5125 || type === 5124 ? 4 : type === 5123 || type === 5122 ? 2 : 1;
1247
- const byteCount = width * height * componentCount * bytesPerComponent;
1248
- const pixelData = new Uint8Array(byteCount);
1249
- this._saveError();
1250
- const result = this._gl.readPixels(x, y, width, height, format, type, Uint8ArrayToVariant(pixelData));
1251
- const error = this.getError();
1252
- this._restoreError(error);
1253
- if (error !== this.NO_ERROR) return;
1254
- const src = result && result.length > 0 ? result : pixelData;
1255
- if (pixels instanceof Uint8Array) {
1256
- pixels.set(src);
1257
- } else if (pixels instanceof Float32Array) {
1258
- const floatView = new Float32Array(src.buffer, 0, pixels.length);
1259
- pixels.set(floatView);
1260
- }
1261
- }
1262
- // framebufferTexture2D: inherits from base class. WebGL2 allows level>0 for
1263
- // mipmap attachments, but Three.js only uses level=0. The base class level===0
1264
- // check is acceptable for now. If needed, override to skip level validation.
1265
- /**
1266
- * WebGL2 never blocks draw calls on JS-side framebuffer format checks.
1267
- * The native GL (Mesa/libepoxy) handles completeness and generates
1268
- * INVALID_FRAMEBUFFER_OPERATION for truly incomplete FBOs at draw time.
1269
- * This matches headless-gl's approach: _framebufferOk() always returns true.
1270
- *
1271
- * The base class rejects valid WebGL2 formats (RGBA16F/HALF_FLOAT, depth
1272
- * textures, WebGL2 renderbuffer formats) causing silent rendering failures
1273
- * for postprocessing effects and environment maps.
1274
- */
1275
- _framebufferOk() {
1276
- return true;
1277
- }
1278
- }
1279
- export {
1280
- WebGL2RenderingContext
15
+
16
+ //#region src/ts/webgl2-rendering-context.ts
17
+ var WebGL2RenderingContext = class WebGL2RenderingContext extends WebGLContextBase {
18
+ get _gl() {
19
+ return this._native2;
20
+ }
21
+ constructor(canvas, options = {}) {
22
+ super(canvas, options);
23
+ this._queries = {};
24
+ this._samplers = {};
25
+ this._transformFeedbacks = {};
26
+ this._vertexArrayObjects = {};
27
+ this._syncs = {};
28
+ this._activeReadFramebuffer = null;
29
+ this._activeDrawFramebuffer = null;
30
+ this._native2 = new Gwebgl.WebGL2RenderingContext({});
31
+ this._init();
32
+ }
33
+ _getGlslVersion(es) {
34
+ return es ? "300 es" : "130";
35
+ }
36
+ /**
37
+ * WebGL2 delegates framebuffer completeness to the native GL driver.
38
+ * Called by _framebufferOk() before draw calls and by _updateFramebufferAttachments.
39
+ * The base class version uses JS-side format whitelists that reject valid WebGL2 formats.
40
+ */
41
+ /** WebGL2 allows COLOR_ATTACHMENT1–15 as framebuffer attachment points. */
42
+ _validFramebufferAttachment(attachment) {
43
+ if (super._validFramebufferAttachment(attachment)) return true;
44
+ return attachment >= 36065 && attachment <= 36079;
45
+ }
46
+ static {
47
+ this._WGL2_ALL_COLOR_ATTACHMENTS = [
48
+ 36064,
49
+ 36065,
50
+ 36066,
51
+ 36067,
52
+ 36068,
53
+ 36069,
54
+ 36070,
55
+ 36071,
56
+ 36072,
57
+ 36073,
58
+ 36074,
59
+ 36075,
60
+ 36076,
61
+ 36077,
62
+ 36078,
63
+ 36079
64
+ ];
65
+ }
66
+ _getColorAttachments() {
67
+ return WebGL2RenderingContext._WGL2_ALL_COLOR_ATTACHMENTS;
68
+ }
69
+ /**
70
+ * WebGL2 extends the base-class framebuffer completeness pre-check to
71
+ * accept WebGL2-specific formats that the WebGL1 whitelist rejects.
72
+ *
73
+ * NOTE: This is called by _updateFramebufferAttachments BEFORE the native
74
+ * GL attachments are set, so we must NOT query glCheckFramebufferStatus
75
+ * here (it would see an empty FBO and always return INCOMPLETE).
76
+ * Instead we extend the JS-side format whitelist to cover WebGL2 formats.
77
+ */
78
+ _preCheckFramebufferStatus(framebuffer) {
79
+ const attachments = framebuffer._attachments;
80
+ let bestWidth = 0;
81
+ let bestHeight = 0;
82
+ const allEnums = [
83
+ this.COLOR_ATTACHMENT0,
84
+ this.DEPTH_ATTACHMENT,
85
+ this.STENCIL_ATTACHMENT,
86
+ this.DEPTH_STENCIL_ATTACHMENT,
87
+ ...WebGL2RenderingContext._WGL2_ALL_COLOR_ATTACHMENTS
88
+ ];
89
+ for (const enumVal of allEnums) {
90
+ const attach = attachments[enumVal];
91
+ if (!attach) continue;
92
+ if (attach instanceof WebGLTexture) {
93
+ const level = framebuffer._attachmentLevel[enumVal] ?? 0;
94
+ const w = attach._levelWidth[level] ?? 0;
95
+ const h = attach._levelHeight[level] ?? 0;
96
+ if (w > 0 && h > 0) {
97
+ bestWidth = w;
98
+ bestHeight = h;
99
+ break;
100
+ }
101
+ } else if (attach instanceof WebGLRenderbuffer) {
102
+ if (attach._width > 0 && attach._height > 0) {
103
+ bestWidth = attach._width;
104
+ bestHeight = attach._height;
105
+ break;
106
+ }
107
+ }
108
+ }
109
+ if (bestWidth > 0 && bestHeight > 0) {
110
+ framebuffer._width = bestWidth;
111
+ framebuffer._height = bestHeight;
112
+ return this.FRAMEBUFFER_COMPLETE;
113
+ }
114
+ return this.FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
115
+ }
116
+ /**
117
+ * WebGL2 completely replaces the base class framebuffer attachment flow.
118
+ *
119
+ * The base class flow is: (1) pre-check formats in JS → (2) set native
120
+ * attachments only if pre-check passes. This is wrong for WebGL2 because
121
+ * the JS pre-check uses WebGL1 format whitelists that reject valid WebGL2
122
+ * formats.
123
+ *
124
+ * WebGL2 flow: (1) always set native attachments first → (2) query native
125
+ * glCheckFramebufferStatus to determine completeness. This delegates all
126
+ * format validation to the native GL driver, matching browser behavior.
127
+ *
128
+ * Also handles COLOR_ATTACHMENT1–15 (WebGL2 MRT) that the base class
129
+ * doesn't know about.
130
+ */
131
+ /**
132
+ * Apply COLOR_ATTACHMENT1–15 to the native GL FBO (WebGL2 MRT).
133
+ * The base class handles CA0, DEPTH, STENCIL, DEPTH_STENCIL and calls
134
+ * _preCheckFramebufferStatus (which we override to query native GL).
135
+ */
136
+ _updateFramebufferAttachments(framebuffer) {
137
+ super._updateFramebufferAttachments(framebuffer);
138
+ if (!framebuffer) return;
139
+ for (let i = 1; i <= 15; i++) {
140
+ const attachmentEnum = 36064 + i;
141
+ if (!(attachmentEnum in framebuffer._attachments)) continue;
142
+ const attachment = framebuffer._attachments[attachmentEnum];
143
+ if (attachment instanceof WebGLTexture) {
144
+ const face = framebuffer._attachmentFace[attachmentEnum] || this.TEXTURE_2D;
145
+ const level = framebuffer._attachmentLevel[attachmentEnum] ?? 0;
146
+ this._gl.framebufferTexture2D(this.FRAMEBUFFER, attachmentEnum, face, attachment._ | 0, level | 0);
147
+ } else if (attachment instanceof WebGLRenderbuffer) {
148
+ this._gl.framebufferRenderbuffer(this.FRAMEBUFFER, attachmentEnum, this.RENDERBUFFER, attachment._ | 0);
149
+ } else {
150
+ this._gl.framebufferTexture2D(this.FRAMEBUFFER, attachmentEnum, this.TEXTURE_2D, 0, 0);
151
+ }
152
+ }
153
+ }
154
+ /** WebGL2 adds UNIFORM_BUFFER, TRANSFORM_FEEDBACK_BUFFER, etc. targets. */
155
+ bindBuffer(target, buffer) {
156
+ const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
157
+ if (isWebGL2Target) {
158
+ const id = buffer ? buffer._ | 0 : 0;
159
+ this._gl.bindBuffer(target, id);
160
+ return;
161
+ }
162
+ super.bindBuffer(target, buffer);
163
+ }
164
+ /**
165
+ * WebGL2 adds READ_FRAMEBUFFER (0x8CA8) and DRAW_FRAMEBUFFER (0x8CA9) targets.
166
+ * The base class only accepts FRAMEBUFFER; this override handles the two new targets
167
+ * and keeps _activeReadFramebuffer / _activeDrawFramebuffer in sync.
168
+ */
169
+ bindFramebuffer(target, framebuffer) {
170
+ if (target === 36008 || target === 36009) {
171
+ if (!checkObject(framebuffer)) {
172
+ throw new TypeError("bindFramebuffer(GLenum, WebGLFramebuffer)");
173
+ }
174
+ if (framebuffer && framebuffer._pendingDelete) return;
175
+ if (framebuffer && !this._checkWrapper(framebuffer, WebGLFramebuffer)) return;
176
+ const id = framebuffer ? framebuffer._ | 0 : this._gtkFboId;
177
+ this._gl.bindFramebuffer(target, id);
178
+ if (target === 36008) {
179
+ const prev = this._activeReadFramebuffer;
180
+ if (prev !== framebuffer) {
181
+ if (prev) {
182
+ prev._refCount -= 1;
183
+ prev._checkDelete();
184
+ }
185
+ if (framebuffer) framebuffer._refCount += 1;
186
+ }
187
+ this._activeReadFramebuffer = framebuffer;
188
+ } else {
189
+ const prev = this._activeDrawFramebuffer;
190
+ if (prev !== framebuffer) {
191
+ if (prev) {
192
+ prev._refCount -= 1;
193
+ prev._checkDelete();
194
+ }
195
+ if (framebuffer) framebuffer._refCount += 1;
196
+ }
197
+ this._activeDrawFramebuffer = framebuffer;
198
+ this._activeFramebuffer = framebuffer;
199
+ }
200
+ return;
201
+ }
202
+ super.bindFramebuffer(this.FRAMEBUFFER, framebuffer);
203
+ this._activeReadFramebuffer = framebuffer;
204
+ this._activeDrawFramebuffer = framebuffer;
205
+ }
206
+ /** WebGL2 also unbinds from read/draw framebuffer slots when deleting. */
207
+ deleteFramebuffer(framebuffer) {
208
+ if (this._activeReadFramebuffer === framebuffer) {
209
+ this.bindFramebuffer(36008, null);
210
+ }
211
+ if (this._activeDrawFramebuffer === framebuffer) {
212
+ this.bindFramebuffer(36009, null);
213
+ }
214
+ super.deleteFramebuffer(framebuffer);
215
+ }
216
+ /** WebGL2 adds READ/COPY buffer usages and additional buffer targets. */
217
+ bufferData(target, dataOrSize, usage) {
218
+ const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
219
+ const isReadOrCopy = usage === 35041 || usage === 35043 || usage === 35045 || usage === 35042 || usage === 35044 || usage === 35046;
220
+ const remappedUsage = isReadOrCopy ? this.STATIC_DRAW : usage;
221
+ if (isWebGL2Target) {
222
+ if (typeof dataOrSize === "number") {
223
+ if (dataOrSize >= 0) this._gl.bufferDataSizeOnly(target, dataOrSize, remappedUsage);
224
+ } else if (dataOrSize !== null && typeof dataOrSize === "object") {
225
+ const u8Data = arrayToUint8Array(dataOrSize);
226
+ this._gl.bufferData(target, Uint8ArrayToVariant(u8Data), remappedUsage);
227
+ }
228
+ return;
229
+ }
230
+ super.bufferData(target, dataOrSize, remappedUsage);
231
+ }
232
+ /** WebGL2 adds UNIFORM_BUFFER, TRANSFORM_FEEDBACK_BUFFER, COPY_READ/WRITE targets. */
233
+ bufferSubData(target, offset, data) {
234
+ const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
235
+ if (isWebGL2Target) {
236
+ if (offset < 0) {
237
+ this.setError(this.INVALID_VALUE);
238
+ return;
239
+ }
240
+ if (!data) {
241
+ this.setError(this.INVALID_VALUE);
242
+ return;
243
+ }
244
+ const u8Data = arrayToUint8Array(data);
245
+ this._gl.bufferSubData(target, offset, Uint8ArrayToVariant(u8Data));
246
+ return;
247
+ }
248
+ super.bufferSubData(target, offset, data);
249
+ }
250
+ /** WebGL2 adds TEXTURE_3D and TEXTURE_2D_ARRAY target support. */
251
+ bindTexture(target, texture) {
252
+ if (target === 32879 || target === 35866) {
253
+ const id = texture ? texture._ | 0 : 0;
254
+ this._gl.bindTexture(target, id);
255
+ if (texture) texture._binding = target;
256
+ return;
257
+ }
258
+ super.bindTexture(target, texture);
259
+ }
260
+ /** WebGL2 adds TEXTURE_3D/TEXTURE_2D_ARRAY targets and many new pnames. */
261
+ texParameteri(target, pname, param) {
262
+ if (target === 32879 || target === 35866) {
263
+ this._gl.texParameteri(target, pname, param);
264
+ return;
265
+ }
266
+ const isWebGL2Pname = pname === 32882 || pname === 34892 || pname === 34893 || pname === 33084 || pname === 33085 || pname === 33083 || pname === 33082;
267
+ if (isWebGL2Pname) {
268
+ this._gl.texParameteri(target, pname, param);
269
+ return;
270
+ }
271
+ super.texParameteri(target, pname, param);
272
+ }
273
+ /**
274
+ * In WebGL2/GLES3 the attribute-0 requirement from WebGL1 does not apply.
275
+ * Override drawArrays to skip the attrib0 hack and call glDrawArrays directly.
276
+ */
277
+ /**
278
+ * In WebGL2/GLES3 the attribute-0 requirement from WebGL1 does not apply.
279
+ * Override drawArrays to skip the attrib0 hack and call glDrawArrays directly.
280
+ */
281
+ drawArrays(mode, first, count) {
282
+ if (first < 0 || count < 0) {
283
+ this.setError(this.INVALID_VALUE);
284
+ return;
285
+ }
286
+ if (!this._checkStencilState()) return;
287
+ const rc = vertexCount(this, mode, count);
288
+ if (rc < 0) {
289
+ this.setError(this.INVALID_ENUM);
290
+ return;
291
+ }
292
+ if (!this._framebufferOk()) return;
293
+ if (count === 0) return;
294
+ if (!this._checkVertexAttribState(count + first - 1 >>> 0)) return;
295
+ this._native2.drawArrays(mode, first, rc);
296
+ }
297
+ /**
298
+ * In WebGL2, UNSIGNED_INT element indices are a core feature — no extension needed.
299
+ * Override drawElements to skip the oes_element_index_uint extension check.
300
+ */
301
+ drawElements(mode = 0, count = 0, type = 0, offset = 0) {
302
+ if (count < 0 || offset < 0) {
303
+ this.setError(this.INVALID_VALUE);
304
+ return;
305
+ }
306
+ if (!this._checkStencilState()) return;
307
+ const elementBuffer = this._vertexObjectState._elementArrayBufferBinding;
308
+ if (!elementBuffer) {
309
+ this.setError(this.INVALID_OPERATION);
310
+ return;
311
+ }
312
+ let elementData = null;
313
+ let adjustedOffset = offset;
314
+ if (type === this.UNSIGNED_SHORT) {
315
+ if (adjustedOffset % 2) {
316
+ this.setError(this.INVALID_OPERATION);
317
+ return;
318
+ }
319
+ adjustedOffset >>= 1;
320
+ elementData = new Uint16Array(elementBuffer._elements.buffer);
321
+ } else if (type === this.UNSIGNED_INT) {
322
+ if (adjustedOffset % 4) {
323
+ this.setError(this.INVALID_OPERATION);
324
+ return;
325
+ }
326
+ adjustedOffset >>= 2;
327
+ elementData = new Uint32Array(elementBuffer._elements.buffer);
328
+ } else if (type === this.UNSIGNED_BYTE) {
329
+ elementData = elementBuffer._elements;
330
+ } else {
331
+ this.setError(this.INVALID_ENUM);
332
+ return;
333
+ }
334
+ let reducedCount = count;
335
+ switch (mode) {
336
+ case this.TRIANGLES:
337
+ if (count % 3) reducedCount -= count % 3;
338
+ break;
339
+ case this.LINES:
340
+ if (count % 2) reducedCount -= count % 2;
341
+ break;
342
+ case this.POINTS: break;
343
+ case this.LINE_LOOP:
344
+ case this.LINE_STRIP:
345
+ if (count < 2) {
346
+ this.setError(this.INVALID_OPERATION);
347
+ return;
348
+ }
349
+ break;
350
+ case this.TRIANGLE_FAN:
351
+ case this.TRIANGLE_STRIP:
352
+ if (count < 3) {
353
+ this.setError(this.INVALID_OPERATION);
354
+ return;
355
+ }
356
+ break;
357
+ default:
358
+ this.setError(this.INVALID_ENUM);
359
+ return;
360
+ }
361
+ if (!this._framebufferOk()) return;
362
+ if (count === 0) return;
363
+ let maxIndex = 0;
364
+ for (let i = adjustedOffset; i < adjustedOffset + reducedCount; ++i) {
365
+ if (i < elementData.length && elementData[i] > maxIndex) maxIndex = elementData[i];
366
+ }
367
+ if (this._checkVertexAttribState(maxIndex)) {
368
+ this._native2.drawElements(mode, reducedCount, type, offset);
369
+ }
370
+ }
371
+ createVertexArray() {
372
+ const id = this._native2.createVertexArray();
373
+ if (!id) return null;
374
+ const vao = new WebGLVertexArrayObject(id, this);
375
+ this._vertexArrayObjects[id] = vao;
376
+ return vao;
377
+ }
378
+ deleteVertexArray(vertexArray) {
379
+ if (!vertexArray || !(vertexArray instanceof WebGLVertexArrayObject)) return;
380
+ vertexArray._pendingDelete = true;
381
+ vertexArray._checkDelete();
382
+ }
383
+ isVertexArray(vertexArray) {
384
+ if (!vertexArray || !(vertexArray instanceof WebGLVertexArrayObject)) return false;
385
+ return this._native2.isVertexArray(vertexArray._);
386
+ }
387
+ bindVertexArray(array) {
388
+ if (array === null) {
389
+ this._native2.bindVertexArray(0);
390
+ this._vertexObjectState = this._defaultVertexObjectState;
391
+ } else if (array instanceof WebGLVertexArrayObject) {
392
+ this._native2.bindVertexArray(array._);
393
+ this._vertexObjectState = array._objectState;
394
+ } else {
395
+ this.setError(this.INVALID_OPERATION);
396
+ }
397
+ }
398
+ createQuery() {
399
+ const id = this._native2.createQuery();
400
+ if (!id) return null;
401
+ const query = new WebGLQuery(id, this);
402
+ this._queries[id] = query;
403
+ return query;
404
+ }
405
+ deleteQuery(query) {
406
+ if (!query || !(query instanceof WebGLQuery)) return;
407
+ query._pendingDelete = true;
408
+ query._checkDelete();
409
+ }
410
+ isQuery(query) {
411
+ if (!query || !(query instanceof WebGLQuery)) return false;
412
+ return this._native2.isQuery(query._);
413
+ }
414
+ beginQuery(target, query) {
415
+ if (!(query instanceof WebGLQuery)) return;
416
+ this._native2.beginQuery(target, query._);
417
+ }
418
+ endQuery(target) {
419
+ this._native2.endQuery(target);
420
+ }
421
+ getQuery(_target, _pname) {
422
+ warnNotImplemented("WebGL2RenderingContext.getQuery");
423
+ return null;
424
+ }
425
+ getQueryParameter(query, pname) {
426
+ if (!(query instanceof WebGLQuery)) return null;
427
+ return this._native2.getQueryParameter(query._, pname);
428
+ }
429
+ createSampler() {
430
+ const id = this._native2.createSampler();
431
+ if (!id) return null;
432
+ const sampler = new WebGLSampler(id, this);
433
+ this._samplers[id] = sampler;
434
+ return sampler;
435
+ }
436
+ deleteSampler(sampler) {
437
+ if (!sampler || !(sampler instanceof WebGLSampler)) return;
438
+ sampler._pendingDelete = true;
439
+ sampler._checkDelete();
440
+ }
441
+ isSampler(sampler) {
442
+ if (!sampler || !(sampler instanceof WebGLSampler)) return false;
443
+ return this._native2.isSampler(sampler._);
444
+ }
445
+ bindSampler(unit, sampler) {
446
+ this._native2.bindSampler(unit, sampler ? sampler._ : 0);
447
+ }
448
+ samplerParameteri(sampler, pname, param) {
449
+ if (!(sampler instanceof WebGLSampler)) return;
450
+ this._native2.samplerParameteri(sampler._, pname, param);
451
+ }
452
+ samplerParameterf(sampler, pname, param) {
453
+ if (!(sampler instanceof WebGLSampler)) return;
454
+ this._native2.samplerParameterf(sampler._, pname, param);
455
+ }
456
+ getSamplerParameter(sampler, pname) {
457
+ if (!(sampler instanceof WebGLSampler)) return null;
458
+ if (pname === 33082 || pname === 33083) {
459
+ return this._native2.getSamplerParameterf(sampler._, pname);
460
+ }
461
+ return this._native2.getSamplerParameteri(sampler._, pname);
462
+ }
463
+ fenceSync(condition, flags) {
464
+ const id = this._native2.fenceSync(condition, flags);
465
+ if (!id) return null;
466
+ const sync = new WebGLSync(id, this);
467
+ this._syncs[id] = sync;
468
+ return sync;
469
+ }
470
+ isSync(sync) {
471
+ if (!sync || !(sync instanceof WebGLSync)) return false;
472
+ return this._native2.isSync(sync._);
473
+ }
474
+ deleteSync(sync) {
475
+ if (!sync || !(sync instanceof WebGLSync)) return;
476
+ sync._pendingDelete = true;
477
+ sync._checkDelete();
478
+ }
479
+ clientWaitSync(sync, flags, timeout) {
480
+ if (!(sync instanceof WebGLSync)) return 37148;
481
+ return this._native2.clientWaitSync(sync._, flags, timeout);
482
+ }
483
+ waitSync(sync, flags, timeout) {
484
+ if (!(sync instanceof WebGLSync)) return;
485
+ this._native2.waitSync(sync._, flags, timeout);
486
+ }
487
+ getSyncParameter(sync, pname) {
488
+ if (!(sync instanceof WebGLSync)) return null;
489
+ return this._native2.getSyncParameter(sync._, pname);
490
+ }
491
+ createTransformFeedback() {
492
+ const id = this._native2.createTransformFeedback();
493
+ if (!id) return null;
494
+ const tf = new WebGLTransformFeedback(id, this);
495
+ this._transformFeedbacks[id] = tf;
496
+ return tf;
497
+ }
498
+ deleteTransformFeedback(tf) {
499
+ if (!tf || !(tf instanceof WebGLTransformFeedback)) return;
500
+ tf._pendingDelete = true;
501
+ tf._checkDelete();
502
+ }
503
+ isTransformFeedback(tf) {
504
+ if (!tf || !(tf instanceof WebGLTransformFeedback)) return false;
505
+ return this._native2.isTransformFeedback(tf._);
506
+ }
507
+ bindTransformFeedback(target, tf) {
508
+ this._native2.bindTransformFeedback(target, tf ? tf._ : 0);
509
+ }
510
+ beginTransformFeedback(primitiveMode) {
511
+ this._native2.beginTransformFeedback(primitiveMode);
512
+ }
513
+ endTransformFeedback() {
514
+ this._native2.endTransformFeedback();
515
+ }
516
+ pauseTransformFeedback() {
517
+ this._native2.pauseTransformFeedback();
518
+ }
519
+ resumeTransformFeedback() {
520
+ this._native2.resumeTransformFeedback();
521
+ }
522
+ transformFeedbackVaryings(program, varyings, bufferMode) {
523
+ this._native2.transformFeedbackVaryings(program._, varyings, bufferMode);
524
+ }
525
+ getTransformFeedbackVarying(program, index) {
526
+ const result = this._native2.getTransformFeedbackVarying(program._, index).deepUnpack();
527
+ return new WebGLActiveInfo({
528
+ size: result.size,
529
+ type: result.type,
530
+ name: result.name
531
+ });
532
+ }
533
+ bindBufferBase(target, index, buffer) {
534
+ this._native2.bindBufferBase(target, index, buffer ? buffer._ : 0);
535
+ }
536
+ bindBufferRange(target, index, buffer, offset, size) {
537
+ this._native2.bindBufferRange(target, index, buffer ? buffer._ : 0, offset, size);
538
+ }
539
+ copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size) {
540
+ this._native2.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
541
+ }
542
+ getBufferSubData(target, srcByteOffset, dstBuffer, dstOffset, length) {
543
+ const byteLength = length !== undefined ? length : dstBuffer.byteLength - (dstOffset ?? 0);
544
+ const data = this._native2.getBufferSubData(target, srcByteOffset, byteLength);
545
+ const dst = new Uint8Array(dstBuffer.buffer, dstBuffer.byteOffset + (dstOffset ?? 0) * (dstBuffer instanceof Uint8Array ? 1 : dstBuffer.BYTES_PER_ELEMENT ?? 1));
546
+ dst.set(data.subarray(0, dst.byteLength));
547
+ }
548
+ texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels) {
549
+ if (pixels === null) {
550
+ this._native2.texImage3DNull(target, level, internalformat, width, height, depth, border, format, type);
551
+ } else {
552
+ this._native2.texImage3D(target, level, internalformat, width, height, depth, border, format, type, Uint8ArrayToVariant(new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength)));
553
+ }
554
+ }
555
+ texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) {
556
+ if (pixels === null) return;
557
+ this._native2.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, Uint8ArrayToVariant(new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength)));
558
+ }
559
+ compressedTexImage3D(target, level, internalformat, width, height, depth, border, _imageSize, data) {
560
+ this._native2.compressedTexImage3D(target, level, internalformat, width, height, depth, border, Uint8ArrayToVariant(new Uint8Array(data.buffer, data.byteOffset, data.byteLength)));
561
+ }
562
+ compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, _imageSize, data) {
563
+ this._native2.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, Uint8ArrayToVariant(new Uint8Array(data.buffer, data.byteOffset, data.byteLength)));
564
+ }
565
+ copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height) {
566
+ this._native2.copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);
567
+ }
568
+ texStorage2D(target, levels, internalformat, width, height) {
569
+ this._native2.texStorage2D(target, levels, internalformat, width, height);
570
+ const texture = this._getTexImage(target);
571
+ if (texture) {
572
+ for (let lvl = 0; lvl < levels; lvl++) {
573
+ texture._levelWidth[lvl] = Math.max(1, width >> lvl);
574
+ texture._levelHeight[lvl] = Math.max(1, height >> lvl);
575
+ }
576
+ texture._format = this.RGBA;
577
+ texture._type = this.UNSIGNED_BYTE;
578
+ }
579
+ }
580
+ texStorage3D(target, levels, internalformat, width, height, depth) {
581
+ this._native2.texStorage3D(target, levels, internalformat, width, height, depth);
582
+ }
583
+ texImage2D(target = 0, level = 0, internalFormat = 0, formatOrWidth = 0, typeOrHeight = 0, sourceOrBorder = 0, _format = 0, type = 0, pixels) {
584
+ let width = 0;
585
+ let height = 0;
586
+ let format = 0;
587
+ let border = 0;
588
+ if (arguments.length === 6) {
589
+ type = typeOrHeight;
590
+ format = formatOrWidth;
591
+ if (sourceOrBorder instanceof GdkPixbuf.Pixbuf) {
592
+ const pixbuf = sourceOrBorder;
593
+ width = pixbuf.get_width();
594
+ height = pixbuf.get_height();
595
+ pixels = pixbuf.get_pixels();
596
+ } else {
597
+ const imageData = extractImageData(sourceOrBorder);
598
+ if (imageData == null) {
599
+ throw new TypeError("texImage2D(GLenum, GLint, GLenum, GLint, GLenum, GLenum, ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement)");
600
+ }
601
+ width = imageData.width;
602
+ height = imageData.height;
603
+ pixels = imageData.data;
604
+ }
605
+ } else if (arguments.length >= 9) {
606
+ width = formatOrWidth;
607
+ height = typeOrHeight;
608
+ border = sourceOrBorder;
609
+ format = _format;
610
+ }
611
+ const texture = this._getTexImage(target);
612
+ if (!texture) {
613
+ this.setError(this.INVALID_OPERATION);
614
+ return;
615
+ }
616
+ let data = convertPixels(pixels);
617
+ if (this._unpackPremultAlpha && data && format === this.RGBA) {
618
+ data = premultiplyAlpha(data);
619
+ }
620
+ if (this._unpackFlipY && data && width > 0 && height > 0) {
621
+ const pixelSize = this._computePixelSize(type, format);
622
+ if (pixelSize > 0) {
623
+ const rowStride = this._computeRowStride(width, pixelSize);
624
+ const flipped = new Uint8Array(data.length);
625
+ for (let row = 0; row < height; row++) {
626
+ const srcOffset = row * rowStride;
627
+ const dstOffset = (height - 1 - row) * rowStride;
628
+ flipped.set(data.subarray(srcOffset, srcOffset + rowStride), dstOffset);
629
+ }
630
+ data = flipped;
631
+ }
632
+ }
633
+ this._saveError();
634
+ this._gl.texImage2D(target, level, internalFormat, width, height, border, format, type, Uint8ArrayToVariant(data));
635
+ const error = this.getError();
636
+ this._restoreError(error);
637
+ if (error !== this.NO_ERROR) return;
638
+ texture._levelWidth[level] = width;
639
+ texture._levelHeight[level] = height;
640
+ texture._format = format;
641
+ texture._type = type;
642
+ const activeFramebuffer = this._activeFramebuffer;
643
+ if (activeFramebuffer) {
644
+ let needsUpdate = false;
645
+ const attachments = this._getAttachments();
646
+ for (let i = 0; i < attachments.length; ++i) {
647
+ if (activeFramebuffer._attachments[attachments[i]] === texture) {
648
+ needsUpdate = true;
649
+ break;
650
+ }
651
+ }
652
+ if (needsUpdate && this._activeFramebuffer) {
653
+ this._updateFramebufferAttachments(this._activeFramebuffer);
654
+ }
655
+ }
656
+ }
657
+ texSubImage2D(target = 0, level = 0, xoffset = 0, yoffset = 0, formatOrWidth = 0, typeOrHeight = 0, sourceOrFormat = 0, type = 0, pixels) {
658
+ let width = 0;
659
+ let height = 0;
660
+ let format = 0;
661
+ if (arguments.length === 7) {
662
+ type = typeOrHeight;
663
+ format = formatOrWidth;
664
+ if (sourceOrFormat instanceof GdkPixbuf.Pixbuf) {
665
+ const pixbuf = sourceOrFormat;
666
+ width = pixbuf.get_width();
667
+ height = pixbuf.get_height();
668
+ pixels = pixbuf.get_pixels();
669
+ } else {
670
+ const imageData = extractImageData(sourceOrFormat);
671
+ if (imageData == null) {
672
+ throw new TypeError("texSubImage2D(GLenum, GLint, GLint, GLint, GLenum, GLenum, ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement)");
673
+ }
674
+ width = imageData.width;
675
+ height = imageData.height;
676
+ pixels = imageData.data;
677
+ }
678
+ } else {
679
+ width = formatOrWidth;
680
+ height = typeOrHeight;
681
+ format = sourceOrFormat;
682
+ }
683
+ const texture = this._getTexImage(target);
684
+ if (!texture) {
685
+ this.setError(this.INVALID_OPERATION);
686
+ return;
687
+ }
688
+ let data = convertPixels(pixels);
689
+ if (!data) {
690
+ this.setError(this.INVALID_OPERATION);
691
+ return;
692
+ }
693
+ if (this._unpackPremultAlpha && data && format === this.RGBA) {
694
+ data = premultiplyAlpha(data);
695
+ }
696
+ if (this._unpackFlipY && data && width > 0 && height > 0) {
697
+ const pixelSize = this._computePixelSize(type, format);
698
+ if (pixelSize > 0) {
699
+ const rowStride = this._computeRowStride(width, pixelSize);
700
+ const flipped = new Uint8Array(data.length);
701
+ for (let row = 0; row < height; row++) {
702
+ const srcOffset = row * rowStride;
703
+ const dstOffset = (height - 1 - row) * rowStride;
704
+ flipped.set(data.subarray(srcOffset, srcOffset + rowStride), dstOffset);
705
+ }
706
+ data = flipped;
707
+ }
708
+ }
709
+ this._gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, Uint8ArrayToVariant(data));
710
+ }
711
+ framebufferTextureLayer(target, attachment, texture, level, layer) {
712
+ this._native2.framebufferTextureLayer(target, attachment, texture ? texture._ : 0, level, layer);
713
+ }
714
+ drawArraysInstanced(mode, first, count, instanceCount) {
715
+ if (first < 0 || count < 0 || instanceCount < 0) {
716
+ this.setError(this.INVALID_VALUE);
717
+ return;
718
+ }
719
+ if (!this._checkStencilState()) return;
720
+ const rc = vertexCount(this, mode, count);
721
+ if (rc < 0) {
722
+ this.setError(this.INVALID_ENUM);
723
+ return;
724
+ }
725
+ if (!this._framebufferOk()) return;
726
+ if (count === 0 || instanceCount === 0) return;
727
+ if (!this._checkVertexAttribState(count + first - 1 >>> 0)) return;
728
+ if (globalThis.__GJSIFY_DEBUG_GL) {
729
+ const n = this.__drawInstCount = (this.__drawInstCount | 0) + 1;
730
+ if (n <= 5 || n % 100 === 0) console.log(`[WebGL] drawArraysInstanced #${n} count=${rc} instances=${instanceCount} fbo=${this._activeFramebuffer?._ ?? "_gtkFbo"}`);
731
+ }
732
+ this._native2.drawArraysInstanced(mode, first, rc, instanceCount);
733
+ }
734
+ drawElementsInstanced(mode, count, type, offset, instanceCount) {
735
+ if (count < 0 || offset < 0 || instanceCount < 0) {
736
+ this.setError(this.INVALID_VALUE);
737
+ return;
738
+ }
739
+ if (!this._checkStencilState()) return;
740
+ const elementBuffer = this._vertexObjectState._elementArrayBufferBinding;
741
+ if (!elementBuffer) {
742
+ this.setError(this.INVALID_OPERATION);
743
+ return;
744
+ }
745
+ let elementData = null;
746
+ let adjustedOffset = offset;
747
+ if (type === this.UNSIGNED_SHORT) {
748
+ if (adjustedOffset % 2) {
749
+ this.setError(this.INVALID_OPERATION);
750
+ return;
751
+ }
752
+ adjustedOffset >>= 1;
753
+ elementData = new Uint16Array(elementBuffer._elements.buffer);
754
+ } else if (type === this.UNSIGNED_INT) {
755
+ if (adjustedOffset % 4) {
756
+ this.setError(this.INVALID_OPERATION);
757
+ return;
758
+ }
759
+ adjustedOffset >>= 2;
760
+ elementData = new Uint32Array(elementBuffer._elements.buffer);
761
+ } else if (type === this.UNSIGNED_BYTE) {
762
+ elementData = elementBuffer._elements;
763
+ } else {
764
+ this.setError(this.INVALID_ENUM);
765
+ return;
766
+ }
767
+ let reducedCount = count;
768
+ switch (mode) {
769
+ case this.TRIANGLES:
770
+ if (count % 3) reducedCount -= count % 3;
771
+ break;
772
+ case this.LINES:
773
+ if (count % 2) reducedCount -= count % 2;
774
+ break;
775
+ case this.POINTS: break;
776
+ case this.LINE_LOOP:
777
+ case this.LINE_STRIP:
778
+ if (count < 2) {
779
+ this.setError(this.INVALID_OPERATION);
780
+ return;
781
+ }
782
+ break;
783
+ case this.TRIANGLE_FAN:
784
+ case this.TRIANGLE_STRIP:
785
+ if (count < 3) {
786
+ this.setError(this.INVALID_OPERATION);
787
+ return;
788
+ }
789
+ break;
790
+ default:
791
+ this.setError(this.INVALID_ENUM);
792
+ return;
793
+ }
794
+ if (!this._framebufferOk()) return;
795
+ if (reducedCount === 0 || instanceCount === 0) {
796
+ this._checkVertexAttribState(0);
797
+ return;
798
+ }
799
+ if (reducedCount + adjustedOffset >>> 0 > elementData.length) {
800
+ this.setError(this.INVALID_OPERATION);
801
+ return;
802
+ }
803
+ let maxIndex = 0;
804
+ for (let i = adjustedOffset; i < adjustedOffset + reducedCount; ++i) {
805
+ if (elementData[i] > maxIndex) maxIndex = elementData[i];
806
+ }
807
+ if (this._checkVertexAttribState(maxIndex)) {
808
+ this._native2.drawElementsInstanced(mode, reducedCount, type, offset, instanceCount);
809
+ }
810
+ }
811
+ vertexAttribDivisor(index, divisor) {
812
+ this._native2.vertexAttribDivisor(index, divisor);
813
+ }
814
+ vertexAttribIPointer(index, size, type, stride, offset) {
815
+ this._native2.vertexAttribIPointer(index, size, type, stride, offset);
816
+ }
817
+ drawBuffers(buffers) {
818
+ let hasBack = false;
819
+ for (let i = 0; i < buffers.length; i++) {
820
+ if (buffers[i] === 1029) {
821
+ hasBack = true;
822
+ break;
823
+ }
824
+ }
825
+ if (!hasBack) {
826
+ this._native2.drawBuffers(buffers);
827
+ return;
828
+ }
829
+ this._native2.drawBuffers(buffers.map((b) => b === 1029 ? this.COLOR_ATTACHMENT0 : b));
830
+ }
831
+ drawRangeElements(mode, start, end, count, type, offset) {
832
+ if (count < 0 || offset < 0) {
833
+ this.setError(this.INVALID_VALUE);
834
+ return;
835
+ }
836
+ if (end < start) {
837
+ this.setError(this.INVALID_VALUE);
838
+ return;
839
+ }
840
+ this.drawElements(mode, count, type, offset);
841
+ }
842
+ blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) {
843
+ if (globalThis.__GJSIFY_DEBUG_GL) {
844
+ const errBefore = this._gl.getError();
845
+ if (errBefore !== 0) console.log(`[WebGL] blitFramebuffer PRE-ERROR 0x${errBefore.toString(16)}`);
846
+ }
847
+ this._native2.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
848
+ if (globalThis.__GJSIFY_DEBUG_GL) {
849
+ const err = this._gl.getError();
850
+ const n = this.__blitCount = (this.__blitCount | 0) + 1;
851
+ if (n <= 5) console.log(`[WebGL] blitFramebuffer #${n} src=(${srcX0},${srcY0},${srcX1},${srcY1}) readFbo=${this._activeReadFramebuffer?._ ?? "_gtkFbo"} err=${err === 0 ? "OK" : "0x" + err.toString(16)}`);
852
+ }
853
+ }
854
+ clearBufferfv(buffer, drawbuffer, values, _srcOffset) {
855
+ const n2 = this._native2;
856
+ if (typeof n2.clearBufferfv === "function") {
857
+ n2.clearBufferfv(buffer, drawbuffer, Array.from(values));
858
+ return;
859
+ }
860
+ const v = values;
861
+ if (buffer === 6144) {
862
+ const prev = this.getParameter(this.COLOR_CLEAR_VALUE);
863
+ this.clearColor(v[0] ?? 0, v[1] ?? 0, v[2] ?? 0, v[3] ?? 0);
864
+ this.clear(this.COLOR_BUFFER_BIT);
865
+ if (prev) this.clearColor(prev[0], prev[1], prev[2], prev[3]);
866
+ } else if (buffer === 6145) {
867
+ const prev = this.getParameter(this.DEPTH_CLEAR_VALUE);
868
+ this.clearDepth(v[0] ?? 1);
869
+ this.clear(this.DEPTH_BUFFER_BIT);
870
+ if (prev !== null) this.clearDepth(prev);
871
+ }
872
+ }
873
+ clearBufferiv(buffer, drawbuffer, values, _srcOffset) {
874
+ const n2 = this._native2;
875
+ if (typeof n2.clearBufferiv === "function") {
876
+ n2.clearBufferiv(buffer, drawbuffer, Array.from(values));
877
+ return;
878
+ }
879
+ if (buffer === 6146) {
880
+ const v = values;
881
+ const prev = this.getParameter(this.STENCIL_CLEAR_VALUE);
882
+ this.clearStencil(v[0] ?? 0);
883
+ this.clear(this.STENCIL_BUFFER_BIT);
884
+ if (prev !== null) this.clearStencil(prev);
885
+ }
886
+ }
887
+ clearBufferuiv(buffer, drawbuffer, values, _srcOffset) {
888
+ const n2 = this._native2;
889
+ if (typeof n2.clearBufferuiv === "function") {
890
+ n2.clearBufferuiv(buffer, drawbuffer, Array.from(values));
891
+ return;
892
+ }
893
+ void buffer;
894
+ void drawbuffer;
895
+ }
896
+ clearBufferfi(buffer, drawbuffer, depth, stencil) {
897
+ const n2 = this._native2;
898
+ if (typeof n2.clearBufferfi === "function") {
899
+ n2.clearBufferfi(buffer, drawbuffer, depth, stencil);
900
+ return;
901
+ }
902
+ if (buffer === 34041) {
903
+ const prevDepth = this.getParameter(this.DEPTH_CLEAR_VALUE);
904
+ const prevStencil = this.getParameter(this.STENCIL_CLEAR_VALUE);
905
+ this.clearDepth(depth);
906
+ this.clearStencil(stencil);
907
+ this.clear(this.DEPTH_BUFFER_BIT | this.STENCIL_BUFFER_BIT);
908
+ if (prevDepth !== null) this.clearDepth(prevDepth);
909
+ if (prevStencil !== null) this.clearStencil(prevStencil);
910
+ }
911
+ void drawbuffer;
912
+ }
913
+ invalidateFramebuffer(target, attachments) {
914
+ this._native2.invalidateFramebuffer(target, attachments);
915
+ }
916
+ invalidateSubFramebuffer(target, attachments, x, y, width, height) {
917
+ this._native2.invalidateSubFramebuffer(target, attachments, x, y, width, height);
918
+ }
919
+ readBuffer(src) {
920
+ this._native2.readBuffer(src);
921
+ }
922
+ renderbufferStorageMultisample(target, samples, internalFormat, width, height) {
923
+ if (target !== this.RENDERBUFFER) {
924
+ this.setError(this.INVALID_ENUM);
925
+ return;
926
+ }
927
+ const renderbuffer = this._activeRenderbuffer;
928
+ if (!renderbuffer) {
929
+ this.setError(this.INVALID_OPERATION);
930
+ return;
931
+ }
932
+ this._saveError();
933
+ this._native2.renderbufferStorageMultisample(target, samples, internalFormat, width, height);
934
+ const error = this.getError();
935
+ this._restoreError(error);
936
+ if (error !== this.NO_ERROR) return;
937
+ renderbuffer._width = width;
938
+ renderbuffer._height = height;
939
+ renderbuffer._format = internalFormat;
940
+ }
941
+ uniform1ui(location, v0) {
942
+ if (!location) return;
943
+ this._native2.uniform1ui(location._, v0);
944
+ }
945
+ uniform2ui(location, v0, v1) {
946
+ if (!location) return;
947
+ this._native2.uniform2ui(location._, v0, v1);
948
+ }
949
+ uniform3ui(location, v0, v1, v2) {
950
+ if (!location) return;
951
+ this._native2.uniform3ui(location._, v0, v1, v2);
952
+ }
953
+ uniform4ui(location, v0, v1, v2, v3) {
954
+ if (!location) return;
955
+ this._native2.uniform4ui(location._, v0, v1, v2, v3);
956
+ }
957
+ uniform1uiv(location, data, _srcOffset, _srcLength) {
958
+ if (!location) return;
959
+ const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
960
+ this._native2.uniform1uiv(location._, arr.length, arr);
961
+ }
962
+ uniform2uiv(location, data, _srcOffset, _srcLength) {
963
+ if (!location) return;
964
+ const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
965
+ this._native2.uniform2uiv(location._, arr.length / 2, arr);
966
+ }
967
+ uniform3uiv(location, data, _srcOffset, _srcLength) {
968
+ if (!location) return;
969
+ const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
970
+ this._native2.uniform3uiv(location._, arr.length / 3, arr);
971
+ }
972
+ uniform4uiv(location, data, _srcOffset, _srcLength) {
973
+ if (!location) return;
974
+ const arr = data instanceof Uint32Array ? data : new Uint32Array(data);
975
+ this._native2.uniform4uiv(location._, arr.length / 4, arr);
976
+ }
977
+ uniformMatrix2x3fv(location, transpose, data, _srcOffset, _srcLength) {
978
+ if (!location) return;
979
+ const arr = data instanceof Float32Array ? data : new Float32Array(data);
980
+ this._native2.uniformMatrix2x3fv(location._, transpose, arr);
981
+ }
982
+ uniformMatrix3x2fv(location, transpose, data, _srcOffset, _srcLength) {
983
+ if (!location) return;
984
+ const arr = data instanceof Float32Array ? data : new Float32Array(data);
985
+ this._native2.uniformMatrix3x2fv(location._, transpose, arr);
986
+ }
987
+ uniformMatrix2x4fv(location, transpose, data, _srcOffset, _srcLength) {
988
+ if (!location) return;
989
+ const arr = data instanceof Float32Array ? data : new Float32Array(data);
990
+ this._native2.uniformMatrix2x4fv(location._, transpose, arr);
991
+ }
992
+ uniformMatrix4x2fv(location, transpose, data, _srcOffset, _srcLength) {
993
+ if (!location) return;
994
+ const arr = data instanceof Float32Array ? data : new Float32Array(data);
995
+ this._native2.uniformMatrix4x2fv(location._, transpose, arr);
996
+ }
997
+ uniformMatrix3x4fv(location, transpose, data, _srcOffset, _srcLength) {
998
+ if (!location) return;
999
+ const arr = data instanceof Float32Array ? data : new Float32Array(data);
1000
+ this._native2.uniformMatrix3x4fv(location._, transpose, arr);
1001
+ }
1002
+ uniformMatrix4x3fv(location, transpose, data, _srcOffset, _srcLength) {
1003
+ if (!location) return;
1004
+ const arr = data instanceof Float32Array ? data : new Float32Array(data);
1005
+ this._native2.uniformMatrix4x3fv(location._, transpose, arr);
1006
+ }
1007
+ /** WebGL1 getUniform falls to default:null for UNSIGNED_INT types. Handle them here. */
1008
+ getUniform(program, location) {
1009
+ const type = location?._activeInfo?.type;
1010
+ const UINT = 5125, UVEC2 = 36294, UVEC3 = 36295, UVEC4 = 36296;
1011
+ const isUintType = type === UINT || type === UVEC2 || type === UVEC3 || type === UVEC4;
1012
+ if (!isUintType) return super.getUniform(program, location);
1013
+ if (!program || !location) return null;
1014
+ const data = this._gl.getUniformi(program._ | 0, location._ | 0);
1015
+ if (!data) return null;
1016
+ if (type === UINT) return data[0] >>> 0;
1017
+ if (type === UVEC2) return new Uint32Array([data[0] >>> 0, data[1] >>> 0]);
1018
+ if (type === UVEC3) return new Uint32Array([
1019
+ data[0] >>> 0,
1020
+ data[1] >>> 0,
1021
+ data[2] >>> 0
1022
+ ]);
1023
+ return new Uint32Array([
1024
+ data[0] >>> 0,
1025
+ data[1] >>> 0,
1026
+ data[2] >>> 0,
1027
+ data[3] >>> 0
1028
+ ]);
1029
+ }
1030
+ getUniformBlockIndex(program, uniformBlockName) {
1031
+ return this._native2.getUniformBlockIndex(program._, uniformBlockName);
1032
+ }
1033
+ uniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding) {
1034
+ this._native2.uniformBlockBinding(program._, uniformBlockIndex, uniformBlockBinding);
1035
+ }
1036
+ getActiveUniformBlockName(program, uniformBlockIndex) {
1037
+ const name = this._native2.getActiveUniformBlockName(program._, uniformBlockIndex);
1038
+ return name.length > 0 ? name : null;
1039
+ }
1040
+ getActiveUniformBlockParameter(program, uniformBlockIndex, pname) {
1041
+ return this._native2.getActiveUniformBlockParameter(program._, uniformBlockIndex, pname);
1042
+ }
1043
+ getActiveUniforms(program, uniformIndices, pname) {
1044
+ const result = this._native2.getActiveUniforms(program._, uniformIndices, pname);
1045
+ return result;
1046
+ }
1047
+ getFragDataLocation(program, name) {
1048
+ return this._native2.getFragDataLocation(program._, name);
1049
+ }
1050
+ getIndexedParameter(target, index) {
1051
+ return this._native2.getIndexedParameteri(target, index);
1052
+ }
1053
+ getInternalformatParameter(target, internalformat, pname) {
1054
+ return this._native2.getInternalformatParameter(target, internalformat, pname);
1055
+ }
1056
+ getParameter(pname) {
1057
+ if (pname === 7938) return "WebGL 2.0";
1058
+ if (pname === 35724) return "WebGL GLSL ES 3.00";
1059
+ if (pname === 7939) {
1060
+ warnNotImplemented("WebGL2RenderingContext.getParameter(GL_EXTENSIONS)");
1061
+ return "";
1062
+ }
1063
+ if (pname === 36006) return this._activeDrawFramebuffer;
1064
+ if (pname === 36010) return this._activeReadFramebuffer;
1065
+ switch (pname) {
1066
+ case 36183:
1067
+ case 35071:
1068
+ case 32883:
1069
+ case 36063:
1070
+ case 34852:
1071
+ case 36203:
1072
+ case 33001:
1073
+ case 33e3:
1074
+ case 37157:
1075
+ case 35373:
1076
+ case 35657:
1077
+ case 35077:
1078
+ case 35978:
1079
+ case 35979:
1080
+ case 35968:
1081
+ case 35376:
1082
+ case 35375:
1083
+ case 35659:
1084
+ case 37154:
1085
+ case 35371:
1086
+ case 35658:
1087
+ case 35379:
1088
+ case 35374:
1089
+ case 35377:
1090
+ case 35076:
1091
+ case 3330:
1092
+ case 3332:
1093
+ case 3331:
1094
+ case 35053:
1095
+ case 35055:
1096
+ case 3074:
1097
+ case 32874:
1098
+ case 35869:
1099
+ case 36389:
1100
+ case 35983:
1101
+ case 35368:
1102
+ case 35380:
1103
+ case 32878:
1104
+ case 3314:
1105
+ case 32877:
1106
+ case 3316:
1107
+ case 3315:
1108
+ case 34045: return this._native2.getParameterx(pname)?.deepUnpack() | 0;
1109
+ case 35977:
1110
+ case 36388:
1111
+ case 36387: return !!this._native2.getParameterx(pname)?.deepUnpack();
1112
+ }
1113
+ return super.getParameter(pname);
1114
+ }
1115
+ getStringi(name, index) {
1116
+ const s = this._native2.getStringi(name, index);
1117
+ return s.length > 0 ? s : null;
1118
+ }
1119
+ /**
1120
+ * WebGL2 supports ~30+ renderbuffer formats (R8, RG8, RGBA8, RGBA16F,
1121
+ * DEPTH_COMPONENT24, DEPTH32F_STENCIL8, etc.). The WebGL1 base class
1122
+ * only allows 7 formats. Delegate format validation to native GL.
1123
+ */
1124
+ renderbufferStorage(target, internalFormat, width, height) {
1125
+ if (target !== this.RENDERBUFFER) {
1126
+ this.setError(this.INVALID_ENUM);
1127
+ return;
1128
+ }
1129
+ const renderbuffer = this._activeRenderbuffer;
1130
+ if (!renderbuffer) {
1131
+ this.setError(this.INVALID_OPERATION);
1132
+ return;
1133
+ }
1134
+ if (width < 0 || height < 0) {
1135
+ this.setError(this.INVALID_VALUE);
1136
+ return;
1137
+ }
1138
+ while (this._gl.getError() !== this.NO_ERROR) {}
1139
+ this._gl.renderbufferStorage(target, internalFormat, width, height);
1140
+ if (this._gl.getError() !== this.NO_ERROR) return;
1141
+ renderbuffer._width = width;
1142
+ renderbuffer._height = height;
1143
+ renderbuffer._format = internalFormat;
1144
+ const activeFramebuffer = this._activeFramebuffer;
1145
+ if (activeFramebuffer) {
1146
+ const attachments = this._getAttachments();
1147
+ let needsUpdate = false;
1148
+ for (let i = 0; i < attachments.length; ++i) {
1149
+ if (activeFramebuffer._attachments[attachments[i]] === renderbuffer) {
1150
+ needsUpdate = true;
1151
+ break;
1152
+ }
1153
+ }
1154
+ if (needsUpdate) this._updateFramebufferAttachments(activeFramebuffer);
1155
+ }
1156
+ }
1157
+ /**
1158
+ * WebGL2 makes several WebGL1 extensions part of the core spec.
1159
+ * EXT_color_buffer_float and EXT_color_buffer_half_float are always
1160
+ * available in WebGL2 contexts. Append them if the base class didn't.
1161
+ */
1162
+ getSupportedExtensions() {
1163
+ const exts = super.getSupportedExtensions();
1164
+ const ensure = [
1165
+ "EXT_color_buffer_float",
1166
+ "EXT_color_buffer_half_float",
1167
+ "OES_texture_half_float"
1168
+ ];
1169
+ for (const ext of ensure) {
1170
+ if (exts.indexOf(ext) === -1) exts.push(ext);
1171
+ }
1172
+ return exts;
1173
+ }
1174
+ /**
1175
+ * WebGL2 allows reading pixels in many more format/type combinations
1176
+ * than WebGL1's strict RGBA/UNSIGNED_BYTE. Delegate validation to native.
1177
+ */
1178
+ readPixels(x, y, width, height, format, type, pixels) {
1179
+ if (!pixels) return;
1180
+ if (width < 0 || height < 0) {
1181
+ this.setError(this.INVALID_VALUE);
1182
+ return;
1183
+ }
1184
+ if (!this._framebufferOk()) return;
1185
+ const componentCount = format === 6408 || format === 32856 ? 4 : format === 6407 ? 3 : format === 33319 ? 2 : 1;
1186
+ const bytesPerComponent = type === 5126 ? 4 : type === 5131 || type === 36193 ? 2 : type === 5125 || type === 5124 ? 4 : type === 5123 || type === 5122 ? 2 : 1;
1187
+ const byteCount = width * height * componentCount * bytesPerComponent;
1188
+ const pixelData = new Uint8Array(byteCount);
1189
+ this._saveError();
1190
+ const result = this._gl.readPixels(x, y, width, height, format, type, Uint8ArrayToVariant(pixelData));
1191
+ const error = this.getError();
1192
+ this._restoreError(error);
1193
+ if (error !== this.NO_ERROR) return;
1194
+ const src = result && result.length > 0 ? result : pixelData;
1195
+ if (pixels instanceof Uint8Array) {
1196
+ pixels.set(src);
1197
+ } else if (pixels instanceof Float32Array) {
1198
+ const floatView = new Float32Array(src.buffer, 0, pixels.length);
1199
+ pixels.set(floatView);
1200
+ }
1201
+ }
1202
+ /**
1203
+ * WebGL2 never blocks draw calls on JS-side framebuffer format checks.
1204
+ * The native GL (Mesa/libepoxy) handles completeness and generates
1205
+ * INVALID_FRAMEBUFFER_OPERATION for truly incomplete FBOs at draw time.
1206
+ * This matches headless-gl's approach: _framebufferOk() always returns true.
1207
+ *
1208
+ * The base class rejects valid WebGL2 formats (RGBA16F/HALF_FLOAT, depth
1209
+ * textures, WebGL2 renderbuffer formats) causing silent rendering failures
1210
+ * for postprocessing effects and environment maps.
1211
+ */
1212
+ _framebufferOk() {
1213
+ return true;
1214
+ }
1281
1215
  };
1216
+
1217
+ //#endregion
1218
+ export { WebGL2RenderingContext };