@luma.gl/webgl 9.3.0-alpha.4 → 9.3.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/converters/webgl-texture-table.d.ts +7 -1
- package/dist/adapter/converters/webgl-texture-table.d.ts.map +1 -1
- package/dist/adapter/converters/webgl-texture-table.js +121 -43
- package/dist/adapter/converters/webgl-texture-table.js.map +1 -1
- package/dist/adapter/device-helpers/webgl-device-features.d.ts.map +1 -1
- package/dist/adapter/device-helpers/webgl-device-features.js +1 -2
- package/dist/adapter/device-helpers/webgl-device-features.js.map +1 -1
- package/dist/adapter/device-helpers/webgl-device-info.js +5 -0
- package/dist/adapter/device-helpers/webgl-device-info.js.map +1 -1
- package/dist/adapter/helpers/get-shader-layout-from-glsl.js +16 -17
- package/dist/adapter/helpers/get-shader-layout-from-glsl.js.map +1 -1
- package/dist/adapter/resources/webgl-buffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-buffer.js +19 -4
- package/dist/adapter/resources/webgl-buffer.js.map +1 -1
- package/dist/adapter/resources/webgl-command-buffer.d.ts +2 -2
- package/dist/adapter/resources/webgl-command-buffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-command-buffer.js +2 -2
- package/dist/adapter/resources/webgl-command-buffer.js.map +1 -1
- package/dist/adapter/resources/webgl-command-encoder.d.ts +5 -4
- package/dist/adapter/resources/webgl-command-encoder.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-command-encoder.js +20 -7
- package/dist/adapter/resources/webgl-command-encoder.js.map +1 -1
- package/dist/adapter/resources/webgl-query-set.d.ts +29 -31
- package/dist/adapter/resources/webgl-query-set.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-query-set.js +193 -97
- package/dist/adapter/resources/webgl-query-set.js.map +1 -1
- package/dist/adapter/resources/webgl-render-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-render-pass.js +12 -0
- package/dist/adapter/resources/webgl-render-pass.js.map +1 -1
- package/dist/adapter/resources/webgl-render-pipeline.d.ts +13 -19
- package/dist/adapter/resources/webgl-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-render-pipeline.js +35 -152
- package/dist/adapter/resources/webgl-render-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgl-shared-render-pipeline.d.ts +24 -0
- package/dist/adapter/resources/webgl-shared-render-pipeline.d.ts.map +1 -0
- package/dist/adapter/resources/webgl-shared-render-pipeline.js +152 -0
- package/dist/adapter/resources/webgl-shared-render-pipeline.js.map +1 -0
- package/dist/adapter/resources/webgl-texture.d.ts +23 -4
- package/dist/adapter/resources/webgl-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgl-texture.js +203 -100
- package/dist/adapter/resources/webgl-texture.js.map +1 -1
- package/dist/adapter/webgl-device.d.ts +4 -2
- package/dist/adapter/webgl-device.d.ts.map +1 -1
- package/dist/adapter/webgl-device.js +31 -3
- package/dist/adapter/webgl-device.js.map +1 -1
- package/dist/adapter/webgl-presentation-context.d.ts +21 -0
- package/dist/adapter/webgl-presentation-context.d.ts.map +1 -0
- package/dist/adapter/webgl-presentation-context.js +64 -0
- package/dist/adapter/webgl-presentation-context.js.map +1 -0
- package/dist/dist.dev.js +1332 -788
- package/dist/dist.min.js +2 -2
- package/dist/index.cjs +1247 -797
- package/dist/index.cjs.map +4 -4
- package/package.json +3 -3
- package/src/adapter/converters/webgl-texture-table.ts +159 -47
- package/src/adapter/device-helpers/webgl-device-features.ts +1 -2
- package/src/adapter/device-helpers/webgl-device-info.ts +6 -0
- package/src/adapter/helpers/get-shader-layout-from-glsl.ts +18 -19
- package/src/adapter/resources/webgl-buffer.ts +16 -4
- package/src/adapter/resources/webgl-command-buffer.ts +3 -2
- package/src/adapter/resources/webgl-command-encoder.ts +22 -7
- package/src/adapter/resources/webgl-query-set.ts +229 -102
- package/src/adapter/resources/webgl-render-pass.ts +13 -0
- package/src/adapter/resources/webgl-render-pipeline.ts +45 -179
- package/src/adapter/resources/webgl-shared-render-pipeline.ts +208 -0
- package/src/adapter/resources/webgl-texture.ts +326 -121
- package/src/adapter/webgl-device.ts +40 -4
- package/src/adapter/webgl-presentation-context.ts +93 -0
|
@@ -11,11 +11,9 @@ import {
|
|
|
11
11
|
type Sampler,
|
|
12
12
|
type SamplerProps,
|
|
13
13
|
type CopyExternalImageOptions,
|
|
14
|
-
type CopyImageDataOptions,
|
|
15
14
|
type TextureReadOptions,
|
|
16
15
|
type TextureWriteOptions,
|
|
17
16
|
type TextureFormat,
|
|
18
|
-
type TypedArray,
|
|
19
17
|
Buffer,
|
|
20
18
|
Texture,
|
|
21
19
|
log
|
|
@@ -35,12 +33,12 @@ import {getTextureFormatWebGL} from '../converters/webgl-texture-table';
|
|
|
35
33
|
import {convertSamplerParametersToWebGL} from '../converters/sampler-parameters';
|
|
36
34
|
import {withGLParameters} from '../../context/state-tracker/with-parameters';
|
|
37
35
|
import {WebGLDevice} from '../webgl-device';
|
|
36
|
+
import {WEBGLBuffer} from './webgl-buffer';
|
|
38
37
|
import {WEBGLFramebuffer} from './webgl-framebuffer';
|
|
39
38
|
import {WEBGLSampler} from './webgl-sampler';
|
|
40
39
|
import {WEBGLTextureView} from './webgl-texture-view';
|
|
41
|
-
import {convertDataTypeToGLDataType} from '../converters/webgl-shadertypes';
|
|
42
40
|
import {convertGLDataTypeToDataType} from '../converters/shader-formats';
|
|
43
|
-
import {getTypedArrayConstructor
|
|
41
|
+
import {getTypedArrayConstructor} from '@luma.gl/core';
|
|
44
42
|
|
|
45
43
|
/**
|
|
46
44
|
* WebGL... the texture API from hell... hopefully made simpler
|
|
@@ -77,8 +75,10 @@ export class WEBGLTexture extends Texture {
|
|
|
77
75
|
// state
|
|
78
76
|
/** Texture binding slot - TODO - move to texture view? */
|
|
79
77
|
_textureUnit: number = 0;
|
|
80
|
-
/**
|
|
78
|
+
/** Cached framebuffer reused for color texture readback. */
|
|
81
79
|
_framebuffer: WEBGLFramebuffer | null = null;
|
|
80
|
+
/** Cache key for the currently attached readback subresource `${mipLevel}:${layer}`. */
|
|
81
|
+
_framebufferAttachmentKey: string | null = null;
|
|
82
82
|
|
|
83
83
|
constructor(device: Device, props: TextureProps) {
|
|
84
84
|
// const byteAlignment = this._getRowByteAlignment(props.format, props.width);
|
|
@@ -107,23 +107,31 @@ export class WEBGLTexture extends Texture {
|
|
|
107
107
|
*/
|
|
108
108
|
this.gl.bindTexture(this.glTarget, this.handle);
|
|
109
109
|
const {dimension, width, height, depth, mipLevels, glTarget, glInternalFormat} = this;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
110
|
+
if (!this.compressed) {
|
|
111
|
+
switch (dimension) {
|
|
112
|
+
case '2d':
|
|
113
|
+
case 'cube':
|
|
114
|
+
this.gl.texStorage2D(glTarget, mipLevels, glInternalFormat, width, height);
|
|
115
|
+
break;
|
|
116
|
+
case '2d-array':
|
|
117
|
+
case '3d':
|
|
118
|
+
this.gl.texStorage3D(glTarget, mipLevels, glInternalFormat, width, height, depth);
|
|
119
|
+
break;
|
|
120
|
+
default:
|
|
121
|
+
throw new Error(dimension);
|
|
122
|
+
}
|
|
121
123
|
}
|
|
122
124
|
this.gl.bindTexture(this.glTarget, null);
|
|
123
125
|
|
|
124
126
|
// Set data
|
|
125
127
|
this._initializeData(props.data);
|
|
126
128
|
|
|
129
|
+
if (!this.props.handle) {
|
|
130
|
+
this.trackAllocatedMemory(this.getAllocatedByteLength(), 'Texture');
|
|
131
|
+
} else {
|
|
132
|
+
this.trackReferencedMemory(this.getAllocatedByteLength(), 'Texture');
|
|
133
|
+
}
|
|
134
|
+
|
|
127
135
|
// Set texture sampler parameters
|
|
128
136
|
this.setSampler(this.props.sampler);
|
|
129
137
|
// @ts-ignore TODO - fix types
|
|
@@ -137,10 +145,15 @@ export class WEBGLTexture extends Texture {
|
|
|
137
145
|
// Destroy any cached framebuffer
|
|
138
146
|
this._framebuffer?.destroy();
|
|
139
147
|
this._framebuffer = null;
|
|
148
|
+
this._framebufferAttachmentKey = null;
|
|
140
149
|
|
|
141
|
-
this.gl.deleteTexture(this.handle);
|
|
142
150
|
this.removeStats();
|
|
143
|
-
this.
|
|
151
|
+
if (!this.props.handle) {
|
|
152
|
+
this.gl.deleteTexture(this.handle);
|
|
153
|
+
this.trackDeallocatedMemory('Texture');
|
|
154
|
+
} else {
|
|
155
|
+
this.trackDeallocatedReferencedMemory('Texture');
|
|
156
|
+
}
|
|
144
157
|
// this.handle = null;
|
|
145
158
|
this.destroyed = true;
|
|
146
159
|
}
|
|
@@ -196,103 +209,159 @@ export class WEBGLTexture extends Texture {
|
|
|
196
209
|
return {width: options.width, height: options.height};
|
|
197
210
|
}
|
|
198
211
|
|
|
199
|
-
copyImageData(options_
|
|
200
|
-
|
|
212
|
+
override copyImageData(options_): void {
|
|
213
|
+
super.copyImageData(options_);
|
|
214
|
+
}
|
|
201
215
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Reads a color texture subresource into a GPU buffer using `PIXEL_PACK_BUFFER`.
|
|
218
|
+
*
|
|
219
|
+
* @note Only first-pass color readback is supported. Unsupported formats and aspects throw
|
|
220
|
+
* before any WebGL calls are issued.
|
|
221
|
+
*/
|
|
222
|
+
readBuffer(options: TextureReadOptions = {}, buffer?: Buffer): Buffer {
|
|
223
|
+
const normalizedOptions = this._getSupportedColorReadOptions(options);
|
|
224
|
+
const memoryLayout = this.computeMemoryLayout(normalizedOptions);
|
|
225
|
+
const readBuffer =
|
|
226
|
+
buffer ||
|
|
227
|
+
this.device.createBuffer({
|
|
228
|
+
byteLength: memoryLayout.byteLength,
|
|
229
|
+
usage: Buffer.COPY_DST | Buffer.MAP_READ
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (readBuffer.byteLength < memoryLayout.byteLength) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`${this} readBuffer target is too small (${readBuffer.byteLength} < ${memoryLayout.byteLength})`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const webglBuffer = readBuffer as WEBGLBuffer;
|
|
239
|
+
this.gl.bindBuffer(GL.PIXEL_PACK_BUFFER, webglBuffer.handle);
|
|
240
|
+
try {
|
|
241
|
+
this._readColorTextureLayers(normalizedOptions, memoryLayout, destinationByteOffset => {
|
|
242
|
+
this.gl.readPixels(
|
|
243
|
+
normalizedOptions.x,
|
|
244
|
+
normalizedOptions.y,
|
|
245
|
+
normalizedOptions.width,
|
|
246
|
+
normalizedOptions.height,
|
|
247
|
+
this.glFormat,
|
|
248
|
+
this.glType,
|
|
249
|
+
destinationByteOffset
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
} finally {
|
|
253
|
+
this.gl.bindBuffer(GL.PIXEL_PACK_BUFFER, null);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return readBuffer;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async readDataAsync(options: TextureReadOptions = {}): Promise<ArrayBuffer> {
|
|
260
|
+
const buffer = this.readBuffer(options);
|
|
261
|
+
const data = await buffer.readAsync();
|
|
262
|
+
buffer.destroy();
|
|
263
|
+
return data.buffer as ArrayBuffer;
|
|
264
|
+
}
|
|
206
265
|
|
|
207
|
-
|
|
266
|
+
writeBuffer(buffer: Buffer, options_: TextureWriteOptions = {}) {
|
|
267
|
+
const options = this._normalizeTextureWriteOptions(options_);
|
|
268
|
+
const {width, height, depthOrArrayLayers, mipLevel, byteOffset, x, y, z} = options;
|
|
269
|
+
const {glFormat, glType, compressed} = this;
|
|
208
270
|
const glTarget = getWebGLCubeFaceTarget(this.glTarget, this.dimension, z);
|
|
209
271
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const {bytesPerPixel} = this.device.getTextureFormatInfo(this.format);
|
|
213
|
-
if (bytesPerPixel) {
|
|
214
|
-
if (options.bytesPerRow % bytesPerPixel !== 0) {
|
|
215
|
-
throw new Error(
|
|
216
|
-
`bytesPerRow (${options.bytesPerRow}) must be a multiple of bytesPerPixel (${bytesPerPixel}) for ${this.format}`
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
unpackRowLength = options.bytesPerRow / bytesPerPixel;
|
|
220
|
-
}
|
|
272
|
+
if (compressed) {
|
|
273
|
+
throw new Error('writeBuffer for compressed textures is not implemented in WebGL');
|
|
221
274
|
}
|
|
222
275
|
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
276
|
+
const {bytesPerPixel} = this.device.getTextureFormatInfo(this.format);
|
|
277
|
+
const unpackRowLength = bytesPerPixel ? options.bytesPerRow / bytesPerPixel : undefined;
|
|
278
|
+
const glParameters: GLValueParameters = {
|
|
279
|
+
[GL.UNPACK_ALIGNMENT]: this.byteAlignment,
|
|
280
|
+
...(unpackRowLength !== undefined ? {[GL.UNPACK_ROW_LENGTH]: unpackRowLength} : {}),
|
|
281
|
+
[GL.UNPACK_IMAGE_HEIGHT]: options.rowsPerImage
|
|
282
|
+
};
|
|
230
283
|
|
|
231
284
|
this.gl.bindTexture(this.glTarget, this.handle);
|
|
285
|
+
this.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, buffer.handle);
|
|
232
286
|
|
|
233
287
|
withGLParameters(this.gl, glParameters, () => {
|
|
234
288
|
switch (this.dimension) {
|
|
235
289
|
case '2d':
|
|
236
290
|
case 'cube':
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
291
|
+
this.gl.texSubImage2D(
|
|
292
|
+
glTarget,
|
|
293
|
+
mipLevel,
|
|
294
|
+
x,
|
|
295
|
+
y,
|
|
296
|
+
width,
|
|
297
|
+
height,
|
|
298
|
+
glFormat,
|
|
299
|
+
glType,
|
|
300
|
+
byteOffset
|
|
301
|
+
);
|
|
244
302
|
break;
|
|
245
303
|
case '2d-array':
|
|
246
304
|
case '3d':
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
305
|
+
this.gl.texSubImage3D(
|
|
306
|
+
glTarget,
|
|
307
|
+
mipLevel,
|
|
308
|
+
x,
|
|
309
|
+
y,
|
|
310
|
+
z,
|
|
311
|
+
width,
|
|
312
|
+
height,
|
|
313
|
+
depthOrArrayLayers,
|
|
314
|
+
glFormat,
|
|
315
|
+
glType,
|
|
316
|
+
byteOffset
|
|
317
|
+
);
|
|
254
318
|
break;
|
|
255
319
|
default:
|
|
256
|
-
// Can never happen in WebGL
|
|
257
320
|
}
|
|
258
321
|
});
|
|
259
322
|
|
|
323
|
+
this.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
|
|
260
324
|
this.gl.bindTexture(this.glTarget, null);
|
|
261
325
|
}
|
|
262
326
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
async readDataAsync(options: TextureReadOptions = {}): Promise<ArrayBuffer> {
|
|
268
|
-
return this.readDataSyncWebGL(options);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
writeBuffer(buffer: Buffer, options_: TextureWriteOptions = {}) {}
|
|
272
|
-
|
|
273
|
-
writeData(data: ArrayBuffer | ArrayBufferView, options_: TextureWriteOptions = {}): void {
|
|
327
|
+
writeData(
|
|
328
|
+
data: ArrayBuffer | SharedArrayBuffer | ArrayBufferView,
|
|
329
|
+
options_: TextureWriteOptions = {}
|
|
330
|
+
): void {
|
|
274
331
|
const options = this._normalizeTextureWriteOptions(options_);
|
|
275
332
|
|
|
276
333
|
const typedArray = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
|
|
277
|
-
const {} =
|
|
278
|
-
const {width, height, mipLevel, x, y, z} = options;
|
|
334
|
+
const {width, height, depthOrArrayLayers, mipLevel, x, y, z, byteOffset} = options;
|
|
279
335
|
const {glFormat, glType, compressed} = this;
|
|
280
|
-
const
|
|
281
|
-
const glTarget = getWebGLCubeFaceTarget(this.glTarget, this.dimension, depth);
|
|
336
|
+
const glTarget = getWebGLCubeFaceTarget(this.glTarget, this.dimension, z);
|
|
282
337
|
|
|
283
|
-
|
|
284
|
-
|
|
338
|
+
let unpackRowLength: number | undefined;
|
|
339
|
+
if (!compressed) {
|
|
340
|
+
const {bytesPerPixel} = this.device.getTextureFormatInfo(this.format);
|
|
341
|
+
if (bytesPerPixel) {
|
|
342
|
+
unpackRowLength = options.bytesPerRow / bytesPerPixel;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
285
345
|
|
|
286
346
|
const glParameters: GLValueParameters = !this.compressed
|
|
287
347
|
? {
|
|
288
|
-
|
|
289
|
-
[GL.
|
|
290
|
-
|
|
291
|
-
// [GL.UNPACK_IMAGE_HEIGHT]: rowsPerImage
|
|
348
|
+
[GL.UNPACK_ALIGNMENT]: this.byteAlignment,
|
|
349
|
+
...(unpackRowLength !== undefined ? {[GL.UNPACK_ROW_LENGTH]: unpackRowLength} : {}),
|
|
350
|
+
[GL.UNPACK_IMAGE_HEIGHT]: options.rowsPerImage
|
|
292
351
|
}
|
|
293
352
|
: {};
|
|
353
|
+
const sourceElementOffset = getWebGLTextureSourceElementOffset(typedArray, byteOffset);
|
|
354
|
+
const compressedData = compressed ? getArrayBufferView(typedArray, byteOffset) : typedArray;
|
|
355
|
+
const mipLevelSize = this._getMipLevelSize(mipLevel);
|
|
356
|
+
const isFullMipUpload =
|
|
357
|
+
x === 0 &&
|
|
358
|
+
y === 0 &&
|
|
359
|
+
z === 0 &&
|
|
360
|
+
width === mipLevelSize.width &&
|
|
361
|
+
height === mipLevelSize.height &&
|
|
362
|
+
depthOrArrayLayers === mipLevelSize.depthOrArrayLayers;
|
|
294
363
|
|
|
295
|
-
this.gl.bindTexture(glTarget, this.handle);
|
|
364
|
+
this.gl.bindTexture(this.glTarget, this.handle);
|
|
296
365
|
this.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
|
|
297
366
|
|
|
298
367
|
withGLParameters(this.gl, glParameters, () => {
|
|
@@ -300,21 +369,51 @@ export class WEBGLTexture extends Texture {
|
|
|
300
369
|
case '2d':
|
|
301
370
|
case 'cube':
|
|
302
371
|
if (compressed) {
|
|
303
|
-
|
|
304
|
-
|
|
372
|
+
if (isFullMipUpload) {
|
|
373
|
+
// prettier-ignore
|
|
374
|
+
this.gl.compressedTexImage2D(glTarget, mipLevel, glFormat, width, height, 0, compressedData);
|
|
375
|
+
} else {
|
|
376
|
+
// prettier-ignore
|
|
377
|
+
this.gl.compressedTexSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, compressedData);
|
|
378
|
+
}
|
|
305
379
|
} else {
|
|
306
380
|
// prettier-ignore
|
|
307
|
-
this.gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, typedArray);
|
|
381
|
+
this.gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, typedArray, sourceElementOffset);
|
|
308
382
|
}
|
|
309
383
|
break;
|
|
310
384
|
case '2d-array':
|
|
311
385
|
case '3d':
|
|
312
386
|
if (compressed) {
|
|
313
|
-
|
|
314
|
-
|
|
387
|
+
if (isFullMipUpload) {
|
|
388
|
+
// prettier-ignore
|
|
389
|
+
this.gl.compressedTexImage3D(
|
|
390
|
+
glTarget,
|
|
391
|
+
mipLevel,
|
|
392
|
+
glFormat,
|
|
393
|
+
width,
|
|
394
|
+
height,
|
|
395
|
+
depthOrArrayLayers,
|
|
396
|
+
0,
|
|
397
|
+
compressedData
|
|
398
|
+
);
|
|
399
|
+
} else {
|
|
400
|
+
// prettier-ignore
|
|
401
|
+
this.gl.compressedTexSubImage3D(
|
|
402
|
+
glTarget,
|
|
403
|
+
mipLevel,
|
|
404
|
+
x,
|
|
405
|
+
y,
|
|
406
|
+
z,
|
|
407
|
+
width,
|
|
408
|
+
height,
|
|
409
|
+
depthOrArrayLayers,
|
|
410
|
+
glFormat,
|
|
411
|
+
compressedData
|
|
412
|
+
);
|
|
413
|
+
}
|
|
315
414
|
} else {
|
|
316
415
|
// prettier-ignore
|
|
317
|
-
this.gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height,
|
|
416
|
+
this.gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height, depthOrArrayLayers, glFormat, glType, typedArray, sourceElementOffset);
|
|
318
417
|
}
|
|
319
418
|
break;
|
|
320
419
|
default:
|
|
@@ -322,7 +421,7 @@ export class WEBGLTexture extends Texture {
|
|
|
322
421
|
}
|
|
323
422
|
});
|
|
324
423
|
|
|
325
|
-
this.gl.bindTexture(glTarget, null);
|
|
424
|
+
this.gl.bindTexture(this.glTarget, null);
|
|
326
425
|
}
|
|
327
426
|
|
|
328
427
|
// IMPLEMENTATION SPECIFIC
|
|
@@ -357,57 +456,138 @@ export class WEBGLTexture extends Texture {
|
|
|
357
456
|
// WEBGL SPECIFIC
|
|
358
457
|
|
|
359
458
|
override readDataSyncWebGL(options_: TextureReadOptions = {}): ArrayBuffer {
|
|
360
|
-
const options = this.
|
|
361
|
-
|
|
459
|
+
const options = this._getSupportedColorReadOptions(options_);
|
|
362
460
|
const memoryLayout = this.computeMemoryLayout(options);
|
|
363
461
|
|
|
364
462
|
// const formatInfo = getTextureFormatInfo(format);
|
|
365
463
|
// Allocate pixel array if not already available, using supplied type
|
|
366
464
|
const shaderType = convertGLDataTypeToDataType(this.glType);
|
|
367
|
-
|
|
368
465
|
const ArrayType = getTypedArrayConstructor(shaderType);
|
|
369
|
-
|
|
370
|
-
// TODO - check for composite type (components = 1).
|
|
371
|
-
|
|
372
|
-
const targetArray = new ArrayType(memoryLayout.byteLength) as
|
|
466
|
+
const targetArray = new ArrayType(memoryLayout.byteLength / ArrayType.BYTES_PER_ELEMENT) as
|
|
373
467
|
| Uint8Array
|
|
374
468
|
| Uint16Array
|
|
375
|
-
| Float32Array
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
469
|
+
| Float32Array
|
|
470
|
+
| Int8Array
|
|
471
|
+
| Int16Array
|
|
472
|
+
| Int32Array
|
|
473
|
+
| Uint32Array;
|
|
474
|
+
|
|
475
|
+
this._readColorTextureLayers(options, memoryLayout, destinationByteOffset => {
|
|
476
|
+
const layerView = new ArrayType(
|
|
477
|
+
targetArray.buffer,
|
|
478
|
+
targetArray.byteOffset + destinationByteOffset,
|
|
479
|
+
memoryLayout.bytesPerImage / ArrayType.BYTES_PER_ELEMENT
|
|
480
|
+
);
|
|
481
|
+
this.gl.readPixels(
|
|
482
|
+
options.x,
|
|
483
|
+
options.y,
|
|
484
|
+
options.width,
|
|
485
|
+
options.height,
|
|
486
|
+
this.glFormat,
|
|
487
|
+
this.glType,
|
|
488
|
+
layerView
|
|
489
|
+
);
|
|
490
|
+
});
|
|
380
491
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
// Querying is possible but expensive:
|
|
384
|
-
// const {device} = framebuffer;
|
|
385
|
-
// texture.glReadFormat ||= gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
|
|
386
|
-
// texture.glReadType ||= gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
|
|
387
|
-
// console.log('params', device.getGLKey(texture.glReadFormat), device.getGLKey(texture.glReadType));
|
|
492
|
+
return targetArray.buffer as ArrayBuffer;
|
|
493
|
+
}
|
|
388
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Iterates the requested mip/layer/slice range, reattaching the cached read framebuffer as
|
|
497
|
+
* needed before delegating the actual `readPixels()` call to the supplied callback.
|
|
498
|
+
*/
|
|
499
|
+
private _readColorTextureLayers(
|
|
500
|
+
options: Required<TextureReadOptions>,
|
|
501
|
+
memoryLayout: ReturnType<Texture['computeMemoryLayout']>,
|
|
502
|
+
readLayer: (destinationByteOffset: number) => void
|
|
503
|
+
): void {
|
|
389
504
|
const framebuffer = this._getFramebuffer();
|
|
390
|
-
|
|
391
|
-
|
|
505
|
+
const packRowLength = memoryLayout.bytesPerRow / memoryLayout.bytesPerPixel;
|
|
506
|
+
const glParameters: GLValueParameters = {
|
|
507
|
+
[GL.PACK_ALIGNMENT]: this.byteAlignment,
|
|
508
|
+
...(packRowLength !== options.width ? {[GL.PACK_ROW_LENGTH]: packRowLength} : {})
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// Note: luma.gl overrides bindFramebuffer so that we can reliably restore the previous framebuffer.
|
|
512
|
+
const prevReadBuffer = this.gl.getParameter(GL.READ_BUFFER) as GL;
|
|
392
513
|
const prevHandle = this.gl.bindFramebuffer(
|
|
393
514
|
GL.FRAMEBUFFER,
|
|
394
515
|
framebuffer.handle
|
|
395
516
|
) as unknown as WebGLFramebuffer | null;
|
|
396
517
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
518
|
+
try {
|
|
519
|
+
this.gl.readBuffer(GL.COLOR_ATTACHMENT0);
|
|
520
|
+
withGLParameters(this.gl, glParameters, () => {
|
|
521
|
+
for (let layerIndex = 0; layerIndex < options.depthOrArrayLayers; layerIndex++) {
|
|
522
|
+
this._attachReadSubresource(framebuffer, options.mipLevel, options.z + layerIndex);
|
|
523
|
+
readLayer(layerIndex * memoryLayout.bytesPerImage);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
} finally {
|
|
527
|
+
this.gl.bindFramebuffer(GL.FRAMEBUFFER, prevHandle || null);
|
|
528
|
+
this.gl.readBuffer(prevReadBuffer);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
409
531
|
|
|
410
|
-
|
|
532
|
+
/**
|
|
533
|
+
* Attaches a single color subresource to the cached read framebuffer.
|
|
534
|
+
*
|
|
535
|
+
* @note Repeated attachments of the same `(mipLevel, layer)` tuple are skipped.
|
|
536
|
+
*/
|
|
537
|
+
private _attachReadSubresource(
|
|
538
|
+
framebuffer: WEBGLFramebuffer,
|
|
539
|
+
mipLevel: number,
|
|
540
|
+
layer: number
|
|
541
|
+
): void {
|
|
542
|
+
const attachmentKey = `${mipLevel}:${layer}`;
|
|
543
|
+
if (this._framebufferAttachmentKey === attachmentKey) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
switch (this.dimension) {
|
|
548
|
+
case '2d':
|
|
549
|
+
this.gl.framebufferTexture2D(
|
|
550
|
+
GL.FRAMEBUFFER,
|
|
551
|
+
GL.COLOR_ATTACHMENT0,
|
|
552
|
+
GL.TEXTURE_2D,
|
|
553
|
+
this.handle,
|
|
554
|
+
mipLevel
|
|
555
|
+
);
|
|
556
|
+
break;
|
|
557
|
+
|
|
558
|
+
case 'cube':
|
|
559
|
+
this.gl.framebufferTexture2D(
|
|
560
|
+
GL.FRAMEBUFFER,
|
|
561
|
+
GL.COLOR_ATTACHMENT0,
|
|
562
|
+
getWebGLCubeFaceTarget(this.glTarget, this.dimension, layer),
|
|
563
|
+
this.handle,
|
|
564
|
+
mipLevel
|
|
565
|
+
);
|
|
566
|
+
break;
|
|
567
|
+
|
|
568
|
+
case '2d-array':
|
|
569
|
+
case '3d':
|
|
570
|
+
this.gl.framebufferTextureLayer(
|
|
571
|
+
GL.FRAMEBUFFER,
|
|
572
|
+
GL.COLOR_ATTACHMENT0,
|
|
573
|
+
this.handle,
|
|
574
|
+
mipLevel,
|
|
575
|
+
layer
|
|
576
|
+
);
|
|
577
|
+
break;
|
|
578
|
+
|
|
579
|
+
default:
|
|
580
|
+
throw new Error(`${this} color readback does not support ${this.dimension} textures`);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (this.device.props.debug) {
|
|
584
|
+
const status = Number(this.gl.checkFramebufferStatus(GL.FRAMEBUFFER));
|
|
585
|
+
if (status !== Number(GL.FRAMEBUFFER_COMPLETE)) {
|
|
586
|
+
throw new Error(`${framebuffer} incomplete for ${this} readback (${status})`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
this._framebufferAttachmentKey = attachmentKey;
|
|
411
591
|
}
|
|
412
592
|
|
|
413
593
|
/**
|
|
@@ -514,6 +694,31 @@ export class WEBGLTexture extends Texture {
|
|
|
514
694
|
}
|
|
515
695
|
}
|
|
516
696
|
|
|
697
|
+
function getArrayBufferView(typedArray: ArrayBufferView, byteOffset = 0): ArrayBufferView {
|
|
698
|
+
if (!byteOffset) {
|
|
699
|
+
return typedArray;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return new typedArray.constructor(
|
|
703
|
+
typedArray.buffer,
|
|
704
|
+
typedArray.byteOffset + byteOffset,
|
|
705
|
+
(typedArray.byteLength - byteOffset) / typedArray.BYTES_PER_ELEMENT
|
|
706
|
+
) as ArrayBufferView;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function getWebGLTextureSourceElementOffset(
|
|
710
|
+
typedArray: ArrayBufferView,
|
|
711
|
+
byteOffset: number
|
|
712
|
+
): number {
|
|
713
|
+
if (byteOffset % typedArray.BYTES_PER_ELEMENT !== 0) {
|
|
714
|
+
throw new Error(
|
|
715
|
+
`Texture byteOffset ${byteOffset} must align to typed array element size ${typedArray.BYTES_PER_ELEMENT}`
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return byteOffset / typedArray.BYTES_PER_ELEMENT;
|
|
720
|
+
}
|
|
721
|
+
|
|
517
722
|
// INTERNAL HELPERS
|
|
518
723
|
|
|
519
724
|
/** Convert a WebGPU style texture constant to a WebGL style texture constant */
|