@gjsify/webgl 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/canvas-webgl-widget.js +12 -0
- package/lib/esm/conformance/attribs.spec.js +296 -0
- package/lib/esm/conformance/buffers.spec.js +203 -0
- package/lib/esm/conformance/context.spec.js +302 -0
- package/lib/esm/conformance/programs.spec.js +468 -0
- package/lib/esm/conformance/rendering-basic.spec.js +136 -0
- package/lib/esm/conformance/rendering.spec.js +424 -0
- package/lib/esm/conformance/setup.js +36 -0
- package/lib/esm/conformance/state.spec.js +348 -0
- package/lib/esm/conformance/textures.spec.js +354 -0
- package/lib/esm/conformance/uniforms.spec.js +305 -0
- package/lib/esm/conformance-test.js +23 -0
- package/lib/esm/extensions/ext-color-buffer-float.js +13 -0
- package/lib/esm/extensions/ext-color-buffer-half-float.js +13 -0
- package/lib/esm/extensions/oes-texture-half-float.js +19 -0
- package/lib/esm/index.js +5 -0
- package/lib/esm/test-utils.js +65 -0
- package/lib/esm/test.js +2 -2
- package/lib/esm/webgl-buffer.js +1 -1
- package/lib/esm/webgl-context-base.js +3371 -0
- package/lib/esm/webgl-framebuffer.js +1 -1
- package/lib/esm/webgl-program.js +1 -1
- package/lib/esm/webgl-renderbuffer.js +1 -1
- package/lib/esm/webgl-rendering-context.js +95 -3253
- package/lib/esm/webgl-shader.js +2 -1
- package/lib/esm/webgl-texture.js +1 -1
- package/lib/esm/{index.spec.js → webgl1.spec.js} +2 -2
- package/lib/esm/webgl2-rendering-context.js +617 -27
- package/lib/esm/webgl2.spec.js +573 -1
- package/lib/types/conformance/setup.d.ts +14 -0
- package/lib/types/extensions/ext-blend-minmax.d.ts +2 -2
- package/lib/types/extensions/ext-color-buffer-float.d.ts +4 -0
- package/lib/types/extensions/ext-color-buffer-half-float.d.ts +4 -0
- package/lib/types/extensions/ext-texture-filter-anisotropic.d.ts +2 -2
- package/lib/types/extensions/oes-element-index-unit.d.ts +2 -2
- package/lib/types/extensions/oes-standard-derivatives.d.ts +2 -2
- package/lib/types/extensions/oes-texture-float-linear.d.ts +2 -2
- package/lib/types/extensions/oes-texture-float.d.ts +2 -2
- package/lib/types/extensions/oes-texture-half-float.d.ts +5 -0
- package/lib/types/extensions/stackgl-destroy-context.d.ts +3 -3
- package/lib/types/extensions/stackgl-resize-drawing-buffer.d.ts +3 -3
- package/lib/types/index.d.ts +1 -0
- package/lib/types/test-utils.d.ts +24 -0
- package/lib/types/types/extension.d.ts +2 -2
- package/lib/types/utils.d.ts +9 -10
- package/lib/types/webgl-buffer.d.ts +3 -3
- package/lib/types/webgl-context-base.d.ts +267 -0
- package/lib/types/webgl-framebuffer.d.ts +3 -3
- package/lib/types/webgl-program.d.ts +3 -3
- package/lib/types/webgl-renderbuffer.d.ts +3 -3
- package/lib/types/webgl-rendering-context.d.ts +5 -250
- package/lib/types/webgl-shader.d.ts +4 -3
- package/lib/types/webgl-texture-unit.d.ts +3 -3
- package/lib/types/webgl-texture.d.ts +3 -3
- package/lib/types/webgl-vertex-attribute.d.ts +5 -5
- package/lib/types/webgl2-rendering-context.d.ts +95 -6
- package/package.json +13 -11
- package/prebuilds/linux-x86_64/Gwebgl-0.1.typelib +0 -0
- package/prebuilds/linux-x86_64/libgwebgl.so +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Gwebgl from "@girs/gwebgl-0.1";
|
|
2
|
-
import
|
|
2
|
+
import GdkPixbuf from "gi://GdkPixbuf?version=2.0";
|
|
3
|
+
import { WebGLContextBase } from "./webgl-context-base.js";
|
|
3
4
|
import { WebGLQuery } from "./webgl-query.js";
|
|
4
5
|
import { WebGLSampler } from "./webgl-sampler.js";
|
|
5
6
|
import { WebGLSync } from "./webgl-sync.js";
|
|
@@ -8,9 +9,10 @@ import { WebGLVertexArrayObject } from "./webgl-vertex-array-object.js";
|
|
|
8
9
|
import { WebGLActiveInfo } from "./webgl-active-info.js";
|
|
9
10
|
import { WebGLTexture } from "./webgl-texture.js";
|
|
10
11
|
import { WebGLRenderbuffer } from "./webgl-renderbuffer.js";
|
|
11
|
-
import {
|
|
12
|
+
import { WebGLFramebuffer } from "./webgl-framebuffer.js";
|
|
13
|
+
import { Uint8ArrayToVariant, arrayToUint8Array, vertexCount, convertPixels, extractImageData, checkObject } from "./utils.js";
|
|
12
14
|
import { warnNotImplemented } from "@gjsify/utils";
|
|
13
|
-
class WebGL2RenderingContext extends
|
|
15
|
+
class WebGL2RenderingContext extends WebGLContextBase {
|
|
14
16
|
constructor(canvas, options = {}) {
|
|
15
17
|
super(canvas, options);
|
|
16
18
|
this._queries = {};
|
|
@@ -18,20 +20,118 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
18
20
|
this._transformFeedbacks = {};
|
|
19
21
|
this._vertexArrayObjects = {};
|
|
20
22
|
this._syncs = {};
|
|
23
|
+
this._activeReadFramebuffer = null;
|
|
24
|
+
this._activeDrawFramebuffer = null;
|
|
21
25
|
this._native2 = new Gwebgl.WebGL2RenderingContext({});
|
|
26
|
+
this._init();
|
|
27
|
+
}
|
|
28
|
+
get _gl() {
|
|
29
|
+
return this._native2;
|
|
22
30
|
}
|
|
23
31
|
_getGlslVersion(es) {
|
|
24
32
|
return es ? "300 es" : "130";
|
|
25
33
|
}
|
|
26
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
|
+
*/
|
|
27
40
|
/** WebGL2 allows COLOR_ATTACHMENT1–15 as framebuffer attachment points. */
|
|
28
41
|
_validFramebufferAttachment(attachment) {
|
|
29
42
|
if (super._validFramebufferAttachment(attachment)) return true;
|
|
30
43
|
return attachment >= 36065 && attachment <= 36079;
|
|
31
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
|
+
}
|
|
32
116
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
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).
|
|
35
135
|
*/
|
|
36
136
|
_updateFramebufferAttachments(framebuffer) {
|
|
37
137
|
super._updateFramebufferAttachments(framebuffer);
|
|
@@ -43,11 +143,11 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
43
143
|
if (attachment instanceof WebGLTexture) {
|
|
44
144
|
const face = framebuffer._attachmentFace[attachmentEnum] || this.TEXTURE_2D;
|
|
45
145
|
const level = framebuffer._attachmentLevel[attachmentEnum] ?? 0;
|
|
46
|
-
this.
|
|
146
|
+
this._gl.framebufferTexture2D(this.FRAMEBUFFER, attachmentEnum, face, attachment._ | 0, level | 0);
|
|
47
147
|
} else if (attachment instanceof WebGLRenderbuffer) {
|
|
48
|
-
this.
|
|
148
|
+
this._gl.framebufferRenderbuffer(this.FRAMEBUFFER, attachmentEnum, this.RENDERBUFFER, attachment._ | 0);
|
|
49
149
|
} else {
|
|
50
|
-
this.
|
|
150
|
+
this._gl.framebufferTexture2D(this.FRAMEBUFFER, attachmentEnum, this.TEXTURE_2D, 0, 0);
|
|
51
151
|
}
|
|
52
152
|
}
|
|
53
153
|
}
|
|
@@ -56,11 +156,63 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
56
156
|
const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
|
|
57
157
|
if (isWebGL2Target) {
|
|
58
158
|
const id = buffer ? buffer._ | 0 : 0;
|
|
59
|
-
this.
|
|
159
|
+
this._gl.bindBuffer(target, id);
|
|
60
160
|
return;
|
|
61
161
|
}
|
|
62
162
|
super.bindBuffer(target, buffer);
|
|
63
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
|
+
}
|
|
64
216
|
/** WebGL2 adds READ/COPY buffer usages and additional buffer targets. */
|
|
65
217
|
bufferData(target, dataOrSize, usage) {
|
|
66
218
|
const isWebGL2Target = target === 35345 || target === 35982 || target === 36662 || target === 36663;
|
|
@@ -68,37 +220,60 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
68
220
|
const remappedUsage = isReadOrCopy ? this.STATIC_DRAW : usage;
|
|
69
221
|
if (isWebGL2Target) {
|
|
70
222
|
if (typeof dataOrSize === "number") {
|
|
71
|
-
if (dataOrSize >= 0) this.
|
|
223
|
+
if (dataOrSize >= 0) this._gl.bufferDataSizeOnly(target, dataOrSize, remappedUsage);
|
|
72
224
|
} else if (dataOrSize !== null && typeof dataOrSize === "object") {
|
|
73
225
|
const u8Data = arrayToUint8Array(dataOrSize);
|
|
74
|
-
this.
|
|
226
|
+
this._gl.bufferData(target, Uint8ArrayToVariant(u8Data), remappedUsage);
|
|
75
227
|
}
|
|
76
228
|
return;
|
|
77
229
|
}
|
|
78
230
|
super.bufferData(target, dataOrSize, remappedUsage);
|
|
79
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
|
+
}
|
|
80
250
|
/** WebGL2 adds TEXTURE_3D and TEXTURE_2D_ARRAY target support. */
|
|
81
251
|
bindTexture(target, texture) {
|
|
82
252
|
if (target === 32879 || target === 35866) {
|
|
83
253
|
const id = texture ? texture._ | 0 : 0;
|
|
84
|
-
this.
|
|
254
|
+
this._gl.bindTexture(target, id);
|
|
85
255
|
if (texture) texture._binding = target;
|
|
86
256
|
return;
|
|
87
257
|
}
|
|
88
258
|
super.bindTexture(target, texture);
|
|
89
259
|
}
|
|
90
|
-
/** WebGL2 adds TEXTURE_3D/TEXTURE_2D_ARRAY targets and
|
|
260
|
+
/** WebGL2 adds TEXTURE_3D/TEXTURE_2D_ARRAY targets and many new pnames. */
|
|
91
261
|
texParameteri(target, pname, param) {
|
|
92
262
|
if (target === 32879 || target === 35866) {
|
|
93
|
-
this.
|
|
263
|
+
this._gl.texParameteri(target, pname, param);
|
|
94
264
|
return;
|
|
95
265
|
}
|
|
96
|
-
|
|
97
|
-
|
|
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);
|
|
98
269
|
return;
|
|
99
270
|
}
|
|
100
271
|
super.texParameteri(target, pname, param);
|
|
101
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
|
+
*/
|
|
102
277
|
/**
|
|
103
278
|
* In WebGL2/GLES3 the attribute-0 requirement from WebGL1 does not apply.
|
|
104
279
|
* Override drawArrays to skip the attrib0 hack and call glDrawArrays directly.
|
|
@@ -116,8 +291,82 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
116
291
|
}
|
|
117
292
|
if (!this._framebufferOk()) return;
|
|
118
293
|
if (count === 0) return;
|
|
119
|
-
if (this._checkVertexAttribState(count + first - 1 >>> 0))
|
|
120
|
-
|
|
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);
|
|
121
370
|
}
|
|
122
371
|
}
|
|
123
372
|
// ─── Vertex Array Objects ─────────────────────────────────────────────
|
|
@@ -322,19 +571,238 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
322
571
|
}
|
|
323
572
|
texStorage2D(target, levels, internalformat, width, height) {
|
|
324
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
|
+
}
|
|
325
583
|
}
|
|
326
584
|
texStorage3D(target, levels, internalformat, width, height, depth) {
|
|
327
585
|
this._native2.texStorage3D(target, levels, internalformat, width, height, depth);
|
|
328
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._unpackFlipY && data && width > 0 && height > 0) {
|
|
622
|
+
const pixelSize = this._computePixelSize(type, format);
|
|
623
|
+
if (pixelSize > 0) {
|
|
624
|
+
const rowStride = this._computeRowStride(width, pixelSize);
|
|
625
|
+
const flipped = new Uint8Array(data.length);
|
|
626
|
+
for (let row = 0; row < height; row++) {
|
|
627
|
+
const srcOffset = row * rowStride;
|
|
628
|
+
const dstOffset = (height - 1 - row) * rowStride;
|
|
629
|
+
flipped.set(data.subarray(srcOffset, srcOffset + rowStride), dstOffset);
|
|
630
|
+
}
|
|
631
|
+
data = flipped;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
this._saveError();
|
|
635
|
+
this._gl.texImage2D(target, level, internalFormat, width, height, border, format, type, Uint8ArrayToVariant(data));
|
|
636
|
+
const error = this.getError();
|
|
637
|
+
this._restoreError(error);
|
|
638
|
+
if (error !== this.NO_ERROR) return;
|
|
639
|
+
texture._levelWidth[level] = width;
|
|
640
|
+
texture._levelHeight[level] = height;
|
|
641
|
+
texture._format = format;
|
|
642
|
+
texture._type = type;
|
|
643
|
+
const activeFramebuffer = this._activeFramebuffer;
|
|
644
|
+
if (activeFramebuffer) {
|
|
645
|
+
let needsUpdate = false;
|
|
646
|
+
const attachments = this._getAttachments();
|
|
647
|
+
for (let i = 0; i < attachments.length; ++i) {
|
|
648
|
+
if (activeFramebuffer._attachments[attachments[i]] === texture) {
|
|
649
|
+
needsUpdate = true;
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if (needsUpdate && this._activeFramebuffer) {
|
|
654
|
+
this._updateFramebufferAttachments(this._activeFramebuffer);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
texSubImage2D(target = 0, level = 0, xoffset = 0, yoffset = 0, formatOrWidth = 0, typeOrHeight = 0, sourceOrFormat = 0, type = 0, pixels) {
|
|
659
|
+
let width = 0;
|
|
660
|
+
let height = 0;
|
|
661
|
+
let format = 0;
|
|
662
|
+
if (arguments.length === 7) {
|
|
663
|
+
type = typeOrHeight;
|
|
664
|
+
format = formatOrWidth;
|
|
665
|
+
if (sourceOrFormat instanceof GdkPixbuf.Pixbuf) {
|
|
666
|
+
const pixbuf = sourceOrFormat;
|
|
667
|
+
width = pixbuf.get_width();
|
|
668
|
+
height = pixbuf.get_height();
|
|
669
|
+
pixels = pixbuf.get_pixels();
|
|
670
|
+
} else {
|
|
671
|
+
const imageData = extractImageData(sourceOrFormat);
|
|
672
|
+
if (imageData == null) {
|
|
673
|
+
throw new TypeError("texSubImage2D(GLenum, GLint, GLint, GLint, GLenum, GLenum, ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement)");
|
|
674
|
+
}
|
|
675
|
+
width = imageData.width;
|
|
676
|
+
height = imageData.height;
|
|
677
|
+
pixels = imageData.data;
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
width = formatOrWidth;
|
|
681
|
+
height = typeOrHeight;
|
|
682
|
+
format = sourceOrFormat;
|
|
683
|
+
}
|
|
684
|
+
const texture = this._getTexImage(target);
|
|
685
|
+
if (!texture) {
|
|
686
|
+
this.setError(this.INVALID_OPERATION);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
let data = convertPixels(pixels);
|
|
690
|
+
if (!data) {
|
|
691
|
+
this.setError(this.INVALID_OPERATION);
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (this._unpackFlipY && data && width > 0 && height > 0) {
|
|
695
|
+
const pixelSize = this._computePixelSize(type, format);
|
|
696
|
+
if (pixelSize > 0) {
|
|
697
|
+
const rowStride = this._computeRowStride(width, pixelSize);
|
|
698
|
+
const flipped = new Uint8Array(data.length);
|
|
699
|
+
for (let row = 0; row < height; row++) {
|
|
700
|
+
const srcOffset = row * rowStride;
|
|
701
|
+
const dstOffset = (height - 1 - row) * rowStride;
|
|
702
|
+
flipped.set(data.subarray(srcOffset, srcOffset + rowStride), dstOffset);
|
|
703
|
+
}
|
|
704
|
+
data = flipped;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
this._gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, Uint8ArrayToVariant(data));
|
|
708
|
+
}
|
|
329
709
|
framebufferTextureLayer(target, attachment, texture, level, layer) {
|
|
330
710
|
this._native2.framebufferTextureLayer(target, attachment, texture ? texture._ : 0, level, layer);
|
|
331
711
|
}
|
|
332
712
|
// ─── Instancing & Advanced Draw ───────────────────────────────────────
|
|
333
713
|
drawArraysInstanced(mode, first, count, instanceCount) {
|
|
334
|
-
|
|
714
|
+
if (first < 0 || count < 0 || instanceCount < 0) {
|
|
715
|
+
this.setError(this.INVALID_VALUE);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (!this._checkStencilState()) return;
|
|
719
|
+
const rc = vertexCount(this, mode, count);
|
|
720
|
+
if (rc < 0) {
|
|
721
|
+
this.setError(this.INVALID_ENUM);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (!this._framebufferOk()) return;
|
|
725
|
+
if (count === 0 || instanceCount === 0) return;
|
|
726
|
+
if (!this._checkVertexAttribState(count + first - 1 >>> 0)) return;
|
|
727
|
+
this._native2.drawArraysInstanced(mode, first, rc, instanceCount);
|
|
335
728
|
}
|
|
336
729
|
drawElementsInstanced(mode, count, type, offset, instanceCount) {
|
|
337
|
-
|
|
730
|
+
if (count < 0 || offset < 0 || instanceCount < 0) {
|
|
731
|
+
this.setError(this.INVALID_VALUE);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (!this._checkStencilState()) return;
|
|
735
|
+
const elementBuffer = this._vertexObjectState._elementArrayBufferBinding;
|
|
736
|
+
if (!elementBuffer) {
|
|
737
|
+
this.setError(this.INVALID_OPERATION);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
let elementData = null;
|
|
741
|
+
let adjustedOffset = offset;
|
|
742
|
+
if (type === this.UNSIGNED_SHORT) {
|
|
743
|
+
if (adjustedOffset % 2) {
|
|
744
|
+
this.setError(this.INVALID_OPERATION);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
adjustedOffset >>= 1;
|
|
748
|
+
elementData = new Uint16Array(elementBuffer._elements.buffer);
|
|
749
|
+
} else if (type === this.UNSIGNED_INT) {
|
|
750
|
+
if (adjustedOffset % 4) {
|
|
751
|
+
this.setError(this.INVALID_OPERATION);
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
adjustedOffset >>= 2;
|
|
755
|
+
elementData = new Uint32Array(elementBuffer._elements.buffer);
|
|
756
|
+
} else if (type === this.UNSIGNED_BYTE) {
|
|
757
|
+
elementData = elementBuffer._elements;
|
|
758
|
+
} else {
|
|
759
|
+
this.setError(this.INVALID_ENUM);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
let reducedCount = count;
|
|
763
|
+
switch (mode) {
|
|
764
|
+
case this.TRIANGLES:
|
|
765
|
+
if (count % 3) reducedCount -= count % 3;
|
|
766
|
+
break;
|
|
767
|
+
case this.LINES:
|
|
768
|
+
if (count % 2) reducedCount -= count % 2;
|
|
769
|
+
break;
|
|
770
|
+
case this.POINTS:
|
|
771
|
+
break;
|
|
772
|
+
case this.LINE_LOOP:
|
|
773
|
+
case this.LINE_STRIP:
|
|
774
|
+
if (count < 2) {
|
|
775
|
+
this.setError(this.INVALID_OPERATION);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
break;
|
|
779
|
+
case this.TRIANGLE_FAN:
|
|
780
|
+
case this.TRIANGLE_STRIP:
|
|
781
|
+
if (count < 3) {
|
|
782
|
+
this.setError(this.INVALID_OPERATION);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
break;
|
|
786
|
+
default:
|
|
787
|
+
this.setError(this.INVALID_ENUM);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (!this._framebufferOk()) return;
|
|
791
|
+
if (reducedCount === 0 || instanceCount === 0) {
|
|
792
|
+
this._checkVertexAttribState(0);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (reducedCount + adjustedOffset >>> 0 > elementData.length) {
|
|
796
|
+
this.setError(this.INVALID_OPERATION);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
let maxIndex = 0;
|
|
800
|
+
for (let i = adjustedOffset; i < adjustedOffset + reducedCount; ++i) {
|
|
801
|
+
if (elementData[i] > maxIndex) maxIndex = elementData[i];
|
|
802
|
+
}
|
|
803
|
+
if (this._checkVertexAttribState(maxIndex)) {
|
|
804
|
+
this._native2.drawElementsInstanced(mode, reducedCount, type, offset, instanceCount);
|
|
805
|
+
}
|
|
338
806
|
}
|
|
339
807
|
vertexAttribDivisor(index, divisor) {
|
|
340
808
|
this._native2.vertexAttribDivisor(index, divisor);
|
|
@@ -343,10 +811,19 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
343
811
|
this._native2.vertexAttribIPointer(index, size, type, stride, offset);
|
|
344
812
|
}
|
|
345
813
|
drawBuffers(buffers) {
|
|
346
|
-
|
|
814
|
+
const mapped = buffers.map((b) => b === 1029 ? this.COLOR_ATTACHMENT0 : b);
|
|
815
|
+
this._native2.drawBuffers(Array.from(mapped));
|
|
347
816
|
}
|
|
348
817
|
drawRangeElements(mode, start, end, count, type, offset) {
|
|
349
|
-
|
|
818
|
+
if (count < 0 || offset < 0) {
|
|
819
|
+
this.setError(this.INVALID_VALUE);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (end < start) {
|
|
823
|
+
this.setError(this.INVALID_VALUE);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
this.drawElements(mode, count, type, offset);
|
|
350
827
|
}
|
|
351
828
|
blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) {
|
|
352
829
|
this._native2.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
|
@@ -360,8 +837,24 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
360
837
|
readBuffer(src) {
|
|
361
838
|
this._native2.readBuffer(src);
|
|
362
839
|
}
|
|
363
|
-
renderbufferStorageMultisample(target, samples,
|
|
364
|
-
|
|
840
|
+
renderbufferStorageMultisample(target, samples, internalFormat, width, height) {
|
|
841
|
+
if (target !== this.RENDERBUFFER) {
|
|
842
|
+
this.setError(this.INVALID_ENUM);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const renderbuffer = this._activeRenderbuffer;
|
|
846
|
+
if (!renderbuffer) {
|
|
847
|
+
this.setError(this.INVALID_OPERATION);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
this._saveError();
|
|
851
|
+
this._native2.renderbufferStorageMultisample(target, samples, internalFormat, width, height);
|
|
852
|
+
const error = this.getError();
|
|
853
|
+
this._restoreError(error);
|
|
854
|
+
if (error !== this.NO_ERROR) return;
|
|
855
|
+
renderbuffer._width = width;
|
|
856
|
+
renderbuffer._height = height;
|
|
857
|
+
renderbuffer._format = internalFormat;
|
|
365
858
|
}
|
|
366
859
|
// ─── Unsigned Integer Uniforms ────────────────────────────────────────
|
|
367
860
|
uniform1ui(location, v0) {
|
|
@@ -439,7 +932,7 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
439
932
|
const isUintType = type === UINT || type === UVEC2 || type === UVEC3 || type === UVEC4;
|
|
440
933
|
if (!isUintType) return super.getUniform(program, location);
|
|
441
934
|
if (!program || !location) return null;
|
|
442
|
-
const data = this.
|
|
935
|
+
const data = this._gl.getUniformi(program._ | 0, location._ | 0);
|
|
443
936
|
if (!data) return null;
|
|
444
937
|
if (type === UINT) return data[0] >>> 0;
|
|
445
938
|
if (type === UVEC2) return new Uint32Array([data[0] >>> 0, data[1] >>> 0]);
|
|
@@ -482,6 +975,8 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
482
975
|
warnNotImplemented("WebGL2RenderingContext.getParameter(GL_EXTENSIONS)");
|
|
483
976
|
return "";
|
|
484
977
|
}
|
|
978
|
+
if (pname === 36006) return this._activeDrawFramebuffer;
|
|
979
|
+
if (pname === 36010) return this._activeReadFramebuffer;
|
|
485
980
|
switch (pname) {
|
|
486
981
|
case 36183:
|
|
487
982
|
// MAX_SAMPLES
|
|
@@ -545,8 +1040,6 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
545
1040
|
// PIXEL_UNPACK_BUFFER_BINDING
|
|
546
1041
|
case 3074:
|
|
547
1042
|
// READ_BUFFER
|
|
548
|
-
case 36010:
|
|
549
|
-
// READ_FRAMEBUFFER_BINDING
|
|
550
1043
|
case 32874:
|
|
551
1044
|
// TEXTURE_BINDING_3D
|
|
552
1045
|
case 35869:
|
|
@@ -585,6 +1078,103 @@ class WebGL2RenderingContext extends WebGLRenderingContext {
|
|
|
585
1078
|
const s = this._native2.getStringi(name, index);
|
|
586
1079
|
return s.length > 0 ? s : null;
|
|
587
1080
|
}
|
|
1081
|
+
// ─── WebGL2 overrides for format validation ────────────────────────────
|
|
1082
|
+
/**
|
|
1083
|
+
* WebGL2 supports ~30+ renderbuffer formats (R8, RG8, RGBA8, RGBA16F,
|
|
1084
|
+
* DEPTH_COMPONENT24, DEPTH32F_STENCIL8, etc.). The WebGL1 base class
|
|
1085
|
+
* only allows 7 formats. Delegate format validation to native GL.
|
|
1086
|
+
*/
|
|
1087
|
+
renderbufferStorage(target, internalFormat, width, height) {
|
|
1088
|
+
if (target !== this.RENDERBUFFER) {
|
|
1089
|
+
this.setError(this.INVALID_ENUM);
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
const renderbuffer = this._activeRenderbuffer;
|
|
1093
|
+
if (!renderbuffer) {
|
|
1094
|
+
this.setError(this.INVALID_OPERATION);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
if (width < 0 || height < 0) {
|
|
1098
|
+
this.setError(this.INVALID_VALUE);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
while (this._gl.getError() !== this.NO_ERROR) {
|
|
1102
|
+
}
|
|
1103
|
+
this._gl.renderbufferStorage(target, internalFormat, width, height);
|
|
1104
|
+
if (this._gl.getError() !== this.NO_ERROR) return;
|
|
1105
|
+
renderbuffer._width = width;
|
|
1106
|
+
renderbuffer._height = height;
|
|
1107
|
+
renderbuffer._format = internalFormat;
|
|
1108
|
+
const activeFramebuffer = this._activeFramebuffer;
|
|
1109
|
+
if (activeFramebuffer) {
|
|
1110
|
+
const attachments = this._getAttachments();
|
|
1111
|
+
let needsUpdate = false;
|
|
1112
|
+
for (let i = 0; i < attachments.length; ++i) {
|
|
1113
|
+
if (activeFramebuffer._attachments[attachments[i]] === renderbuffer) {
|
|
1114
|
+
needsUpdate = true;
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (needsUpdate) this._updateFramebufferAttachments(activeFramebuffer);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* WebGL2 makes several WebGL1 extensions part of the core spec.
|
|
1123
|
+
* EXT_color_buffer_float and EXT_color_buffer_half_float are always
|
|
1124
|
+
* available in WebGL2 contexts. Append them if the base class didn't.
|
|
1125
|
+
*/
|
|
1126
|
+
getSupportedExtensions() {
|
|
1127
|
+
const exts = super.getSupportedExtensions();
|
|
1128
|
+
const ensure = ["EXT_color_buffer_float", "EXT_color_buffer_half_float", "OES_texture_half_float"];
|
|
1129
|
+
for (const ext of ensure) {
|
|
1130
|
+
if (exts.indexOf(ext) === -1) exts.push(ext);
|
|
1131
|
+
}
|
|
1132
|
+
return exts;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* WebGL2 allows reading pixels in many more format/type combinations
|
|
1136
|
+
* than WebGL1's strict RGBA/UNSIGNED_BYTE. Delegate validation to native.
|
|
1137
|
+
*/
|
|
1138
|
+
readPixels(x, y, width, height, format, type, pixels) {
|
|
1139
|
+
if (!pixels) return;
|
|
1140
|
+
if (width < 0 || height < 0) {
|
|
1141
|
+
this.setError(this.INVALID_VALUE);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
if (!this._framebufferOk()) return;
|
|
1145
|
+
const componentCount = format === 6408 || format === 32856 ? 4 : format === 6407 ? 3 : format === 33319 ? 2 : 1;
|
|
1146
|
+
const bytesPerComponent = type === 5126 ? 4 : type === 5131 || type === 36193 ? 2 : type === 5125 || type === 5124 ? 4 : type === 5123 || type === 5122 ? 2 : 1;
|
|
1147
|
+
const byteCount = width * height * componentCount * bytesPerComponent;
|
|
1148
|
+
const pixelData = new Uint8Array(byteCount);
|
|
1149
|
+
this._saveError();
|
|
1150
|
+
const result = this._gl.readPixels(x, y, width, height, format, type, Uint8ArrayToVariant(pixelData));
|
|
1151
|
+
const error = this.getError();
|
|
1152
|
+
this._restoreError(error);
|
|
1153
|
+
if (error !== this.NO_ERROR) return;
|
|
1154
|
+
const src = result && result.length > 0 ? result : pixelData;
|
|
1155
|
+
if (pixels instanceof Uint8Array) {
|
|
1156
|
+
pixels.set(src);
|
|
1157
|
+
} else if (pixels instanceof Float32Array) {
|
|
1158
|
+
const floatView = new Float32Array(src.buffer, 0, pixels.length);
|
|
1159
|
+
pixels.set(floatView);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
// framebufferTexture2D: inherits from base class. WebGL2 allows level>0 for
|
|
1163
|
+
// mipmap attachments, but Three.js only uses level=0. The base class level===0
|
|
1164
|
+
// check is acceptable for now. If needed, override to skip level validation.
|
|
1165
|
+
/**
|
|
1166
|
+
* WebGL2 never blocks draw calls on JS-side framebuffer format checks.
|
|
1167
|
+
* The native GL (Mesa/libepoxy) handles completeness and generates
|
|
1168
|
+
* INVALID_FRAMEBUFFER_OPERATION for truly incomplete FBOs at draw time.
|
|
1169
|
+
* This matches headless-gl's approach: _framebufferOk() always returns true.
|
|
1170
|
+
*
|
|
1171
|
+
* The base class rejects valid WebGL2 formats (RGBA16F/HALF_FLOAT, depth
|
|
1172
|
+
* textures, WebGL2 renderbuffer formats) causing silent rendering failures
|
|
1173
|
+
* for postprocessing effects and environment maps.
|
|
1174
|
+
*/
|
|
1175
|
+
_framebufferOk() {
|
|
1176
|
+
return true;
|
|
1177
|
+
}
|
|
588
1178
|
}
|
|
589
1179
|
export {
|
|
590
1180
|
WebGL2RenderingContext
|