@luma.gl/webgl 9.0.11 → 9.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/LICENSE +34 -0
  2. package/dist/adapter/converters/device-parameters.d.ts.map +1 -1
  3. package/dist/adapter/converters/device-parameters.js +12 -1
  4. package/dist/adapter/converters/texture-formats.d.ts +22 -16
  5. package/dist/adapter/converters/texture-formats.d.ts.map +1 -1
  6. package/dist/adapter/converters/texture-formats.js +31 -32
  7. package/dist/adapter/device-helpers/webgl-device-features.js +1 -1
  8. package/dist/adapter/device-helpers/webgl-device-limits.js +1 -1
  9. package/dist/adapter/helpers/webgl-texture-utils.d.ts +300 -0
  10. package/dist/adapter/helpers/webgl-texture-utils.d.ts.map +1 -0
  11. package/dist/adapter/helpers/webgl-texture-utils.js +368 -0
  12. package/dist/adapter/helpers/webgl-topology-utils.d.ts.map +1 -1
  13. package/dist/adapter/helpers/webgl-topology-utils.js +0 -4
  14. package/dist/adapter/resources/webgl-buffer.d.ts.map +1 -1
  15. package/dist/adapter/resources/webgl-buffer.js +2 -2
  16. package/dist/adapter/resources/webgl-command-buffer.d.ts.map +1 -1
  17. package/dist/adapter/resources/webgl-command-buffer.js +6 -9
  18. package/dist/adapter/resources/webgl-framebuffer.d.ts +32 -5
  19. package/dist/adapter/resources/webgl-framebuffer.d.ts.map +1 -1
  20. package/dist/adapter/resources/webgl-framebuffer.js +42 -60
  21. package/dist/adapter/resources/webgl-render-pass.d.ts +3 -2
  22. package/dist/adapter/resources/webgl-render-pass.d.ts.map +1 -1
  23. package/dist/adapter/resources/webgl-render-pass.js +1 -3
  24. package/dist/adapter/resources/webgl-render-pipeline.d.ts.map +1 -1
  25. package/dist/adapter/resources/webgl-render-pipeline.js +45 -20
  26. package/dist/adapter/resources/webgl-shader.d.ts.map +1 -1
  27. package/dist/adapter/resources/webgl-shader.js +2 -2
  28. package/dist/adapter/resources/webgl-texture-view.d.ts +1 -1
  29. package/dist/adapter/resources/webgl-texture-view.d.ts.map +1 -1
  30. package/dist/adapter/resources/webgl-texture-view.js +1 -1
  31. package/dist/adapter/resources/webgl-texture.d.ts +58 -172
  32. package/dist/adapter/resources/webgl-texture.d.ts.map +1 -1
  33. package/dist/adapter/resources/webgl-texture.js +402 -510
  34. package/dist/adapter/resources/webgl-vertex-array.d.ts +3 -2
  35. package/dist/adapter/resources/webgl-vertex-array.d.ts.map +1 -1
  36. package/dist/adapter/resources/webgl-vertex-array.js +2 -2
  37. package/dist/adapter/webgl-canvas-context.d.ts +3 -1
  38. package/dist/adapter/webgl-canvas-context.d.ts.map +1 -1
  39. package/dist/adapter/webgl-canvas-context.js +2 -0
  40. package/dist/adapter/webgl-device.d.ts +4 -3
  41. package/dist/adapter/webgl-device.d.ts.map +1 -1
  42. package/dist/adapter/webgl-device.js +6 -5
  43. package/dist/classic/accessor.d.ts +22 -1
  44. package/dist/classic/accessor.d.ts.map +1 -1
  45. package/dist/classic/accessor.js +1 -9
  46. package/dist/classic/clear.d.ts.map +1 -1
  47. package/dist/classic/clear.js +2 -5
  48. package/dist/classic/copy-and-blit.d.ts +2 -1
  49. package/dist/classic/copy-and-blit.d.ts.map +1 -1
  50. package/dist/classic/copy-and-blit.js +11 -9
  51. package/dist/classic/format-utils.d.ts.map +1 -1
  52. package/dist/classic/format-utils.js +0 -3
  53. package/dist/classic/typed-array-utils.d.ts +1 -1
  54. package/dist/classic/typed-array-utils.d.ts.map +1 -1
  55. package/dist/context/debug/spector.d.ts.map +1 -1
  56. package/dist/context/debug/spector.js +2 -1
  57. package/dist/context/debug/webgl-developer-tools.d.ts +1 -1
  58. package/dist/context/debug/webgl-developer-tools.d.ts.map +1 -1
  59. package/dist/context/debug/webgl-developer-tools.js +2 -5
  60. package/dist/context/parameters/webgl-parameter-tables.js +1 -1
  61. package/dist/context/state-tracker/track-context-state.d.ts.map +1 -1
  62. package/dist/context/state-tracker/track-context-state.js +5 -6
  63. package/dist/dist.dev.js +870 -1261
  64. package/dist/dist.min.js +2 -2
  65. package/dist/index.cjs +859 -1137
  66. package/dist/index.cjs.map +4 -4
  67. package/dist/index.d.ts +0 -4
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +0 -3
  70. package/dist/utils/fill-array.d.ts +8 -0
  71. package/dist/utils/fill-array.d.ts.map +1 -0
  72. package/dist/utils/fill-array.js +26 -0
  73. package/dist/utils/load-script.d.ts +8 -0
  74. package/dist/utils/load-script.d.ts.map +1 -0
  75. package/dist/utils/load-script.js +26 -0
  76. package/dist/utils/split-uniforms-and-bindings.d.ts +9 -0
  77. package/dist/utils/split-uniforms-and-bindings.d.ts.map +1 -0
  78. package/dist/utils/split-uniforms-and-bindings.js +20 -0
  79. package/package.json +6 -5
  80. package/src/adapter/converters/device-parameters.ts +13 -1
  81. package/src/adapter/converters/texture-formats.ts +45 -42
  82. package/src/adapter/device-helpers/webgl-device-features.ts +1 -1
  83. package/src/adapter/device-helpers/webgl-device-limits.ts +1 -1
  84. package/src/adapter/helpers/webgl-texture-utils.ts +481 -0
  85. package/src/adapter/helpers/webgl-topology-utils.ts +0 -4
  86. package/src/adapter/resources/webgl-buffer.ts +2 -2
  87. package/src/adapter/resources/webgl-command-buffer.ts +8 -10
  88. package/src/adapter/resources/webgl-framebuffer.ts +22 -56
  89. package/src/adapter/resources/webgl-render-pass.ts +4 -5
  90. package/src/adapter/resources/webgl-render-pipeline.ts +48 -23
  91. package/src/adapter/resources/webgl-shader.ts +3 -3
  92. package/src/adapter/resources/webgl-texture-view.ts +1 -3
  93. package/src/adapter/resources/webgl-texture.ts +432 -784
  94. package/src/adapter/resources/webgl-vertex-array.ts +8 -7
  95. package/src/adapter/webgl-canvas-context.ts +4 -1
  96. package/src/adapter/webgl-device.ts +10 -18
  97. package/src/classic/accessor.ts +31 -11
  98. package/src/classic/clear.ts +3 -6
  99. package/src/classic/copy-and-blit.ts +19 -19
  100. package/src/classic/format-utils.ts +0 -3
  101. package/src/classic/typed-array-utils.ts +1 -1
  102. package/src/context/debug/spector.ts +2 -1
  103. package/src/context/debug/webgl-developer-tools.ts +3 -7
  104. package/src/context/parameters/webgl-parameter-tables.ts +3 -3
  105. package/src/context/state-tracker/track-context-state.ts +5 -6
  106. package/src/index.ts +0 -6
  107. package/src/utils/fill-array.ts +35 -0
  108. package/src/utils/load-script.ts +30 -0
  109. package/src/utils/split-uniforms-and-bindings.ts +31 -0
  110. package/dist/adapter/objects/constants-to-keys.d.ts +0 -3
  111. package/dist/adapter/objects/constants-to-keys.d.ts.map +0 -1
  112. package/dist/adapter/objects/constants-to-keys.js +0 -22
  113. package/dist/adapter/objects/webgl-renderbuffer.d.ts +0 -43
  114. package/dist/adapter/objects/webgl-renderbuffer.d.ts.map +0 -1
  115. package/dist/adapter/objects/webgl-renderbuffer.js +0 -95
  116. package/dist/adapter/objects/webgl-resource.d.ts +0 -32
  117. package/dist/adapter/objects/webgl-resource.d.ts.map +0 -1
  118. package/dist/adapter/objects/webgl-resource.js +0 -114
  119. package/src/adapter/objects/constants-to-keys.ts +0 -27
  120. package/src/adapter/objects/webgl-renderbuffer.ts +0 -132
  121. package/src/adapter/objects/webgl-resource.ts +0 -183
@@ -2,355 +2,256 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- // Texture class.
6
- // @todo
7
- // - [ ] cube texture init params
5
+ // @todo texture refactor
6
+ // - [ ] cube texture init params P1
7
+ // - [ ] 3d texture init params P1
8
+ // - [ ] GPU memory tracking
9
+ // - [ ] raw data inputs
8
10
  // - [ ] video (external) textures
9
11
 
10
- import {
12
+ // import {TypedArray} from '@math.gl/types';
13
+ import type {
11
14
  Device,
12
15
  TextureProps,
13
16
  TextureViewProps,
14
17
  Sampler,
15
18
  SamplerProps,
16
19
  SamplerParameters,
17
- TypedArray
20
+ // TextureFormat,
21
+ TextureCubeFace,
22
+ ExternalImage,
23
+ TextureLevelData,
24
+ Texture1DData,
25
+ Texture2DData,
26
+ Texture3DData,
27
+ TextureCubeData,
28
+ TextureArrayData,
29
+ TextureCubeArrayData
18
30
  } from '@luma.gl/core';
19
- import {Texture, log, assert, loadImage, isObjectEmpty} from '@luma.gl/core';
20
- import {GL, GLSamplerParameters} from '@luma.gl/constants';
21
- import {withGLParameters} from '../../context/state-tracker/with-parameters';
31
+ // import {decodeTextureFormat} from '@luma.gl/core';
32
+ // import {Buffer, Texture, log} from '@luma.gl/core';
33
+ import {Texture, log} from '@luma.gl/core';
22
34
  import {
23
- convertTextureFormatToGL,
24
- getWebGLTextureParameters,
25
- getTextureFormatBytesPerPixel
26
- } from '../converters/texture-formats';
35
+ GL,
36
+ GLPixelType,
37
+ GLSamplerParameters,
38
+ GLTexelDataFormat,
39
+ GLTextureTarget
40
+ } from '@luma.gl/constants';
41
+ // import {GLPixelDataType} from '@luma.gl/constants';
42
+ import {withGLParameters} from '../../context/state-tracker/with-parameters';
43
+ // getTextureFormatBytesPerPixel
44
+ import {getTextureFormatWebGL} from '../converters/texture-formats';
27
45
  import {convertSamplerParametersToWebGL} from '../converters/sampler-parameters';
28
46
  import {WebGLDevice} from '../webgl-device';
29
- import {WEBGLBuffer} from './webgl-buffer';
47
+ // import {WEBGLBuffer} from './webgl-buffer';
30
48
  import {WEBGLSampler} from './webgl-sampler';
31
49
  import {WEBGLTextureView} from './webgl-texture-view';
32
50
 
33
- export type WEBGLTextureProps = TextureProps & {
34
- /** @deprecated use props.sampler */
35
- parameters?: Record<number, number>;
36
- /** @deprecated use props.data */
37
- pixels?: any;
38
- /** @deprecated use props.format */
39
- dataFormat?: number | null;
40
- /** @deprecated rarely supported */
41
- border?: number;
42
- /** @deprecated WebGL only. */
43
- pixelStore?: object;
44
- /** @deprecated WebGL only. */
45
- textureUnit?: number;
46
- /** @deprecated WebGL only. Use dimension. */
47
- target?: number;
48
- };
49
-
50
- export const DEFAULT_WEBGL_TEXTURE_PROPS = {
51
- // deprecated
52
- parameters: {},
53
- pixelStore: {},
54
- pixels: null,
55
- border: 0,
56
- dataFormat: undefined!,
57
- textureUnit: undefined!,
58
- target: undefined!
59
- };
60
-
61
- export type TextureSourceData =
62
- | TypedArray
63
- | ImageData
64
- | HTMLImageElement
65
- | HTMLCanvasElement
66
- | ImageBitmap
67
- | HTMLVideoElement;
68
-
69
- type SetImageDataOptions = {
70
- target?: number;
71
- level?: number;
72
- dataFormat?: any;
73
- width?: number;
74
- height?: number;
75
- depth?: number;
76
- glFormat?: GL;
77
- type?: any;
78
- offset?: number;
79
- data: any; // TextureSourceData;
80
- compressed?: boolean;
81
- parameters?: Record<GL, any>;
82
- /** @deprecated */
83
- pixels?: any;
84
- };
51
+ // import type {WebGLSetTextureOptions, WebGLCopyTextureOptions} from '../helpers/webgl-texture-utils';
52
+ import {
53
+ initializeTextureStorage,
54
+ // clearMipLevel,
55
+ copyCPUImageToMipLevel,
56
+ copyCPUDataToMipLevel,
57
+ // copyGPUBufferToMipLevel,
58
+ getWebGLTextureTarget
59
+ } from '../helpers/webgl-texture-utils';
60
+
61
+ // PORTABLE HELPERS (Move to methods on Texture?)
85
62
 
86
63
  /**
87
- * @param {*} pixels, data -
88
- * null - create empty texture of specified format
89
- * Typed array - init from image data in typed array
90
- * Buffer|WebGLBuffer - (WEBGL2) init from image data in WebGLBuffer
91
- * HTMLImageElement|Image - Inits with content of image. Auto width/height
92
- * HTMLCanvasElement - Inits with contents of canvas. Auto width/height
93
- * HTMLVideoElement - Creates video texture. Auto width/height
94
- *
95
- * @param x - xOffset from where texture to be updated
96
- * @param y - yOffset from where texture to be updated
97
- * @param width - width of the sub image to be updated
98
- * @param height - height of the sub image to be updated
99
- * @param level - mip level to be updated
100
- * @param {GLenum} format - internal format of image data.
101
- * @param {GLenum} type
102
- * - format of array (autodetect from type) or
103
- * - (WEBGL2) format of buffer or ArrayBufferView
104
- * @param {GLenum} dataFormat - format of image data.
105
- * @param {Number} offset - (WEBGL2) offset from start of buffer
106
- * @parameters - temporary settings to be applied, can be used to supply pixel store settings.
64
+ * Normalize TextureData to an array of TextureLevelData / ExternalImages
65
+ * @param data
66
+ * @param options
67
+ * @returns array of TextureLevelData / ExternalImages
107
68
  */
108
- type SetSubImageDataOptions = {
109
- target?: number;
110
- level?: number;
111
- dataFormat?: any;
112
- width?: number;
113
- height?: number;
114
- depth?: number;
115
- glFormat?: any;
116
- type?: any;
117
- offset?: number;
118
- data: any;
119
- parameters?: Record<GL, any>;
120
- compressed?: boolean;
121
- x?: number;
122
- y?: number;
123
- /** @deprecated */
124
- pixels?: any;
125
- };
126
-
127
- type SetImageData3DOptions = {
128
- level?: number;
129
- dataFormat?: any;
130
- width?: number;
131
- height?: number;
132
- depth?: number;
133
- format?: any;
134
- type?: any;
135
- offset?: number;
136
- data: any;
137
- parameters?: Record<GL, any>;
138
- };
139
-
140
- // Polyfill
141
- export class WEBGLTexture extends Texture<WEBGLTextureProps> {
142
- // TODO - remove?
143
- static FACES: number[] = [
144
- GL.TEXTURE_CUBE_MAP_POSITIVE_X,
145
- GL.TEXTURE_CUBE_MAP_NEGATIVE_X,
146
- GL.TEXTURE_CUBE_MAP_POSITIVE_Y,
147
- GL.TEXTURE_CUBE_MAP_NEGATIVE_Y,
148
- GL.TEXTURE_CUBE_MAP_POSITIVE_Z,
149
- GL.TEXTURE_CUBE_MAP_NEGATIVE_Z
150
- ];
69
+ function normalizeTextureData(
70
+ data: Texture2DData,
71
+ options: {width: number; height: number; depth: number}
72
+ ): (TextureLevelData | ExternalImage)[] {
73
+ let lodArray: (TextureLevelData | ExternalImage)[];
74
+ if (ArrayBuffer.isView(data)) {
75
+ lodArray = [
76
+ {
77
+ // ts-expect-error does data really need to be Uint8ClampedArray?
78
+ data,
79
+ width: options.width,
80
+ height: options.height
81
+ // depth: options.depth
82
+ }
83
+ ];
84
+ } else if (!Array.isArray(data)) {
85
+ lodArray = [data];
86
+ } else {
87
+ lodArray = data;
88
+ }
89
+ return lodArray;
90
+ }
151
91
 
92
+ /**
93
+ * WebGL... the texture API from hell... hopefully made simpler
94
+ */
95
+ export class WEBGLTexture extends Texture {
152
96
  readonly MAX_ATTRIBUTES: number;
153
97
  readonly device: WebGLDevice;
154
98
  readonly gl: WebGL2RenderingContext;
155
- readonly handle: WebGLTexture;
99
+ handle: WebGLTexture;
156
100
 
157
- // (TODO - currently unused in WebGL, but WebGL 2 does support sampler objects) */
158
- sampler: WEBGLSampler = undefined;
101
+ sampler: WEBGLSampler = undefined; // TODO - currently unused in WebGL. Create dummy sampler?
102
+ view: WEBGLTextureView = undefined; // TODO - currently unused in WebGL. Create dummy view?
159
103
 
160
- view: WEBGLTextureView = undefined;
161
-
162
- // data;
163
-
164
- glFormat: GL = undefined;
165
- type: GL = undefined;
166
- dataFormat: GL = undefined;
167
- mipmaps: boolean = undefined;
104
+ mipmaps: boolean = false;
168
105
 
169
106
  /**
170
107
  * @note `target` cannot be modified by bind:
171
108
  * textures are special because when you first bind them to a target,
172
- * they get special information. When you first bind a texture as a
173
- * GL_TEXTURE_2D, you are saying that this texture is a 2D texture.
109
+ * When you first bind a texture as a GL_TEXTURE_2D, you are saying that this texture is a 2D texture.
174
110
  * And it will always be a 2D texture; this state cannot be changed ever.
175
111
  * A texture that was first bound as a GL_TEXTURE_2D, must always be bound as a GL_TEXTURE_2D;
176
112
  * attempting to bind it as GL_TEXTURE_3D will give rise to a run-time error
177
- * */
178
- target: GL;
179
- textureUnit: number = undefined;
180
-
181
- /**
182
- * Program.draw() checks the loaded flag of all textures to avoid
183
- * Textures that are still loading from promises
184
- * Set to true as soon as texture has been initialized with valid data
185
113
  */
186
- loaded: boolean = false;
114
+ glTarget: GLTextureTarget;
115
+
116
+ // Texture type
117
+
118
+ /** The WebGL format - essentially channel structure */
119
+ glFormat: GLTexelDataFormat;
120
+ /** The WebGL data format - the type of each channel */
121
+ glType: GLPixelType;
122
+ /** The WebGL constant corresponding to the WebGPU style constant in format */
123
+ glInternalFormat: GL;
124
+ /** Whether the internal format is compressed */
125
+ compressed: boolean;
126
+
127
+ // data;
128
+ // inherited props
129
+ // dimension: ...
130
+ // format: GLTextureTarget;
131
+ // width: number = undefined;
132
+ // height: number = undefined;
133
+ // depth: number = undefined;
134
+
135
+ // state
136
+ /** Texture binding slot */
137
+ textureUnit: number = 0;
138
+ /** For automatically updating video */
187
139
  _video: {
188
140
  video: HTMLVideoElement;
189
141
  parameters: any;
190
142
  lastTime: number;
191
- };
143
+ } | null = null;
192
144
 
193
- constructor(device: Device, props: WEBGLTextureProps) {
194
- super(device, {...DEFAULT_WEBGL_TEXTURE_PROPS, format: 'rgba8unorm', ...props});
145
+ constructor(device: Device, props: TextureProps) {
146
+ // Note: Clear out `props.data` so that we don't hold a reference to any big memory chunks
147
+ super(device, {...Texture.defaultProps, ...props, data: undefined!});
195
148
 
196
149
  this.device = device as WebGLDevice;
197
150
  this.gl = this.device.gl;
198
- this.handle = this.props.handle || this.gl.createTexture();
199
- this.device.setSpectorMetadata(this.handle, {...this.props, data: typeof this.props.data}); // {name: this.props.id};
200
151
 
201
- this.glFormat = GL.RGBA;
202
- this.target = getWebGLTextureTarget(this.props);
152
+ // Note: In WebGL the texture target defines the type of texture on first bind.
153
+ this.glTarget = getWebGLTextureTarget(this.props.dimension);
203
154
 
204
- // Program.draw() checks the loaded flag of all textures
205
- this.loaded = false;
155
+ // The target format of this texture
156
+ const format = getTextureFormatWebGL(this.props.format);
157
+ this.glInternalFormat = format.internalFormat;
158
+ this.glFormat = format.format;
159
+ this.glType = format.type;
160
+ this.compressed = format.compressed;
206
161
 
207
- // Signature: new Texture2D(gl, {data: url})
208
- if (typeof this.props?.data === 'string') {
209
- Object.assign(this.props, {data: loadImage(this.props.data)});
162
+ if (
163
+ typeof HTMLVideoElement !== 'undefined' &&
164
+ props.data instanceof HTMLVideoElement &&
165
+ // @ts-expect-error
166
+ props.data.readyState < HTMLVideoElement.HAVE_METADATA
167
+ ) {
168
+ const video = props.data;
169
+ this._video = null; // Declare member before the object is sealed
170
+ video.addEventListener('loadeddata', () => this.initialize(props));
210
171
  }
211
172
 
212
- this.initialize(this.props);
173
+ // We removed data, we need to add it again.
174
+ // @ts-expect-error
175
+ this.initialize({...this.props, data: props.data});
213
176
 
214
177
  Object.seal(this);
215
178
  }
216
179
 
217
- override destroy(): void {
218
- if (this.handle) {
219
- this.gl.deleteTexture(this.handle);
220
- this.removeStats();
221
- this.trackDeallocatedMemory('Texture');
222
- // this.handle = null;
223
- this.destroyed = true;
224
- }
225
- }
226
-
227
- override toString(): string {
228
- return `Texture(${this.id},${this.width}x${this.height})`;
229
- }
230
-
231
- createView(props: TextureViewProps): WEBGLTextureView {
232
- return new WEBGLTextureView(this.device, {...props, texture: this});
233
- }
234
-
180
+ /**
181
+ * Initialize texture with supplied props
182
+ */
235
183
  // eslint-disable-next-line max-statements
236
- initialize(props: WEBGLTextureProps = {}): this {
237
- // Cube textures
238
- if (this.props.dimension === 'cube') {
239
- return this.initializeCube(props);
240
- }
241
-
242
- let data = props.data;
243
-
244
- if (data instanceof Promise) {
245
- data.then(resolvedImageData =>
246
- this.initialize(
247
- Object.assign({}, props, {
248
- pixels: resolvedImageData,
249
- data: resolvedImageData
250
- })
251
- )
252
- );
253
- return this;
254
- }
184
+ initialize(props: TextureProps = {}): void {
185
+ this.handle = this.props.handle || this.gl.createTexture();
186
+ this.device.setSpectorMetadata(this.handle, {...this.props, data: typeof this.props.data});
255
187
 
256
- const isVideo = typeof HTMLVideoElement !== 'undefined' && data instanceof HTMLVideoElement;
257
- // @ts-expect-error
258
- if (isVideo && data.readyState < HTMLVideoElement.HAVE_METADATA) {
259
- this._video = null; // Declare member before the object is sealed
260
- // @ts-expect-error
261
- data.addEventListener('loadeddata', () => this.initialize(props));
262
- return this;
263
- }
188
+ const data = props.data;
264
189
 
265
- const {parameters = {} as Record<GL, any>} = props;
190
+ // const {parameters = {} as Record<GL, any>} = props;
266
191
 
267
- const {pixels = null, pixelStore = {}, textureUnit = undefined, mipmaps = true} = props;
192
+ let {width, height} = props;
268
193
 
269
- // pixels variable is for API compatibility purpose
270
- if (!data) {
271
- // TODO - This looks backwards? Commenting out for now until we decide
272
- // which prop to use
273
- // log.deprecated('data', 'pixels')();
274
- data = pixels;
194
+ if (!width || !height) {
195
+ const textureSize = this.getTextureDataSize(data);
196
+ width = textureSize?.width || 1;
197
+ height = textureSize?.height || 1;
275
198
  }
276
199
 
277
- let {width, height, dataFormat, type, compressed = false} = props;
278
- const {depth = 0} = props;
279
-
280
- const glFormat = convertTextureFormatToGL(props.format);
281
-
282
- // Deduce width and height
283
- ({width, height, compressed, dataFormat, type} = this._deduceParameters({
284
- format: props.format,
285
- type,
286
- dataFormat,
287
- compressed,
288
- data,
289
- width,
290
- height
291
- }));
292
-
293
200
  // Store opts for accessors
294
201
  this.width = width;
295
202
  this.height = height;
296
- // this.depth = depth;
297
- this.glFormat = glFormat;
298
- this.type = type;
299
- this.dataFormat = dataFormat;
300
- this.textureUnit = textureUnit;
301
-
302
- if (Number.isFinite(this.textureUnit)) {
303
- this.gl.activeTexture(GL.TEXTURE0 + this.textureUnit);
304
- this.gl.bindTexture(this.target, this.handle);
305
- }
306
-
307
- this.mipmaps = mipmaps;
308
-
309
- this.setImageData({
310
- data,
311
- width,
312
- height,
313
- depth,
314
- format: glFormat,
315
- type,
316
- dataFormat,
317
- // @ts-expect-error
318
- parameters: pixelStore,
319
- compressed
320
- });
203
+ this.depth = props.depth;
321
204
 
322
205
  // Set texture sampler parameters
323
206
  this.setSampler(props.sampler);
324
- this._setSamplerParameters(parameters);
325
-
326
- this.view = this.createView({...this.props, mipLevelCount: 1, arrayLayerCount: 1});
207
+ // @ts-ignore
208
+ this.view = new WEBGLTextureView(this.device, {...this.props, texture: this});
327
209
 
328
- if (mipmaps && this.device.isTextureFormatFilterable(props.format)) {
329
- this.generateMipmap();
210
+ this.bind();
211
+ if (!this.props.data) {
212
+ initializeTextureStorage(this.gl, this.mipLevels, this);
330
213
  }
331
214
 
332
- if (isVideo) {
333
- this._video = {
334
- video: data as HTMLVideoElement,
335
- parameters,
215
+ if (props.data) {
216
+ // prettier-ignore
217
+ switch (props.dimension) {
218
+ case '1d': this.setTexture1DData(props.data); break;
219
+ case '2d': this.setTexture2DData(props.data); break;
220
+ case '3d': this.setTexture3DData(props.data); break;
221
+ case 'cube': this.setTextureCubeData(props.data); break;
222
+ case '2d-array': this.setTextureArrayData(props.data); break;
223
+ case 'cube-array': this.setTextureCubeArrayData(props.data); break;
336
224
  // @ts-expect-error
337
- lastTime: data.readyState >= HTMLVideoElement.HAVE_CURRENT_DATA ? data.currentTime : -1
338
- };
225
+ default: throw new Error(props.dimension);
226
+ }
339
227
  }
340
228
 
341
- return this;
229
+ this.mipmaps = Boolean(props.mipmaps);
230
+
231
+ if (this.mipmaps) {
232
+ this.generateMipmap();
233
+ }
234
+
235
+ // if (isVideo) {
236
+ // this._video = {
237
+ // video: data,
238
+ // // TODO - should we be using the sampler parameters here?
239
+ // parameters: {},
240
+ // // @ts-expect-error HTMLVideoElement.HAVE_CURRENT_DATA is not declared
241
+ // lastTime: data.readyState >= HTMLVideoElement.HAVE_CURRENT_DATA ? data.currentTime : -1
242
+ // };
243
+ // }
342
244
  }
343
245
 
344
- initializeCube(props?: WEBGLTextureProps): this {
345
- const {mipmaps = true, parameters = {} as Record<GL, any>} = props;
246
+ /*
247
+ initializeCube(props?: TextureProps): void {
248
+ const {mipmaps = true} = props; // , parameters = {} as Record<GL, any>} = props;
346
249
 
347
250
  // Store props for accessors
348
251
  // this.props = props;
349
252
 
350
253
  // @ts-expect-error
351
- this.setCubeMapImageData(props).then(() => {
352
- this.loaded = true;
353
-
254
+ this.setCubeMapData(props).then(() => {
354
255
  // TODO - should genMipmap() be called on the cubemap or on the faces?
355
256
  // TODO - without generateMipmap() cube textures do not work at all!!! Why?
356
257
  if (mipmaps) {
@@ -358,12 +259,33 @@ export class WEBGLTexture extends Texture<WEBGLTextureProps> {
358
259
  }
359
260
 
360
261
  this.setSampler(props.sampler);
361
- this._setSamplerParameters(parameters);
262
+
263
+ // v8 compatibility?
264
+ // const {parameters = {} as Record<GL, any>} = props;
265
+ // this._setSamplerParameters(parameters);
362
266
  });
363
- return this;
267
+ }
268
+ */
269
+
270
+ override destroy(): void {
271
+ if (this.handle) {
272
+ this.gl.deleteTexture(this.handle);
273
+ this.removeStats();
274
+ this.trackDeallocatedMemory('Texture');
275
+ // this.handle = null;
276
+ this.destroyed = true;
277
+ }
278
+ }
279
+
280
+ override toString(): string {
281
+ return `Texture(${this.id},${this.width}x${this.height})`;
364
282
  }
365
283
 
366
- setSampler(sampler: Sampler | SamplerProps = {}): this {
284
+ createView(props: TextureViewProps): WEBGLTextureView {
285
+ return new WEBGLTextureView(this.device, {...props, texture: this});
286
+ }
287
+
288
+ setSampler(sampler: Sampler | SamplerProps = {}): void {
367
289
  let samplerProps: SamplerParameters;
368
290
  if (sampler instanceof WEBGLSampler) {
369
291
  this.sampler = sampler;
@@ -375,422 +297,253 @@ export class WEBGLTexture extends Texture<WEBGLTextureProps> {
375
297
 
376
298
  const parameters = convertSamplerParametersToWebGL(samplerProps);
377
299
  this._setSamplerParameters(parameters);
378
- return this;
379
- }
380
-
381
- /**
382
- * If size has changed, reinitializes with current format
383
- * @note note clears image and mipmaps
384
- */
385
- resize(options: {height: number; width: number; mipmaps?: boolean}): this {
386
- const {height, width, mipmaps = false} = options;
387
- if (width !== this.width || height !== this.height) {
388
- return this.initialize({
389
- width,
390
- height,
391
- format: this.format,
392
- type: this.type,
393
- dataFormat: this.dataFormat,
394
- mipmaps
395
- });
396
- }
397
- return this;
398
300
  }
399
301
 
400
- /** Update external texture (video frame) */
302
+ /** Update external texture (video frame or canvas) */
401
303
  update(): void {
402
- if (this._video) {
403
- const {video, parameters, lastTime} = this._video;
404
- // @ts-expect-error
405
- if (lastTime === video.currentTime || video.readyState < HTMLVideoElement.HAVE_CURRENT_DATA) {
406
- return;
407
- }
408
- this.setSubImageData({
409
- data: video,
410
- parameters
411
- });
412
- if (this.mipmaps) {
413
- this.generateMipmap();
414
- }
415
- this._video.lastTime = video.currentTime;
416
- }
304
+ log.warn('Texture.update() not implemented');
305
+ // if (this._video) {
306
+ // const {video, parameters, lastTime} = this._video;
307
+ // // @ts-expect-error
308
+ // if (lastTime === video.currentTime || video.readyState < HTMLVideoElement.HAVE_CURRENT_DATA) {
309
+ // return;
310
+ // }
311
+ // this.setSubImageData({
312
+ // data: video,
313
+ // parameters
314
+ // });
315
+ // if (this.mipmaps) {
316
+ // this.generateMipmap();
317
+ // }
318
+ // this._video.lastTime = video.currentTime;
319
+ // }
417
320
  }
418
321
 
419
322
  // Call to regenerate mipmaps after modifying texture(s)
420
- generateMipmap(params = {}): this {
323
+ generateMipmap(params = {}): void {
324
+ if (!this.props.data) {
325
+ return;
326
+ }
421
327
  this.mipmaps = true;
422
-
423
- this.gl.bindTexture(this.target, this.handle);
328
+ this.gl.bindTexture(this.glTarget, this.handle);
424
329
  withGLParameters(this.gl, params, () => {
425
- this.gl.generateMipmap(this.target);
330
+ this.gl.generateMipmap(this.glTarget);
426
331
  });
427
- this.gl.bindTexture(this.target, null);
428
- return this;
332
+ this.gl.bindTexture(this.glTarget, null);
429
333
  }
430
334
 
431
- /*
432
- * Allocates storage
433
- * @param {*} pixels -
434
- * null - create empty texture of specified format
435
- * Typed array - init from image data in typed array
436
- * Buffer|WebGLBuffer - (WEBGL2) init from image data in WebGLBuffer
437
- * HTMLImageElement|Image - Inits with content of image. Auto width/height
438
- * HTMLCanvasElement - Inits with contents of canvas. Auto width/height
439
- * HTMLVideoElement - Creates video texture. Auto width/height
440
- *
441
- * @param width -
442
- * @param height -
443
- * @param mipMapLevel -
444
- * @param {GLenum} format - format of image data.
445
- * @param {GLenum} type
446
- * - format of array (autodetect from type) or
447
- * - (WEBGL2) format of buffer
448
- * @param {Number} offset - (WEBGL2) offset from start of buffer
449
- * @parameters - temporary settings to be applied, can be used to supply pixel store settings.
450
- */
451
- // eslint-disable-next-line max-statements, complexity
452
- setImageData(options: SetImageDataOptions) {
453
- if (this.props.dimension === '3d' || this.props.dimension === '2d-array') {
454
- return this.setImageData3D(options);
455
- }
335
+ // Image Data Setters
456
336
 
457
- this.trackDeallocatedMemory('Texture');
337
+ setTexture1DData(data: Texture1DData): void {
338
+ throw new Error('setTexture1DData not supported in WebGL.');
339
+ }
458
340
 
459
- const {
460
- target = this.target,
461
- pixels = null,
462
- level = 0,
463
- glFormat = this.glFormat,
464
- offset = 0,
465
- parameters = {} as Record<GL, any>
466
- } = options;
341
+ /** Set a simple texture */
342
+ setTexture2DData(lodData: Texture2DData, depth = 0, glTarget = this.glTarget): void {
343
+ this.bind();
467
344
 
468
- let {
469
- data = null,
470
- type = this.type,
471
- width = this.width,
472
- height = this.height,
473
- dataFormat = this.dataFormat,
474
- compressed = false
475
- } = options;
345
+ const lodArray = normalizeTextureData(lodData, this);
476
346
 
477
- // pixels variable is for API compatibility purpose
478
- if (!data) {
479
- data = pixels;
347
+ // If the user provides multiple LODs, then automatic mipmap
348
+ // generation generateMipmap() should be disabled to avoid overwriting them.
349
+ if (lodArray.length > 1 && this.props.mipmaps !== false) {
350
+ log.warn(`Texture ${this.id} mipmap and multiple LODs.`)();
480
351
  }
481
352
 
482
- ({type, dataFormat, compressed, width, height} = this._deduceParameters({
483
- format: this.props.format,
484
- type,
485
- dataFormat,
486
- compressed,
487
- data,
488
- width,
489
- height
490
- }));
491
-
492
- const {gl} = this;
493
- gl.bindTexture(this.target, this.handle);
494
-
495
- let dataType = null;
496
- ({data, dataType} = this._getDataType({data, compressed}));
497
-
498
- withGLParameters(this.gl, parameters, () => {
499
- switch (dataType) {
500
- case 'null':
501
- gl.texImage2D(
502
- target,
503
- level,
504
- glFormat,
505
- width,
506
- height,
507
- 0 /* border*/,
508
- dataFormat,
509
- type,
510
- data
511
- );
512
- break;
513
- case 'typed-array':
514
- gl.texImage2D(
515
- target,
516
- level,
517
- glFormat,
518
- width,
519
- height,
520
- 0, // border (must be 0)
521
- dataFormat,
522
- type,
523
- data,
524
- offset
525
- );
526
- break;
527
- case 'buffer':
528
- // WebGL2 enables creating textures directly from a WebGL buffer
529
- this.device.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, data.handle || data);
530
- this.device.gl.texImage2D(
531
- target,
532
- level,
533
- glFormat,
534
- width,
535
- height,
536
- 0 /* border*/,
537
- dataFormat,
538
- type,
539
- offset
540
- );
541
- this.device.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
542
- break;
543
- case 'browser-object':
544
- gl.texImage2D(
545
- target,
546
- level,
547
- glFormat,
548
- width,
549
- height,
550
- 0 /* border*/,
551
- dataFormat,
552
- type,
553
- data
554
- );
555
- break;
556
- case 'compressed':
557
- for (const [levelIndex, levelData] of data.entries()) {
558
- gl.compressedTexImage2D(
559
- target,
560
- levelIndex,
561
- levelData.format,
562
- levelData.width,
563
- levelData.height,
564
- 0 /* border, must be 0 */,
565
- levelData.data
566
- );
567
- }
568
-
569
- break;
570
- default:
571
- assert(false, 'Unknown image data type');
572
- }
573
- });
574
-
575
- if (data && data.byteLength) {
576
- this.trackAllocatedMemory(data.byteLength, 'Texture');
577
- } else {
578
- const bytesPerPixel = getTextureFormatBytesPerPixel(this.props.format);
579
- this.trackAllocatedMemory(this.width * this.height * bytesPerPixel, 'Texture');
353
+ for (let lodLevel = 0; lodLevel < lodArray.length; lodLevel++) {
354
+ const imageData = lodArray[lodLevel];
355
+ this._setMipLevel(depth, lodLevel, imageData);
580
356
  }
581
357
 
582
- this.loaded = true;
583
-
584
- return this;
358
+ this.unbind();
585
359
  }
586
360
 
587
361
  /**
588
- * Redefines an area of an existing texture
589
- * Note: does not allocate storage
590
- * Redefines an area of an existing texture
362
+ * Sets a 3D texture
363
+ * @param data
591
364
  */
592
- setSubImageData({
593
- target = this.target,
594
- pixels = null,
595
- data = null,
596
- x = 0,
597
- y = 0,
598
- width = this.width,
599
- height = this.height,
600
- level = 0,
601
- glFormat = this.glFormat,
602
- type = this.type,
603
- dataFormat = this.dataFormat,
604
- compressed = false,
605
- offset = 0,
606
- parameters = {} as Record<GL, any>
607
- }: SetSubImageDataOptions) {
608
- ({type, dataFormat, compressed, width, height} = this._deduceParameters({
609
- format: this.props.format,
610
- type,
611
- dataFormat,
612
- compressed,
613
- data,
614
- width,
615
- height
616
- }));
617
-
618
- assert(this.depth === 1, 'texSubImage not supported for 3D textures');
619
-
620
- // pixels variable is for API compatibility purpose
621
- if (!data) {
622
- data = pixels;
365
+ setTexture3DData(data: Texture3DData): void {
366
+ if (this.props.dimension !== '3d') {
367
+ throw new Error(this.id);
623
368
  }
624
-
625
- // Support ndarrays
626
- if (data && data.data) {
627
- const ndarray = data;
628
- data = ndarray.data;
629
- width = ndarray.shape[0];
630
- height = ndarray.shape[1];
369
+ if (ArrayBuffer.isView(data)) {
370
+ copyCPUDataToMipLevel(this.device.gl, data, this);
631
371
  }
372
+ }
632
373
 
633
- // Support buffers
634
- if (data instanceof WEBGLBuffer) {
635
- data = data.handle;
374
+ /**
375
+ * Set a Texture Cube Data
376
+ * @todo - could support TextureCubeArray with depth
377
+ * @param data
378
+ * @param index
379
+ */
380
+ setTextureCubeData(data: TextureCubeData, depth: number = 0): void {
381
+ if (this.props.dimension !== 'cube') {
382
+ throw new Error(this.id);
636
383
  }
637
-
638
- this.gl.bindTexture(this.target, this.handle);
639
-
640
- withGLParameters(this.gl, parameters, () => {
641
- // TODO - x,y parameters
642
- if (compressed) {
643
- this.gl.compressedTexSubImage2D(target, level, x, y, width, height, glFormat, data);
644
- } else if (data === null) {
645
- this.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, null);
646
- } else if (ArrayBuffer.isView(data)) {
647
- this.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, data, offset);
648
- } else if (typeof WebGLBuffer !== 'undefined' && data instanceof WebGLBuffer) {
649
- // WebGL2 allows us to create texture directly from a WebGL buffer
650
- // This texImage2D signature uses currently bound GL.PIXEL_UNPACK_BUFFER
651
- this.device.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, data);
652
- this.device.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, offset);
653
- this.device.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
654
- } else {
655
- // Assume data is a browser supported object (ImageData, Canvas, ...)
656
- this.device.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, data);
657
- }
658
- });
659
-
660
- this.gl.bindTexture(this.target, null);
384
+ // for (const face of Texture.CubeFaces) {
385
+ // // this.setTextureCubeFaceData(face, data[face]);
386
+ // }
661
387
  }
662
388
 
663
389
  /**
664
- * Defines a two-dimensional texture image or cube-map texture image with
665
- * pixels from the current framebuffer (rather than from client memory).
666
- * (gl.copyTexImage2D wrapper)
667
- *
668
- * Note that binding a texture into a Framebuffer's color buffer and
669
- * rendering can be faster.
390
+ * Sets an entire texture array
391
+ * @param data
670
392
  */
671
- copyFramebuffer(opts = {}) {
672
- log.error(
673
- 'Texture.copyFramebuffer({...}) is no logner supported, use copyToTexture(source, target, opts})'
674
- )();
675
- return null;
393
+ setTextureArrayData(data: TextureArrayData): void {
394
+ if (this.props.dimension !== '2d-array') {
395
+ throw new Error(this.id);
396
+ }
397
+ throw new Error('setTextureArrayData not implemented.');
676
398
  }
677
399
 
678
- getActiveUnit(): number {
679
- return this.gl.getParameter(GL.ACTIVE_TEXTURE) - GL.TEXTURE0;
400
+ /**
401
+ * Sets an entire texture cube array
402
+ * @param data
403
+ */
404
+ setTextureCubeArrayData(data: TextureCubeArrayData): void {
405
+ throw new Error('setTextureCubeArrayData not supported in WebGL2.');
680
406
  }
681
407
 
682
- bind(textureUnit = this.textureUnit) {
683
- const {gl} = this;
408
+ setTextureCubeFaceData(lodData: Texture2DData, face: TextureCubeFace, depth: number = 0): void {
409
+ // assert(this.props.dimension === 'cube');
684
410
 
685
- if (textureUnit !== undefined) {
686
- this.textureUnit = textureUnit;
687
- gl.activeTexture(gl.TEXTURE0 + textureUnit);
411
+ // If the user provides multiple LODs, then automatic mipmap
412
+ // generation generateMipmap() should be disabled to avoid overwriting them.
413
+ if (Array.isArray(lodData) && lodData.length > 1 && this.props.mipmaps !== false) {
414
+ log.warn(`${this.id} has mipmap and multiple LODs.`)();
688
415
  }
689
416
 
690
- gl.bindTexture(this.target, this.handle);
691
- return textureUnit;
417
+ // const glFace = GL.TEXTURE_CUBE_MAP_POSITIVE_X + Texture.CubeFaces.indexOf(face);
418
+ // const glType = GL.UNSIGNED_BYTE;
419
+ // const {width, height, format = GL.RGBA, type = GL.UNSIGNED_BYTE} = this;
420
+ // const {width, height, format = GL.RGBA, type = GL.UNSIGNED_BYTE} = this;
421
+
422
+ this.bind();
423
+ // for (let lodLevel = 0; lodLevel < lodData.length; lodLevel++) {
424
+ // const imageData = lodData[lodLevel];
425
+ // if (imageData instanceof ArrayBuffer) {
426
+ // // const imageData = image instanceof ArrayBuffer ? new ImageData(new Uint8ClampedArray(image), this.width) : image;
427
+ // this.device.gl.texImage2D?.(
428
+ // glFace,
429
+ // lodLevel,
430
+ // this.glInternalFormat,
431
+ // this.glInternalFormat,
432
+ // glType,
433
+ // imageData
434
+ // );
435
+ // }
436
+ // }
437
+ this.unbind();
692
438
  }
693
439
 
694
- unbind(textureUnit = this.textureUnit) {
695
- const {gl} = this;
440
+ // INTERNAL METHODS
696
441
 
697
- if (textureUnit !== undefined) {
698
- this.textureUnit = textureUnit;
699
- gl.activeTexture(gl.TEXTURE0 + textureUnit);
700
- }
442
+ /** @todo update this method to accept LODs */
443
+ setImageDataForFace(options): void {
444
+ const {
445
+ face,
446
+ width,
447
+ height,
448
+ pixels,
449
+ data,
450
+ format = GL.RGBA,
451
+ type = GL.UNSIGNED_BYTE
452
+ // generateMipmap = false // TODO
453
+ } = options;
701
454
 
702
- gl.bindTexture(this.target, null);
703
- return textureUnit;
704
- }
455
+ const {gl} = this;
705
456
 
706
- // PRIVATE METHODS
457
+ const imageData = pixels || data;
707
458
 
708
- _getDataType({data, compressed = false}) {
709
- if (compressed) {
710
- return {data, dataType: 'compressed'};
711
- }
712
- if (data === null) {
713
- return {data, dataType: 'null'};
714
- }
715
- if (ArrayBuffer.isView(data)) {
716
- return {data, dataType: 'typed-array'};
717
- }
718
- if (data instanceof WEBGLBuffer) {
719
- return {data: data.handle, dataType: 'buffer'};
459
+ this.bind();
460
+ if (imageData instanceof Promise) {
461
+ imageData.then(resolvedImageData =>
462
+ this.setImageDataForFace(
463
+ Object.assign({}, options, {
464
+ face,
465
+ data: resolvedImageData,
466
+ pixels: resolvedImageData
467
+ })
468
+ )
469
+ );
470
+ } else if (this.width || this.height) {
471
+ gl.texImage2D(face, 0, format, width, height, 0 /* border*/, format, type, imageData);
472
+ } else {
473
+ gl.texImage2D(face, 0, format, format, type, imageData);
720
474
  }
721
- // Raw WebGL handle (not a luma wrapper)
722
- if (typeof WebGLBuffer !== 'undefined' && data instanceof WebGLBuffer) {
723
- return {data, dataType: 'buffer'};
475
+ }
476
+
477
+ _getImageDataMap(faceData: Record<string | GL, any>): Record<GL, any> {
478
+ for (let i = 0; i < Texture.CubeFaces.length; ++i) {
479
+ const faceName = Texture.CubeFaces[i];
480
+ if (faceData[faceName]) {
481
+ faceData[GL.TEXTURE_CUBE_MAP_POSITIVE_X + i] = faceData[faceName];
482
+ delete faceData[faceName];
483
+ }
724
484
  }
725
- // Assume data is a browser supported object (ImageData, Canvas, ...)
726
- return {data, dataType: 'browser-object'};
485
+ return faceData;
727
486
  }
728
487
 
729
- // HELPER METHODS
488
+ // RESOURCE METHODS
730
489
 
731
- _deduceParameters(opts: WEBGLTextureProps) {
732
- const {format, data} = opts;
733
- let {width, height, dataFormat, type, compressed} = opts;
490
+ /**
491
+ * Sets sampler parameters on texture
492
+ */
493
+ _setSamplerParameters(parameters: GLSamplerParameters): void {
494
+ log.log(1, 'texture sampler parameters', parameters)();
734
495
 
735
- // Deduce format and type from format
736
- const parameters = getWebGLTextureParameters(format);
737
- dataFormat = dataFormat || parameters.dataFormat;
738
- type = type || parameters.type;
739
- compressed = compressed || parameters.compressed;
496
+ this.gl.bindTexture(this.glTarget, this.handle);
497
+ for (const [pname, pvalue] of Object.entries(parameters)) {
498
+ const param = Number(pname) as keyof GLSamplerParameters;
499
+ const value = pvalue;
740
500
 
741
- ({width, height} = this._deduceImageSize(data, width, height));
501
+ // Apparently integer/float issues require two different texture parameter setting functions in JavaScript.
502
+ // For now, pick the float version for parameters specified as GLfloat.
503
+ switch (param) {
504
+ case GL.TEXTURE_MIN_LOD:
505
+ case GL.TEXTURE_MAX_LOD:
506
+ this.gl.texParameterf(this.glTarget, param, value);
507
+ break;
742
508
 
743
- return {dataFormat, type, compressed, width, height, format, data};
744
- }
509
+ case GL.TEXTURE_MIN_FILTER:
510
+ this.gl.texParameteri(this.glTarget, param, value);
511
+ break;
745
512
 
746
- // eslint-disable-next-line complexity
747
- _deduceImageSize(data, width, height): {width: number; height: number} {
748
- let size;
749
-
750
- if (typeof ImageData !== 'undefined' && data instanceof ImageData) {
751
- size = {width: data.width, height: data.height};
752
- } else if (typeof HTMLImageElement !== 'undefined' && data instanceof HTMLImageElement) {
753
- size = {width: data.naturalWidth, height: data.naturalHeight};
754
- } else if (typeof HTMLCanvasElement !== 'undefined' && data instanceof HTMLCanvasElement) {
755
- size = {width: data.width, height: data.height};
756
- } else if (typeof ImageBitmap !== 'undefined' && data instanceof ImageBitmap) {
757
- size = {width: data.width, height: data.height};
758
- } else if (typeof HTMLVideoElement !== 'undefined' && data instanceof HTMLVideoElement) {
759
- size = {width: data.videoWidth, height: data.videoHeight};
760
- } else if (!data) {
761
- size = {width: width >= 0 ? width : 1, height: height >= 0 ? height : 1};
762
- } else {
763
- size = {width, height};
513
+ case GL.TEXTURE_WRAP_S:
514
+ case GL.TEXTURE_WRAP_T:
515
+ this.gl.texParameteri(this.glTarget, param, value);
516
+ break;
517
+ case GL.TEXTURE_MAX_ANISOTROPY_EXT:
518
+ // We have to query feature before using it
519
+ if (this.device.features.has('texture-filterable-anisotropic-webgl')) {
520
+ this.gl.texParameteri(this.glTarget, param, value);
521
+ }
522
+ break;
523
+ default:
524
+ this.gl.texParameteri(this.glTarget, param, value);
525
+ break;
526
+ }
764
527
  }
765
528
 
766
- assert(size, 'Could not deduced texture size');
767
- assert(
768
- width === undefined || size.width === width,
769
- 'Deduced texture width does not match supplied width'
770
- );
771
- assert(
772
- height === undefined || size.height === height,
773
- 'Deduced texture height does not match supplied height'
774
- );
775
-
776
- return size;
529
+ this.gl.bindTexture(this.glTarget, null);
777
530
  }
778
531
 
779
- // CUBE MAP METHODS
532
+ // CLASSIC
780
533
 
781
- /* eslint-disable max-statements, max-len */
782
- async setCubeMapImageData(options: {
783
- width: any;
784
- height: any;
785
- pixels: any;
786
- data: any;
534
+ /*
535
+ setCubeMapData(options: {
536
+ width: number;
537
+ height: number;
538
+ data: Record<GL, Texture2DData> | Record<TextureCubeFace, Texture2DData>;
787
539
  format?: any;
788
540
  type?: any;
789
- }): Promise<void> {
541
+ /** @deprecated Use .data *
542
+ pixels: any;
543
+ }): void {
790
544
  const {gl} = this;
791
545
 
792
546
  const {width, height, pixels, data, format = GL.RGBA, type = GL.UNSIGNED_BYTE} = options;
793
- const imageDataMap = pixels || data;
794
547
 
795
548
  // pixel data (imageDataMap) is an Object from Face to Image or Promise.
796
549
  // For example:
@@ -805,13 +558,12 @@ export class WEBGLTexture extends Texture<WEBGLTextureProps> {
805
558
  // GL.TEXTURE_CUBE_MAP_NEGATIVE_X : [Image-or-Promise-LOD-0, Image-or-Promise-LOD-1],
806
559
  // ... }
807
560
 
808
- const resolvedFaces = await Promise.all(
809
- WEBGLTexture.FACES.map(face => {
810
- const facePixels = imageDataMap[face];
811
- return Promise.all(Array.isArray(facePixels) ? facePixels : [facePixels]);
812
- })
813
- );
561
+ const imageDataMap = this._getImageDataMap(pixels || data);
814
562
 
563
+ const resolvedFaces = WEBGLTexture.FACES.map(face => {
564
+ const facePixels = imageDataMap[face];
565
+ return Array.isArray(facePixels) ? facePixels : [facePixels];
566
+ });
815
567
  this.bind();
816
568
 
817
569
  WEBGLTexture.FACES.forEach((face, index) => {
@@ -823,7 +575,7 @@ export class WEBGLTexture extends Texture<WEBGLTextureProps> {
823
575
  resolvedFaces[index].forEach((image, lodLevel) => {
824
576
  // TODO: adjust width & height for LOD!
825
577
  if (width && height) {
826
- gl.texImage2D(face, lodLevel, format, width, height, 0 /* border*/, format, type, image);
578
+ gl.texImage2D(face, lodLevel, format, width, height, 0 /* border*, format, type, image);
827
579
  } else {
828
580
  gl.texImage2D(face, lodLevel, format, format, type, image);
829
581
  }
@@ -832,168 +584,64 @@ export class WEBGLTexture extends Texture<WEBGLTextureProps> {
832
584
 
833
585
  this.unbind();
834
586
  }
587
+ */
835
588
 
836
- /** @todo update this method to accept LODs */
837
- setImageDataForFace(options) {
838
- const {
839
- face,
840
- width,
841
- height,
842
- pixels,
843
- data,
844
- format = GL.RGBA,
845
- type = GL.UNSIGNED_BYTE
846
- // generateMipmap = false // TODO
847
- } = options;
848
-
849
- const {gl} = this;
589
+ // INTERNAL SETTERS
850
590
 
851
- const imageData = pixels || data;
852
-
853
- this.bind();
854
- if (imageData instanceof Promise) {
855
- imageData.then(resolvedImageData =>
856
- this.setImageDataForFace(
857
- Object.assign({}, options, {
858
- face,
859
- data: resolvedImageData,
860
- pixels: resolvedImageData
861
- })
862
- )
863
- );
864
- } else if (this.width || this.height) {
865
- gl.texImage2D(face, 0, format, width, height, 0 /* border*/, format, type, imageData);
866
- } else {
867
- gl.texImage2D(face, 0, format, format, type, imageData);
591
+ /**
592
+ * Copy a region of data from a CPU memory buffer into this texture.
593
+ * @todo - GLUnpackParameters parameters
594
+ */
595
+ protected _setMipLevel(depth: number, level: number, textureData: Texture2DData, offset = 0) {
596
+ // if (!textureData) {
597
+ // clearMipLevel(this.device.gl, {...this, depth, level});
598
+ // return;
599
+ // }
600
+
601
+ if (Texture.isExternalImage(textureData)) {
602
+ copyCPUImageToMipLevel(this.device.gl, textureData, {...this, depth, level});
603
+ return;
868
604
  }
869
605
 
870
- return this;
871
- }
872
-
873
- /** Image 3D copies from Typed Array or WebGLBuffer */
874
- setImageData3D(options: SetImageData3DOptions) {
875
- const {
876
- level = 0,
877
- dataFormat,
878
- format,
879
- type, // = GL.UNSIGNED_BYTE,
880
- width,
881
- height,
882
- depth = 1,
883
- offset = 0,
884
- data,
885
- parameters = {}
886
- } = options;
887
-
888
- this.trackDeallocatedMemory('Texture');
889
-
890
- this.gl.bindTexture(this.target, this.handle);
891
-
892
- const webglTextureFormat = getWebGLTextureParameters(format);
893
-
894
- withGLParameters(this.gl, parameters, () => {
895
- if (ArrayBuffer.isView(data)) {
896
- this.gl.texImage3D(
897
- this.target,
898
- level,
899
- webglTextureFormat.format,
900
- width,
901
- height,
902
- depth,
903
- 0 /* border, must be 0 */,
904
- webglTextureFormat.dataFormat,
905
- webglTextureFormat.type, // dataType: getWebGL,
906
- data
907
- );
908
- }
909
-
910
- if (data instanceof WEBGLBuffer) {
911
- this.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, data.handle);
912
- this.gl.texImage3D(
913
- this.target,
914
- level,
915
- dataFormat,
916
- width,
917
- height,
918
- depth,
919
- 0 /* border, must be 0 */,
920
- format,
921
- type,
922
- offset
923
- );
924
- }
925
- });
926
-
927
- if (data && data.byteLength) {
928
- this.trackAllocatedMemory(data.byteLength, 'Texture');
929
- } else {
930
- const bytesPerPixel = getTextureFormatBytesPerPixel(this.props.format);
931
- this.trackAllocatedMemory(this.width * this.height * this.depth * bytesPerPixel, 'Texture');
606
+ // @ts-expect-error
607
+ if (this.isTextureLevelData(textureData)) {
608
+ copyCPUDataToMipLevel(this.device.gl, textureData.data, {
609
+ ...this,
610
+ depth,
611
+ level
612
+ });
613
+ return;
932
614
  }
933
615
 
934
- this.loaded = true;
616
+ throw new Error('Texture: invalid image data');
617
+ }
618
+ // HELPERS
935
619
 
936
- return this;
620
+ getActiveUnit(): number {
621
+ return this.gl.getParameter(GL.ACTIVE_TEXTURE) - GL.TEXTURE0;
937
622
  }
938
623
 
939
- // RESOURCE METHODS
624
+ bind(textureUnit?: number): number {
625
+ const {gl} = this;
940
626
 
941
- /**
942
- * Sets sampler parameters on texture
943
- */
944
- _setSamplerParameters(parameters: GLSamplerParameters): void {
945
- // NPOT parameters may populate an empty object
946
- if (isObjectEmpty(parameters)) {
947
- return;
627
+ if (textureUnit !== undefined) {
628
+ this.textureUnit = textureUnit;
629
+ gl.activeTexture(gl.TEXTURE0 + textureUnit);
948
630
  }
949
631
 
950
- logParameters(parameters);
951
-
952
- this.gl.bindTexture(this.target, this.handle);
953
- for (const [pname, pvalue] of Object.entries(parameters)) {
954
- const param = Number(pname) as GL.TEXTURE_MIN_LOD | GL.TEXTURE_MAX_LOD;
955
- const value = pvalue;
632
+ gl.bindTexture(this.glTarget, this.handle);
633
+ return textureUnit;
634
+ }
956
635
 
957
- // Apparently there are integer/float conversion issues requires two parameter setting functions in JavaScript.
958
- // For now, pick the float version for parameters specified as GLfloat.
959
- switch (param) {
960
- case GL.TEXTURE_MIN_LOD:
961
- case GL.TEXTURE_MAX_LOD:
962
- this.gl.texParameterf(this.target, param, value);
963
- break;
636
+ unbind(textureUnit?: number): number | undefined {
637
+ const {gl} = this;
964
638
 
965
- default:
966
- this.gl.texParameteri(this.target, param, value);
967
- break;
968
- }
639
+ if (textureUnit !== undefined) {
640
+ this.textureUnit = textureUnit;
641
+ gl.activeTexture(gl.TEXTURE0 + textureUnit);
969
642
  }
970
- this.gl.bindTexture(this.target, null);
971
- return;
972
- }
973
- }
974
643
 
975
- // HELPERS
976
-
977
- function getWebGLTextureTarget(props: TextureProps) {
978
- switch (props.dimension) {
979
- // supported in WebGL
980
- case '2d':
981
- return GL.TEXTURE_2D;
982
- case 'cube':
983
- return GL.TEXTURE_CUBE_MAP;
984
- // supported in WebGL2
985
- case '2d-array':
986
- return GL.TEXTURE_2D_ARRAY;
987
- case '3d':
988
- return GL.TEXTURE_3D;
989
- // not supported in any WebGL version
990
- case '1d':
991
- case 'cube-array':
992
- default:
993
- throw new Error(props.dimension);
644
+ gl.bindTexture(this.glTarget, null);
645
+ return textureUnit;
994
646
  }
995
647
  }
996
-
997
- function logParameters(parameters: Record<number, GL | number>) {
998
- log.log(1, 'texture sampler parameters', parameters)();
999
- }