@geastack/native-webgl-angle 0.1.11 → 0.1.13

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/src/inflate.ts ADDED
@@ -0,0 +1,252 @@
1
+ // Raw DEFLATE (RFC 1951) decoder for data baked into a native binary -- glyph
2
+ // atlases, an app's mesh blobs. Baked data ships as base64 string literals,
3
+ // and compressing it first is what keeps a font or a model kit inside a
4
+ // microcontroller's app partition: zlib's deflate packs a 512 KB alpha atlas to
5
+ // ~40 KB and mesh blobs (flat shading triples every vertex) to a third.
6
+ // Encoders are zlib's own (node:zlib deflateRawSync, Python zlib with wbits=-15).
7
+
8
+ const lengthBase = new Uint16Array([3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258])
9
+ const lengthExtra = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0])
10
+ const distanceBase = new Uint16Array([
11
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385,
12
+ 24577
13
+ ])
14
+ const distanceExtra = new Uint8Array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13])
15
+ const codeLengthOrder = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15])
16
+
17
+ // The decoder is plain functions over locals and typed arrays, no class: a
18
+ // native build narrows a function's integer locals to machine integers, but a
19
+ // class's number fields stay doubles, and on a core without a double FPU (the
20
+ // ESP32-S31) every bit read through a field was several soft-float calls --
21
+ // inflating a game's asset kit at boot took over 80 seconds.
22
+
23
+ /** Decoder state: read position, bit buffer, bits held, bytes written. */
24
+ const position = 0
25
+ const bitBuffer = 1
26
+ const bitCount = 2
27
+ const written = 3
28
+
29
+ function bits(state: Int32Array, source: Uint8Array, count: number): number {
30
+ let buffer = state[bitBuffer]
31
+ let have = state[bitCount]
32
+ let at = state[position]
33
+ while (have < count) {
34
+ buffer |= source[at] << have
35
+ at += 1
36
+ have += 8
37
+ }
38
+ state[position] = at
39
+ state[bitBuffer] = buffer >>> count
40
+ state[bitCount] = have - count
41
+ return buffer & ((1 << count) - 1)
42
+ }
43
+
44
+ /**
45
+ * A canonical Huffman code as puff.c keeps it: `counts` (codes per length,
46
+ * 16 entries) and `symbols` (in code order) built from code lengths.
47
+ */
48
+ function buildHuffman(counts: Uint16Array, symbols: Uint16Array, lengths: Uint8Array, start: number, count: number): void {
49
+ counts.fill(0)
50
+ for (let i = 0; i < count; i++) counts[lengths[start + i]] += 1
51
+ counts[0] = 0
52
+ const offsets = new Uint16Array(16)
53
+ for (let length = 1; length < 15; length++) offsets[length + 1] = offsets[length] + counts[length]
54
+ for (let i = 0; i < count; i++) {
55
+ const length = lengths[start + i]
56
+ if (length !== 0) {
57
+ symbols[offsets[length]] = i
58
+ offsets[length] += 1
59
+ }
60
+ }
61
+ }
62
+
63
+ function decode(state: Int32Array, source: Uint8Array, counts: Uint16Array, symbols: Uint16Array): number {
64
+ let code = 0
65
+ let first = 0
66
+ let index = 0
67
+ for (let length = 1; length < 16; length++) {
68
+ code |= bits(state, source, 1)
69
+ const count = counts[length]
70
+ if (code - first < count) return symbols[index + code - first]
71
+ index += count
72
+ first = (first + count) << 1
73
+ code <<= 1
74
+ }
75
+ throw new Error('inflate: invalid Huffman code')
76
+ }
77
+
78
+ function stored(state: Int32Array, source: Uint8Array, output: Uint8Array): void {
79
+ state[bitBuffer] = 0
80
+ state[bitCount] = 0
81
+ let at = state[position]
82
+ let out = state[written]
83
+ const length = source[at] | (source[at + 1] << 8)
84
+ at += 4
85
+ for (let i = 0; i < length; i++) {
86
+ output[out] = source[at]
87
+ out += 1
88
+ at += 1
89
+ }
90
+ state[position] = at
91
+ state[written] = out
92
+ }
93
+
94
+ function fixedCodes(literalCounts: Uint16Array, literals: Uint16Array, distanceCounts: Uint16Array, distances: Uint16Array): void {
95
+ const lengths = new Uint8Array(320)
96
+ for (let i = 0; i < 144; i++) lengths[i] = 8
97
+ for (let i = 144; i < 256; i++) lengths[i] = 9
98
+ for (let i = 256; i < 280; i++) lengths[i] = 7
99
+ for (let i = 280; i < 288; i++) lengths[i] = 8
100
+ for (let i = 288; i < 320; i++) lengths[i] = 5
101
+ buildHuffman(literalCounts, literals, lengths, 0, 288)
102
+ buildHuffman(distanceCounts, distances, lengths, 288, 30)
103
+ }
104
+
105
+ function dynamicCodes(
106
+ state: Int32Array,
107
+ source: Uint8Array,
108
+ literalCounts: Uint16Array,
109
+ literals: Uint16Array,
110
+ distanceCounts: Uint16Array,
111
+ distances: Uint16Array
112
+ ): void {
113
+ const literalCount = bits(state, source, 5) + 257
114
+ const distanceCount = bits(state, source, 5) + 1
115
+ const codeLengthCount = bits(state, source, 4) + 4
116
+ const lengths = new Uint8Array(320)
117
+ for (let i = 0; i < codeLengthCount; i++) lengths[codeLengthOrder[i]] = bits(state, source, 3)
118
+ buildHuffman(literalCounts, literals, lengths, 0, 19)
119
+ lengths.fill(0)
120
+ let index = 0
121
+ while (index < literalCount + distanceCount) {
122
+ const symbol = decode(state, source, literalCounts, literals)
123
+ if (symbol < 16) {
124
+ lengths[index] = symbol
125
+ index += 1
126
+ continue
127
+ }
128
+ let value = 0
129
+ let repeat = 0
130
+ if (symbol === 16) {
131
+ value = lengths[index - 1]
132
+ repeat = 3 + bits(state, source, 2)
133
+ } else if (symbol === 17) {
134
+ repeat = 3 + bits(state, source, 3)
135
+ } else {
136
+ repeat = 11 + bits(state, source, 7)
137
+ }
138
+ for (let i = 0; i < repeat; i++) {
139
+ lengths[index] = value
140
+ index += 1
141
+ }
142
+ }
143
+ buildHuffman(literalCounts, literals, lengths, 0, literalCount)
144
+ buildHuffman(distanceCounts, distances, lengths, literalCount, distanceCount)
145
+ }
146
+
147
+ function codes(
148
+ state: Int32Array,
149
+ source: Uint8Array,
150
+ output: Uint8Array,
151
+ literalCounts: Uint16Array,
152
+ literals: Uint16Array,
153
+ distanceCounts: Uint16Array,
154
+ distances: Uint16Array
155
+ ): void {
156
+ for (;;) {
157
+ const symbol = decode(state, source, literalCounts, literals)
158
+ if (symbol < 256) {
159
+ const out = state[written]
160
+ output[out] = symbol
161
+ state[written] = out + 1
162
+ continue
163
+ }
164
+ if (symbol === 256) return
165
+ const lengthSymbol = symbol - 257
166
+ const length = lengthBase[lengthSymbol] + bits(state, source, lengthExtra[lengthSymbol])
167
+ const distanceSymbol = decode(state, source, distanceCounts, distances)
168
+ const distance = distanceBase[distanceSymbol] + bits(state, source, distanceExtra[distanceSymbol])
169
+ // A match may overlap its own output, so it copies byte by byte.
170
+ let out = state[written]
171
+ let from = out - distance
172
+ for (let i = 0; i < length; i++) {
173
+ output[out] = output[from]
174
+ out += 1
175
+ from += 1
176
+ }
177
+ state[written] = out
178
+ }
179
+ }
180
+
181
+ /** Inflates a raw DEFLATE stream into exactly `length` bytes. */
182
+ export function inflateRaw(source: Uint8Array, length: number): Uint8Array {
183
+ const output = new Uint8Array(length)
184
+ const state = new Int32Array(4)
185
+ const literalCounts = new Uint16Array(16)
186
+ const literals = new Uint16Array(288)
187
+ const distanceCounts = new Uint16Array(16)
188
+ const distances = new Uint16Array(288)
189
+ let last = 0
190
+ while (last === 0) {
191
+ last = bits(state, source, 1)
192
+ const type = bits(state, source, 2)
193
+ if (type === 0) stored(state, source, output)
194
+ else if (type === 1) {
195
+ fixedCodes(literalCounts, literals, distanceCounts, distances)
196
+ codes(state, source, output, literalCounts, literals, distanceCounts, distances)
197
+ } else if (type === 2) {
198
+ dynamicCodes(state, source, literalCounts, literals, distanceCounts, distances)
199
+ codes(state, source, output, literalCounts, literals, distanceCounts, distances)
200
+ } else throw new Error('inflate: invalid block type')
201
+ }
202
+ if (state[written] !== length) throw new Error('inflate: stream length does not match')
203
+ return output
204
+ }
205
+
206
+ function base64Value(code: number): number {
207
+ if (code >= 65 && code <= 90) return code - 65
208
+ if (code >= 97 && code <= 122) return code - 71
209
+ if (code >= 48 && code <= 57) return code + 4
210
+ if (code === 43) return 62
211
+ if (code === 47) return 63
212
+ return 0
213
+ }
214
+
215
+ /**
216
+ * The bytes base64 string chunks spell. Not atob: the native runtime stores
217
+ * strings as UTF-8, so atob's "binary string" would read bytes >= 0x80 back as
218
+ * Unicode through charCodeAt. Every chunk but the last is a multiple of 4.
219
+ */
220
+ export function base64Bytes(chunks: string[]): Uint8Array {
221
+ let length = 0
222
+ for (let c = 0; c < chunks.length; c++) {
223
+ const chunk = chunks[c]
224
+ length += (chunk.length / 4) * 3
225
+ if (chunk.length > 0 && chunk.charCodeAt(chunk.length - 1) === 61) length -= 1
226
+ if (chunk.length > 1 && chunk.charCodeAt(chunk.length - 2) === 61) length -= 1
227
+ }
228
+ const bytes = new Uint8Array(length)
229
+ let out = 0
230
+ for (let c = 0; c < chunks.length; c++) {
231
+ const chunk = chunks[c]
232
+ for (let i = 0; i < chunk.length; i += 4) {
233
+ const code2 = chunk.charCodeAt(i + 2)
234
+ const code3 = chunk.charCodeAt(i + 3)
235
+ const v0 = base64Value(chunk.charCodeAt(i))
236
+ const v1 = base64Value(chunk.charCodeAt(i + 1))
237
+ const v2 = base64Value(code2)
238
+ const v3 = base64Value(code3)
239
+ bytes[out] = (v0 << 2) | (v1 >> 4)
240
+ out += 1
241
+ if (code2 !== 61) {
242
+ bytes[out] = ((v1 & 15) << 4) | (v2 >> 2)
243
+ out += 1
244
+ }
245
+ if (code3 !== 61) {
246
+ bytes[out] = ((v2 & 3) << 6) | v3
247
+ out += 1
248
+ }
249
+ }
250
+ }
251
+ return bytes
252
+ }
@@ -1,3 +1,7 @@
1
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
2
+ declare function threeWebGLCopyTexImage2D(target:number,level:number,internalFormat:number,x:number,y:number,width:number,height:number,border:number):void;
3
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
4
+ declare function threeWebGLCopyTexSubImage2D(target:number,level:number,xoffset:number,yoffset:number,x:number,y:number,width:number,height:number):void;
1
5
  /// <reference path="./gea-native-types.d.ts" />
2
6
 
3
7
  import {
@@ -91,6 +95,7 @@ type NativeHandleKind =
91
95
  | 'program'
92
96
  | 'renderbuffer'
93
97
  | 'shader'
98
+ | 'sync'
94
99
  | 'texture'
95
100
  | 'uniform'
96
101
  | 'vertexArray'
@@ -198,6 +203,27 @@ function typedArrayKind(value: BufferSourceLike): number {
198
203
  return 1
199
204
  }
200
205
 
206
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
207
+ declare function threeWebGLBlitFramebuffer(sx0:number, sy0:number, sx1:number, sy1:number, dx0:number, dy0:number, dx1:number, dy1:number, mask:number, filter:number): void
208
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
209
+ declare function threeWebGLRenderbufferStorageMultisample(target:number, samples:number, format:number, width:number, height:number): void
210
+ type ReadPixelsDestination = Uint8Array | Uint16Array | Float32Array | Uint32Array | Int32Array | Int16Array | Int8Array
211
+ // Mutates the destination view; deliberately not marked host-inert.
212
+ /** @gea-host-typed-array-element-writes Stores pixels into the destination typed array's elements only; no other property of any object, no callbacks. */
213
+ declare function threeWebGLReadPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination, offset: number): void
214
+ // WebGL2 asynchronous readback: into the bound PIXEL_PACK_BUFFER, fenced, then read through getBufferSubData.
215
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
216
+ declare function threeWebGLReadPixelsPack(x: number, y: number, width: number, height: number, format: number, type: number, offset: number): void
217
+ /** @gea-host-inert Creates a GL sync object; never calls back into script. */
218
+ declare function threeWebGLFenceSync(): number
219
+ /** @gea-host-inert Reads scalar arguments, waits on GL state only; never calls back into script. */
220
+ declare function threeWebGLClientWaitSync(sync: number, flags: number, timeout: number): number
221
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
222
+ declare function threeWebGLDeleteSync(sync: number): void
223
+ // Mutates the destination view; deliberately not marked host-inert.
224
+ /** @gea-host-typed-array-element-writes Stores buffer bytes into the destination typed array's elements only; no other property of any object, no callbacks. */
225
+ declare function threeWebGLGetBufferSubData(target: number, srcByteOffset: number, destination: ReadPixelsDestination, dstOffset: number, length: number): void
226
+
201
227
  function zeroF32Array(length: number): f32[] {
202
228
  const out: f32[] = []
203
229
  for (let index = 0; index < length; index++) out.push(0 as f32)
@@ -246,6 +272,14 @@ declare function threeWebGLTexImage2D(target: number, level: number, internalFor
246
272
  declare function threeWebGLTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, values: BufferSourceLike): void
247
273
  /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
248
274
  declare function threeWebGLTexStorage2D(target: number, levels: number, internalFormat: number, width: number, height: number): void
275
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
276
+ declare function threeWebGLTexImage3D(target: number, level: number, internalFormat: number, width: number, height: number, depth: number, format: number, type: number, values: BufferSourceLike): void
277
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
278
+ declare function threeWebGLTexSubImage3D(target: number, level: number, xoffset: number, yoffset: number, zoffset: number, width: number, height: number, depth: number, format: number, type: number, values: BufferSourceLike): void
279
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
280
+ declare function threeWebGLTexStorage3D(target: number, levels: number, internalFormat: number, width: number, height: number, depth: number): void
281
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
282
+ declare function threeWebGLFramebufferTextureLayer(target: number, attachment: number, texture: number, level: number, layer: number): void
249
283
 
250
284
  // Shader source keyed by the stable native GL handle. A NativeShader instance
251
285
  // can be value-copied when it round-trips through a function-return / gcv
@@ -303,6 +337,9 @@ export interface NativeWebGLContextAttributes {
303
337
  failIfMajorPerformanceCaveat: boolean
304
338
  }
305
339
 
340
+ // Decoded image boundary. A native host never pretends DOM images have pixels.
341
+ export interface NativeTextureImage { width: number; height: number; data: BufferSourceLike }
342
+
306
343
  export class NativeWebGLCanvas {
307
344
  width: number
308
345
  height: number
@@ -652,6 +689,10 @@ export class NativeWebGL2RenderingContext {
652
689
  readonly TIMEOUT_EXPIRED = 0x911B
653
690
  readonly CONDITION_SATISFIED = 0x911C
654
691
  readonly WAIT_FAILED = 0x911D
692
+ readonly SYNC_GPU_COMMANDS_COMPLETE = 0x9117
693
+ readonly PIXEL_PACK_BUFFER = 0x88EB
694
+ readonly PIXEL_PACK_BUFFER_BINDING = 0x88ED
695
+ readonly STREAM_READ = 0x88E1
655
696
  readonly COLOR = 0x1800
656
697
  readonly DEPTH_COMPONENT = 0x1902
657
698
  readonly ALPHA = 0x1906
@@ -924,11 +965,14 @@ export class NativeWebGL2RenderingContext {
924
965
  if (name === this.MAX_RENDERBUFFER_SIZE || name === this.MAX_3D_TEXTURE_SIZE) return 16384
925
966
  if (name === this.MAX_ARRAY_TEXTURE_LAYERS) return 2048
926
967
  if (name === this.MAX_VERTEX_ATTRIBS) return 16
927
- if (name === this.MAX_TEXTURE_IMAGE_UNITS || name === this.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 16
968
+ // threejs-rendozer binds up to 32 textures per program (both stages); three checks a program's total against this.
969
+ if (name === this.MAX_TEXTURE_IMAGE_UNITS || name === this.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 32
928
970
  if (name === this.MAX_COMBINED_TEXTURE_IMAGE_UNITS) return 32
929
971
  if (name === this.MAX_VERTEX_UNIFORM_VECTORS || name === this.MAX_FRAGMENT_UNIFORM_VECTORS) return 1024
930
972
  if (name === this.MAX_VARYING_VECTORS) return 32
931
- if (name === this.MAX_SAMPLES || name === this.SAMPLES) return 4
973
+ // threejs-rendozer reports 8 when every multisampled renderbuffer format supports it (and steps down per format).
974
+ if (name === this.MAX_SAMPLES) return 8
975
+ if (name === this.SAMPLES) return 4
932
976
  if (name === this.MAX_UNIFORM_BUFFER_BINDINGS) return 24
933
977
  if (name === this.DEPTH_BITS) return 24
934
978
  if (name === this.STENCIL_BITS) return 8
@@ -1092,9 +1136,13 @@ export class NativeWebGL2RenderingContext {
1092
1136
  texParameterf(target: number, pname: number, param: number): void { nativeWebGLTexParameteri(target, pname, param) }
1093
1137
  pixelStorei(pname: number, param: number | boolean): void { nativeWebGLPixelStorei(pname, param === true ? 1 : param === false ? 0 : param) }
1094
1138
  generateMipmap(target: number): void { nativeWebGLGenerateMipmap(target) }
1095
- texImage2D(target: number, level: number, internalFormat: number, widthOrFormat: number, heightOrType: number, borderOrSource?: number | TexImageSource | null, format?: number, type?: number, pixels?: BufferSourceLike | TexImageSource | null): void {
1139
+ texImage2D(target: number, level: number, internalFormat: number, widthOrFormat: number, heightOrType: number, borderOrSource?: number | NativeTextureImage | null, format?: number, type?: number, pixels?: BufferSourceLike | null): void {
1096
1140
  if (typeof borderOrSource === 'number' && typeof format === 'number' && typeof type === 'number') {
1097
- threeWebGLTexImage2D(target, level, internalFormat, widthOrFormat, heightOrType, format, type, pixels as BufferSourceLike)
1141
+ threeWebGLTexImage2D(target, level, internalFormat, widthOrFormat, heightOrType, format, type, pixels ?? new Uint8Array(0))
1142
+ } else if (borderOrSource !== null && borderOrSource !== undefined && typeof borderOrSource !== 'number') {
1143
+ threeWebGLTexImage2D(target, level, internalFormat, borderOrSource.width, borderOrSource.height, widthOrFormat, heightOrType, borderOrSource.data)
1144
+ } else {
1145
+ throw new Error(`Native texImage2D requires dimensions and typed pixels, or a decoded NativeTextureImage (target ${target}, level ${level}, internal format ${internalFormat}, source ${borderOrSource === null ? 'null' : typeof borderOrSource})`)
1098
1146
  }
1099
1147
  }
1100
1148
  texSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, pixels: BufferSourceLike): void {
@@ -1110,9 +1158,15 @@ export class NativeWebGL2RenderingContext {
1110
1158
  // Three uploads 3D and array textures only from typed `image.data` /
1111
1159
  // `mipmap.data` (or `null` to allocate), the same carrier the 2D uploads
1112
1160
  // take; no TexImageSource ever reaches these entry points.
1113
- texImage3D(_target: number, _level: number, _internalFormat: number, _width: number, _height: number, _depth: number, _border: number, _format: number, _type: number, _source: BufferSourceLike | null): void {}
1114
- texSubImage3D(_target: number, _level: number, _xoffset: number, _yoffset: number, _zoffset: number, _width: number, _height: number, _depth: number, _format: number, _type: number, _source: BufferSourceLike): void {}
1115
- texStorage3D(_target: number, _levels: number, _internalFormat: number, _width: number, _height: number, _depth: number): void {}
1161
+ texImage3D(target: number, level: number, internalFormat: number, width: number, height: number, depth: number, _border: number, format: number, type: number, source: BufferSourceLike | null): void {
1162
+ threeWebGLTexImage3D(target, level, internalFormat, width, height, depth, format, type, source ?? new Uint8Array(0))
1163
+ }
1164
+ texSubImage3D(target: number, level: number, xoffset: number, yoffset: number, zoffset: number, width: number, height: number, depth: number, format: number, type: number, source: BufferSourceLike): void {
1165
+ threeWebGLTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, source)
1166
+ }
1167
+ texStorage3D(target: number, levels: number, internalFormat: number, width: number, height: number, depth: number): void {
1168
+ threeWebGLTexStorage3D(target, levels, internalFormat, width, height, depth)
1169
+ }
1116
1170
 
1117
1171
  createVertexArray(): NativeHandle { return createHandle('vertexArray', nativeWebGLCreateVertexArray()) }
1118
1172
  bindVertexArray(vertexArray: NativeHandle | null): void { nativeWebGLBindVertexArray(nullableNativeHandle(vertexArray)) }
@@ -1130,12 +1184,14 @@ export class NativeWebGL2RenderingContext {
1130
1184
  deleteRenderbuffer(renderbuffer: NativeHandle | null): void { nativeWebGLDeleteRenderbuffer(nullableNativeHandle(renderbuffer)) }
1131
1185
  renderbufferStorage(target: number, internalFormat: number, width: number, height: number): void { nativeWebGLRenderbufferStorage(target, internalFormat, width, height) }
1132
1186
  renderbufferStorageMultisample(target: number, samples: number, internalFormat: number, width: number, height: number): void {
1133
- this.renderbufferStorage(target, internalFormat, width, height)
1187
+ threeWebGLRenderbufferStorageMultisample(target, samples, internalFormat, width, height)
1134
1188
  }
1135
1189
  framebufferRenderbuffer(target: number, attachment: number, renderbuffertarget: number, renderbuffer: NativeHandle | null): void {
1136
1190
  nativeWebGLFramebufferRenderbuffer(target, attachment, renderbuffertarget, nullableNativeHandle(renderbuffer))
1137
1191
  }
1138
- framebufferTextureLayer(_target: number, _attachment: number, _texture: NativeHandle | null, _level: number, _layer: number): void {}
1192
+ framebufferTextureLayer(target: number, attachment: number, texture: NativeHandle | null, level: number, layer: number): void {
1193
+ threeWebGLFramebufferTextureLayer(target, attachment, nullableNativeHandle(texture), level, layer)
1194
+ }
1139
1195
  drawBuffers(buffers: number[]): void { nativeWebGLDrawBuffers(buffers[0] ?? this.COLOR_ATTACHMENT0) }
1140
1196
 
1141
1197
  clearColor(r: number, g: number, b: number, a: number): void { nativeWebGLClearColor(r, g, b, a) }
@@ -1176,16 +1232,26 @@ export class NativeWebGL2RenderingContext {
1176
1232
  flush(): void {}
1177
1233
  hint(_target: number, _mode: number): void {}
1178
1234
  invalidateFramebuffer(_target: number, _attachments: BufferSourceLike): void {}
1179
- blitFramebuffer(_srcX0: number, _srcY0: number, _srcX1: number, _srcY1: number, _dstX0: number, _dstY0: number, _dstX1: number, _dstY1: number, _mask: number, _filter: number): void {}
1235
+ blitFramebuffer(sx0:number, sy0:number, sx1:number, sy1:number, dx0:number, dy0:number, dx1:number, dy1:number, mask:number, filter:number): void { threeWebGLBlitFramebuffer(sx0,sy0,sx1,sy1,dx0,dy0,dx1,dy1,mask,filter) }
1180
1236
  readBuffer(_src: number): void {}
1181
- readPixels(_x: number, _y: number, _width: number, _height: number, _format: number, _type: number, _destination: unknown): void {}
1182
- copyTexImage2D(_target: number, _level: number, _internalFormat: number, _x: number, _y: number, _width: number, _height: number, _border: number): void {}
1183
- copyTexSubImage2D(_target: number, _level: number, _xoffset: number, _yoffset: number, _x: number, _y: number, _width: number, _height: number): void {}
1237
+ // WebGL2 also reads into the bound PIXEL_PACK_BUFFER at a byte offset (three's
1238
+ // readRenderTargetPixelsAsync): the GPU copy is recorded without waiting for the GPU.
1239
+ readPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination | number, offset = 0): void {
1240
+ if (typeof destination === 'number') {
1241
+ threeWebGLReadPixelsPack(x, y, width, height, format, type, destination)
1242
+ return
1243
+ }
1244
+ threeWebGLReadPixels(x, y, width, height, format, type, destination, offset)
1245
+ }
1246
+ copyTexImage2D(target: number, level: number, internalFormat: number, x: number, y: number, width: number, height: number, border: number): void {threeWebGLCopyTexImage2D(target,level,internalFormat,x,y,width,height,border);}
1247
+ copyTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, x: number, y: number, width: number, height: number): void {threeWebGLCopyTexSubImage2D(target,level,xoffset,yoffset,x,y,width,height);}
1184
1248
  copyTexSubImage3D(_target: number, _level: number, _xoffset: number, _yoffset: number, _zoffset: number, _x: number, _y: number, _width: number, _height: number): void {}
1185
1249
  clearBufferiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
1186
1250
  clearBufferuiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
1187
1251
  getBufferParameter(_target: number, _pname: number): number { return 0 }
1188
- getBufferSubData(_target: number, _srcByteOffset: number, _destination: unknown): void {}
1252
+ getBufferSubData(target: number, srcByteOffset: number, destination: ReadPixelsDestination, dstOffset = 0, length = 0): void {
1253
+ threeWebGLGetBufferSubData(target, srcByteOffset, destination, dstOffset, length)
1254
+ }
1189
1255
  getFramebufferAttachmentParameter(_target: number, _attachment: number, _pname: number): number { return 0 }
1190
1256
  getRenderbufferParameter(_target: number, _pname: number): number { return 0 }
1191
1257
  getTexParameter(_target: number, _pname: number): number { return 0 }
@@ -1196,9 +1262,14 @@ export class NativeWebGL2RenderingContext {
1196
1262
  uniformBlockBinding(_program: NativeProgram | null, _uniformBlockIndex: number, _uniformBlockBinding: number): void {}
1197
1263
  bindBufferBase(_target: number, _index: number, _buffer: NativeHandle | null): void {}
1198
1264
  bindBufferRange(_target: number, _index: number, _buffer: NativeHandle | null, _offset: number, _size: number): void {}
1199
- fenceSync(_condition: number, _flags: number): null { return null }
1200
- clientWaitSync(_sync: NativeHandle | null, _flags: number, _timeout: number): number { return 0 }
1201
- deleteSync(_sync: NativeHandle | null): void {}
1265
+ fenceSync(_condition: number, _flags: number): NativeHandle | null {
1266
+ const sync = threeWebGLFenceSync()
1267
+ return sync ? createHandle('sync', sync) : null
1268
+ }
1269
+ clientWaitSync(sync: NativeHandle | null, flags: number, timeout: number): number {
1270
+ return sync ? threeWebGLClientWaitSync(nativeHandle(sync), flags, timeout) : this.WAIT_FAILED
1271
+ }
1272
+ deleteSync(sync: NativeHandle | null): void { if (sync) threeWebGLDeleteSync(nativeHandle(sync)) }
1202
1273
  isBuffer(_buffer: NativeHandle | null): boolean { return true }
1203
1274
  isFramebuffer(_framebuffer: NativeHandle | null): boolean { return true }
1204
1275
  isProgram(_program: NativeProgram | null): boolean { return true }
@@ -35,6 +35,25 @@ export function nativeWebGLSwap(): void {
35
35
  threeWebGLSwap()
36
36
  }
37
37
 
38
+ // GPU time-elapsed timers (GL_EXT_disjoint_timer_query): begin returns a timer id (0 when
39
+ // unsupported), end closes the open timer, and result is the elapsed nanoseconds, or -1 while the
40
+ // GPU has not finished it. A timer is released once its result has been read.
41
+ export function nativeWebGLTimerBegin(): number {
42
+ return threeWebGLTimerBegin()
43
+ }
44
+ export function nativeWebGLTimerEnd(): void {
45
+ threeWebGLTimerEnd()
46
+ }
47
+ export function nativeWebGLTimerResult(timer: number): number {
48
+ return threeWebGLTimerResult(timer)
49
+ }
50
+ /** @gea-host-inert Native state only; retains no script references and invokes no script. */
51
+ declare function threeWebGLTimerBegin(): number
52
+ /** @gea-host-inert Native state only; retains no script references and invokes no script. */
53
+ declare function threeWebGLTimerEnd(): void
54
+ /** @gea-host-inert Native state only; retains no script references and invokes no script. */
55
+ declare function threeWebGLTimerResult(timer: number): number
56
+
38
57
  export function logNativeWebGL(message: string): void {
39
58
  threeAngleSmokeLog(message)
40
59
  }
@@ -148,9 +167,16 @@ export function nativeWebGLDrawElements(mode: number, count: number, type: numbe
148
167
  export function nativeWebGLDrawArrays(mode: number, first: number, count: number): void { threeWebGLDrawArrays(mode, first, count) }
149
168
  export function nativeWebGLGetError(): number { return threeWebGLGetError() }
150
169
 
151
- /** Lifecycle entry: retains/uses the host view and installs native monitors; not an inert call. */
170
+ /**
171
+ * Lifecycle entry: retains/uses the host view and installs native monitors; not an inert call.
172
+ * It writes no property on any script object, which is the narrower contract.
173
+ * @gea-host-no-property-writes
174
+ */
152
175
  declare function threeWebGLAttach(view: NSView, width: number, height: number, devicePixelRatio: number): boolean
153
- /** Lifecycle entry: synchronizes host layout and viewport state; keep outside the inert contract. */
176
+ /**
177
+ * Lifecycle entry: synchronizes host layout and viewport state; keep outside the inert contract.
178
+ * @gea-host-no-property-writes
179
+ */
154
180
  declare function threeWebGLSyncSize(fallbackAspect: number): number
155
181
  /** @gea-host-inert Native state only; retains no script references and invokes no script. */
156
182
  declare function threeWebGLWidth(): number