@luma.gl/engine 9.2.6 → 9.3.0-alpha.4
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/animation-loop/animation-loop.d.ts +3 -1
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +10 -4
- package/dist/animation-loop/animation-loop.js.map +1 -1
- package/dist/compute/computation.d.ts.map +1 -1
- package/dist/compute/computation.js +3 -2
- package/dist/compute/computation.js.map +1 -1
- package/dist/compute/swap.d.ts +2 -0
- package/dist/compute/swap.d.ts.map +1 -1
- package/dist/compute/swap.js +10 -5
- package/dist/compute/swap.js.map +1 -1
- package/dist/dist.dev.js +1251 -574
- package/dist/dist.min.js +216 -48
- package/dist/dynamic-texture/dynamic-texture.d.ts +95 -0
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
- package/dist/dynamic-texture/dynamic-texture.js +389 -0
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
- package/dist/dynamic-texture/mipmaps.d.ts +6 -0
- package/dist/dynamic-texture/mipmaps.d.ts.map +1 -0
- package/dist/dynamic-texture/mipmaps.js +441 -0
- package/dist/dynamic-texture/mipmaps.js.map +1 -0
- package/dist/dynamic-texture/texture-data.d.ts +137 -0
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
- package/dist/dynamic-texture/texture-data.js +183 -0
- package/dist/dynamic-texture/texture-data.js.map +1 -0
- package/dist/factories/pipeline-factory.d.ts.map +1 -1
- package/dist/factories/pipeline-factory.js +3 -3
- package/dist/factories/pipeline-factory.js.map +1 -1
- package/dist/factories/shader-factory.d.ts.map +1 -1
- package/dist/factories/shader-factory.js +3 -2
- package/dist/factories/shader-factory.js.map +1 -1
- package/dist/index.cjs +1243 -583
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/model/model.d.ts +31 -10
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +34 -14
- package/dist/model/model.js.map +1 -1
- package/dist/models/billboard-texture-model.d.ts +8 -5
- package/dist/models/billboard-texture-model.d.ts.map +1 -1
- package/dist/models/billboard-texture-model.js +70 -18
- package/dist/models/billboard-texture-model.js.map +1 -1
- package/dist/passes/get-fragment-shader.js +15 -11
- package/dist/passes/get-fragment-shader.js.map +1 -1
- package/dist/passes/shader-pass-renderer.d.ts +5 -5
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
- package/dist/passes/shader-pass-renderer.js +13 -12
- package/dist/passes/shader-pass-renderer.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/buffer-layout-order.d.ts.map +1 -1
- package/dist/utils/buffer-layout-order.js +12 -2
- package/dist/utils/buffer-layout-order.js.map +1 -1
- package/package.json +6 -6
- package/src/animation-loop/animation-loop.ts +11 -4
- package/src/compute/computation.ts +3 -2
- package/src/compute/swap.ts +13 -7
- package/src/dynamic-texture/dynamic-texture.ts +499 -0
- package/src/dynamic-texture/mipmaps.ts +517 -0
- package/src/dynamic-texture/texture-data.ts +301 -0
- package/src/factories/pipeline-factory.ts +4 -3
- package/src/factories/shader-factory.ts +4 -2
- package/src/index.ts +9 -4
- package/src/model/model.ts +37 -18
- package/src/models/billboard-texture-model.ts +81 -22
- package/src/passes/get-fragment-shader.ts +15 -11
- package/src/passes/shader-pass-renderer.ts +22 -16
- package/src/types.ts +11 -0
- package/src/utils/buffer-layout-order.ts +18 -2
- package/dist/async-texture/async-texture.d.ts +0 -166
- package/dist/async-texture/async-texture.d.ts.map +0 -1
- package/dist/async-texture/async-texture.js +0 -386
- package/dist/async-texture/async-texture.js.map +0 -1
- package/src/async-texture/async-texture.ts +0 -551
- /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import type {TypedArray, TextureFormat, ExternalImage} from '@luma.gl/core';
|
|
2
|
+
import {isExternalImage, getExternalImageSize} from '@luma.gl/core';
|
|
3
|
+
|
|
4
|
+
export type TextureImageSource = ExternalImage;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One mip level
|
|
8
|
+
* Basic data structure is similar to `ImageData`
|
|
9
|
+
* additional optional fields can describe compressed texture data.
|
|
10
|
+
*/
|
|
11
|
+
export type TextureImageData = {
|
|
12
|
+
/** WebGPU style format string. Defaults to 'rgba8unorm' */
|
|
13
|
+
format?: TextureFormat;
|
|
14
|
+
/** Typed Array with the bytes of the image. @note beware row byte alignment requirements */
|
|
15
|
+
data: TypedArray;
|
|
16
|
+
/** Width of the image, in pixels, @note beware row byte alignment requirements */
|
|
17
|
+
width: number;
|
|
18
|
+
/** Height of the image, in rows */
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A single mip-level can be initialized by data or an ImageBitmap etc
|
|
24
|
+
* @note in the WebGPU spec a mip-level is called a subresource
|
|
25
|
+
*/
|
|
26
|
+
export type TextureMipLevelData = TextureImageData | TextureImageSource;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Texture data for one image "slice" (which can consist of multiple miplevels)
|
|
30
|
+
* Thus data for one slice be a single mip level or an array of miplevels
|
|
31
|
+
* @note in the WebGPU spec each cross-section image in a 3D texture is called a "slice",
|
|
32
|
+
* in a array texture each image in the array is called an array "layer"
|
|
33
|
+
* luma.gl calls one image in a GPU texture a "slice" regardless of context.
|
|
34
|
+
*/
|
|
35
|
+
export type TextureSliceData = TextureMipLevelData | TextureMipLevelData[];
|
|
36
|
+
|
|
37
|
+
/** Names of cube texture faces */
|
|
38
|
+
export type TextureCubeFace = '+X' | '-X' | '+Y' | '-Y' | '+Z' | '-Z';
|
|
39
|
+
|
|
40
|
+
/** Array of cube texture faces. @note: index in array is the face index */
|
|
41
|
+
// prettier-ignore
|
|
42
|
+
export const TEXTURE_CUBE_FACES = ['+X', '-X', '+Y', '-Y', '+Z', '-Z'] as const satisfies readonly TextureCubeFace[];
|
|
43
|
+
|
|
44
|
+
/** Map of cube texture face names to face indexes */
|
|
45
|
+
// prettier-ignore
|
|
46
|
+
export const TEXTURE_CUBE_FACE_MAP = {'+X': 0, '-X': 1, '+Y': 2, '-Y': 3, '+Z': 4, '-Z': 5} as const satisfies Record<TextureCubeFace, number>;
|
|
47
|
+
|
|
48
|
+
/** @todo - Define what data type is supported for 1D textures. TextureImageData with height = 1 */
|
|
49
|
+
export type Texture1DData = TextureSliceData;
|
|
50
|
+
|
|
51
|
+
/** Texture data can be one or more mip levels */
|
|
52
|
+
export type Texture2DData = TextureSliceData;
|
|
53
|
+
|
|
54
|
+
/** 6 face textures */
|
|
55
|
+
export type TextureCubeData = Record<TextureCubeFace, TextureSliceData>;
|
|
56
|
+
|
|
57
|
+
/** Array of textures */
|
|
58
|
+
export type Texture3DData = TextureSliceData[];
|
|
59
|
+
|
|
60
|
+
/** Array of textures */
|
|
61
|
+
export type TextureArrayData = TextureSliceData[];
|
|
62
|
+
|
|
63
|
+
/** Array of 6 face textures */
|
|
64
|
+
export type TextureCubeArrayData = Record<TextureCubeFace, TextureSliceData>[];
|
|
65
|
+
|
|
66
|
+
type TextureData =
|
|
67
|
+
| Texture1DData
|
|
68
|
+
| Texture3DData
|
|
69
|
+
| TextureArrayData
|
|
70
|
+
| TextureCubeArrayData
|
|
71
|
+
| TextureCubeData;
|
|
72
|
+
|
|
73
|
+
/** Sync data props */
|
|
74
|
+
export type TextureDataProps =
|
|
75
|
+
| {dimension: '1d'; data: Texture1DData | null}
|
|
76
|
+
| {dimension?: '2d'; data: Texture2DData | null}
|
|
77
|
+
| {dimension: '3d'; data: Texture3DData | null}
|
|
78
|
+
| {dimension: '2d-array'; data: TextureArrayData | null}
|
|
79
|
+
| {dimension: 'cube'; data: TextureCubeData | null}
|
|
80
|
+
| {dimension: 'cube-array'; data: TextureCubeArrayData | null};
|
|
81
|
+
|
|
82
|
+
/** Async data props */
|
|
83
|
+
export type TextureDataAsyncProps =
|
|
84
|
+
| {dimension: '1d'; data?: Promise<Texture1DData> | Texture1DData | null}
|
|
85
|
+
| {dimension?: '2d'; data?: Promise<Texture2DData> | Texture2DData | null}
|
|
86
|
+
| {dimension: '3d'; data?: Promise<Texture3DData> | Texture3DData | null}
|
|
87
|
+
| {dimension: '2d-array'; data?: Promise<TextureArrayData> | TextureArrayData | null}
|
|
88
|
+
| {dimension: 'cube'; data?: Promise<TextureCubeData> | TextureCubeData | null}
|
|
89
|
+
| {dimension: 'cube-array'; data?: Promise<TextureCubeArrayData> | TextureCubeArrayData | null};
|
|
90
|
+
|
|
91
|
+
/** Describes data for one sub resource (one mip level of one slice (depth or array layer)) */
|
|
92
|
+
export type TextureSubresource = {
|
|
93
|
+
/** slice (depth or array layer)) */
|
|
94
|
+
z: number;
|
|
95
|
+
/** mip level (0 - max mip levels) */
|
|
96
|
+
mipLevel: number;
|
|
97
|
+
} & (
|
|
98
|
+
| {
|
|
99
|
+
type: 'external-image';
|
|
100
|
+
image: ExternalImage;
|
|
101
|
+
/** @deprecated is this an appropriate place for this flag? */
|
|
102
|
+
flipY?: boolean;
|
|
103
|
+
}
|
|
104
|
+
| {
|
|
105
|
+
type: 'texture-data';
|
|
106
|
+
data: TextureImageData;
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
/** Check if texture data is a typed array */
|
|
111
|
+
export function isTextureSliceData(data: TextureData): data is TextureImageData {
|
|
112
|
+
const typedArray = (data as TextureImageData)?.data;
|
|
113
|
+
return ArrayBuffer.isView(typedArray);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function getFirstMipLevel(layer: TextureSliceData | null): TextureMipLevelData | null {
|
|
117
|
+
if (!layer) return null;
|
|
118
|
+
return Array.isArray(layer) ? (layer[0] ?? null) : layer;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function getTextureSizeFromData(
|
|
122
|
+
props: TextureDataProps
|
|
123
|
+
): {width: number; height: number} | null {
|
|
124
|
+
const {dimension, data} = props;
|
|
125
|
+
if (!data) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
switch (dimension) {
|
|
130
|
+
case '1d': {
|
|
131
|
+
const mipLevel = getFirstMipLevel(data);
|
|
132
|
+
if (!mipLevel) return null;
|
|
133
|
+
const {width} = getTextureMipLevelSize(mipLevel);
|
|
134
|
+
return {width, height: 1};
|
|
135
|
+
}
|
|
136
|
+
case '2d': {
|
|
137
|
+
const mipLevel = getFirstMipLevel(data);
|
|
138
|
+
return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
|
|
139
|
+
}
|
|
140
|
+
case '3d':
|
|
141
|
+
case '2d-array': {
|
|
142
|
+
if (!Array.isArray(data) || data.length === 0) return null;
|
|
143
|
+
const mipLevel = getFirstMipLevel(data[0]);
|
|
144
|
+
return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
|
|
145
|
+
}
|
|
146
|
+
case 'cube': {
|
|
147
|
+
const face = (Object.keys(data)[0] as TextureCubeFace) ?? null;
|
|
148
|
+
if (!face) return null;
|
|
149
|
+
const faceData = (data as Record<TextureCubeFace, TextureSliceData>)[face];
|
|
150
|
+
const mipLevel = getFirstMipLevel(faceData);
|
|
151
|
+
return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
|
|
152
|
+
}
|
|
153
|
+
case 'cube-array': {
|
|
154
|
+
if (!Array.isArray(data) || data.length === 0) return null;
|
|
155
|
+
const firstCube = data[0];
|
|
156
|
+
const face = (Object.keys(firstCube)[0] as TextureCubeFace) ?? null;
|
|
157
|
+
if (!face) return null;
|
|
158
|
+
const mipLevel = getFirstMipLevel(firstCube[face]);
|
|
159
|
+
return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
|
|
160
|
+
}
|
|
161
|
+
default:
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getTextureMipLevelSize(data: TextureMipLevelData): {width: number; height: number} {
|
|
167
|
+
if (isExternalImage(data)) {
|
|
168
|
+
return getExternalImageSize(data);
|
|
169
|
+
}
|
|
170
|
+
if (typeof data === 'object' && 'width' in data && 'height' in data) {
|
|
171
|
+
return {width: data.width, height: data.height};
|
|
172
|
+
}
|
|
173
|
+
throw new Error('Unsupported mip-level data');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Type guard: is a mip-level `TextureImageData` (vs ExternalImage) */
|
|
177
|
+
function isTextureImageData(data: TextureMipLevelData): data is TextureImageData {
|
|
178
|
+
return (
|
|
179
|
+
typeof data === 'object' &&
|
|
180
|
+
data !== null &&
|
|
181
|
+
'data' in data &&
|
|
182
|
+
'width' in data &&
|
|
183
|
+
'height' in data
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Resolve size for a single mip-level datum */
|
|
188
|
+
// function getTextureMipLevelSizeFromData(data: TextureMipLevelData): {
|
|
189
|
+
// width: number;
|
|
190
|
+
// height: number;
|
|
191
|
+
// } {
|
|
192
|
+
// if (this.device.isExternalImage(data)) {
|
|
193
|
+
// return this.device.getExternalImageSize(data);
|
|
194
|
+
// }
|
|
195
|
+
// if (this.isTextureImageData(data)) {
|
|
196
|
+
// return {width: data.width, height: data.height};
|
|
197
|
+
// }
|
|
198
|
+
// // Fallback (should not happen with current types)
|
|
199
|
+
// throw new Error('Unsupported mip-level data');
|
|
200
|
+
// }
|
|
201
|
+
|
|
202
|
+
/** Convert cube face label to depth index */
|
|
203
|
+
export function getCubeFaceIndex(face: TextureCubeFace): number {
|
|
204
|
+
const idx = TEXTURE_CUBE_FACE_MAP[face];
|
|
205
|
+
if (idx === undefined) throw new Error(`Invalid cube face: ${face}`);
|
|
206
|
+
return idx;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
|
|
210
|
+
export function getCubeArrayFaceIndex(cubeIndex: number, face: TextureCubeFace): number {
|
|
211
|
+
return 6 * cubeIndex + getCubeFaceIndex(face);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ------------------ Upload helpers ------------------
|
|
215
|
+
|
|
216
|
+
/** Experimental: Set multiple mip levels (1D) */
|
|
217
|
+
export function getTexture1DSubresources(data: Texture1DData): TextureSubresource[] {
|
|
218
|
+
// Not supported in WebGL; left explicit
|
|
219
|
+
throw new Error('setTexture1DData not supported in WebGL.');
|
|
220
|
+
// const subresources: TextureSubresource[] = [];
|
|
221
|
+
// return subresources;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Normalize 2D layer payload into an array of mip-level items */
|
|
225
|
+
function _normalizeTexture2DData(data: Texture2DData): (TextureImageData | ExternalImage)[] {
|
|
226
|
+
return Array.isArray(data) ? data : [data];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Experimental: Set multiple mip levels (2D), optionally at `z` (depth/array index) */
|
|
230
|
+
export function getTexture2DSubresources(
|
|
231
|
+
slice: number,
|
|
232
|
+
lodData: Texture2DData
|
|
233
|
+
): TextureSubresource[] {
|
|
234
|
+
const lodArray = _normalizeTexture2DData(lodData);
|
|
235
|
+
const z = slice;
|
|
236
|
+
|
|
237
|
+
const subresources: TextureSubresource[] = [];
|
|
238
|
+
|
|
239
|
+
for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
|
|
240
|
+
const imageData = lodArray[mipLevel];
|
|
241
|
+
if (isExternalImage(imageData)) {
|
|
242
|
+
subresources.push({
|
|
243
|
+
type: 'external-image',
|
|
244
|
+
image: imageData,
|
|
245
|
+
z,
|
|
246
|
+
mipLevel
|
|
247
|
+
});
|
|
248
|
+
} else if (isTextureImageData(imageData)) {
|
|
249
|
+
subresources.push({
|
|
250
|
+
type: 'texture-data',
|
|
251
|
+
data: imageData,
|
|
252
|
+
z,
|
|
253
|
+
mipLevel
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
throw new Error('Unsupported 2D mip-level payload');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return subresources;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** 3D: multiple depth slices, each may carry multiple mip levels */
|
|
264
|
+
export function getTexture3DSubresources(data: Texture3DData): TextureSubresource[] {
|
|
265
|
+
const subresources: TextureSubresource[] = [];
|
|
266
|
+
for (let depth = 0; depth < data.length; depth++) {
|
|
267
|
+
subresources.push(...getTexture2DSubresources(depth, data[depth]));
|
|
268
|
+
}
|
|
269
|
+
return subresources;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** 2D array: multiple layers, each may carry multiple mip levels */
|
|
273
|
+
export function getTextureArraySubresources(data: TextureArrayData): TextureSubresource[] {
|
|
274
|
+
const subresources: TextureSubresource[] = [];
|
|
275
|
+
for (let layer = 0; layer < data.length; layer++) {
|
|
276
|
+
subresources.push(...getTexture2DSubresources(layer, data[layer]));
|
|
277
|
+
}
|
|
278
|
+
return subresources;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Cube: 6 faces, each may carry multiple mip levels */
|
|
282
|
+
export function getTextureCubeSubresources(data: TextureCubeData): TextureSubresource[] {
|
|
283
|
+
const subresources: TextureSubresource[] = [];
|
|
284
|
+
for (const [face, faceData] of Object.entries(data) as [TextureCubeFace, TextureSliceData][]) {
|
|
285
|
+
const faceDepth = getCubeFaceIndex(face);
|
|
286
|
+
subresources.push(...getTexture2DSubresources(faceDepth, faceData));
|
|
287
|
+
}
|
|
288
|
+
return subresources;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
|
|
292
|
+
export function getTextureCubeArraySubresources(data: TextureCubeArrayData): TextureSubresource[] {
|
|
293
|
+
const subresources: TextureSubresource[] = [];
|
|
294
|
+
data.forEach((cubeData, cubeIndex) => {
|
|
295
|
+
for (const [face, faceData] of Object.entries(cubeData)) {
|
|
296
|
+
const faceDepth = getCubeArrayFaceIndex(cubeIndex, face as TextureCubeFace);
|
|
297
|
+
subresources.push(...getTexture2DSubresources(faceDepth, faceData));
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
return subresources;
|
|
301
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type {RenderPipelineProps, ComputePipelineProps} from '@luma.gl/core';
|
|
6
6
|
import {Device, RenderPipeline, ComputePipeline, log} from '@luma.gl/core';
|
|
7
|
+
import type {EngineModuleState} from '../types';
|
|
7
8
|
import {uid} from '../utils/uid';
|
|
8
9
|
|
|
9
10
|
export type PipelineFactoryProps = RenderPipelineProps;
|
|
@@ -19,9 +20,9 @@ export class PipelineFactory {
|
|
|
19
20
|
|
|
20
21
|
/** Get the singleton default pipeline factory for the specified device */
|
|
21
22
|
static getDefaultPipelineFactory(device: Device): PipelineFactory {
|
|
22
|
-
device.
|
|
23
|
-
|
|
24
|
-
return
|
|
23
|
+
const moduleData = device.getModuleData<EngineModuleState>('@luma.gl/engine');
|
|
24
|
+
moduleData.defaultPipelineFactory ||= new PipelineFactory(device);
|
|
25
|
+
return moduleData.defaultPipelineFactory;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
readonly device: Device;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {Device, Shader, ShaderProps, log} from '@luma.gl/core';
|
|
6
|
+
import type {EngineModuleState} from '../types';
|
|
6
7
|
|
|
7
8
|
/** Manages a cached pool of Shaders for reuse. */
|
|
8
9
|
export class ShaderFactory {
|
|
@@ -10,8 +11,9 @@ export class ShaderFactory {
|
|
|
10
11
|
|
|
11
12
|
/** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
|
|
12
13
|
static getDefaultShaderFactory(device: Device): ShaderFactory {
|
|
13
|
-
device.
|
|
14
|
-
|
|
14
|
+
const moduleData = device.getModuleData<EngineModuleState>('@luma.gl/engine');
|
|
15
|
+
moduleData.defaultShaderFactory ||= new ShaderFactory(device);
|
|
16
|
+
return moduleData.defaultShaderFactory;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
public readonly device: Device;
|
package/src/index.ts
CHANGED
|
@@ -83,17 +83,16 @@ export {Computation} from './compute/computation';
|
|
|
83
83
|
export type {
|
|
84
84
|
TextureCubeFace,
|
|
85
85
|
TextureImageData,
|
|
86
|
-
TextureData,
|
|
87
86
|
Texture1DData,
|
|
88
87
|
Texture2DData,
|
|
89
88
|
Texture3DData,
|
|
90
89
|
TextureCubeData,
|
|
91
90
|
TextureArrayData,
|
|
92
91
|
TextureCubeArrayData
|
|
93
|
-
} from './
|
|
92
|
+
} from './dynamic-texture/texture-data';
|
|
94
93
|
|
|
95
|
-
export type {
|
|
96
|
-
export {
|
|
94
|
+
export type {DynamicTextureProps} from './dynamic-texture/dynamic-texture';
|
|
95
|
+
export {DynamicTexture} from './dynamic-texture/dynamic-texture';
|
|
97
96
|
|
|
98
97
|
export {PickingManager} from './modules/picking/picking-manager';
|
|
99
98
|
export {picking as indexPicking} from './modules/picking/index-picking';
|
|
@@ -107,3 +106,9 @@ export {
|
|
|
107
106
|
// DEPRECATED
|
|
108
107
|
|
|
109
108
|
export {LegacyPickingManager} from './modules/picking/legacy-picking-manager';
|
|
109
|
+
|
|
110
|
+
import {DynamicTexture, type DynamicTextureProps} from './dynamic-texture/dynamic-texture';
|
|
111
|
+
/** @deprecated use DynamicTexture */
|
|
112
|
+
export const AsyncTexture = DynamicTexture;
|
|
113
|
+
/** @deprecated use DynamicTextureProps */
|
|
114
|
+
export type AsyncTextureProps = DynamicTextureProps;
|
package/src/model/model.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
} from '@luma.gl/core';
|
|
32
32
|
|
|
33
33
|
import type {ShaderModule, PlatformInfo} from '@luma.gl/shadertools';
|
|
34
|
-
import {ShaderAssembler
|
|
34
|
+
import {ShaderAssembler} from '@luma.gl/shadertools';
|
|
35
35
|
|
|
36
36
|
import type {Geometry} from '../geometry/geometry';
|
|
37
37
|
import {GPUGeometry, makeGPUGeometry} from '../geometry/gpu-geometry';
|
|
@@ -44,7 +44,7 @@ import {BufferLayoutHelper} from '../utils/buffer-layout-helper';
|
|
|
44
44
|
import {sortedBufferLayoutByShaderSourceLocations} from '../utils/buffer-layout-order';
|
|
45
45
|
import {uid} from '../utils/uid';
|
|
46
46
|
import {ShaderInputs} from '../shader-inputs';
|
|
47
|
-
import {
|
|
47
|
+
import {DynamicTexture} from '../dynamic-texture/dynamic-texture';
|
|
48
48
|
|
|
49
49
|
const LOG_DRAW_PRIORITY = 2;
|
|
50
50
|
const LOG_DRAW_TIMEOUT = 10000;
|
|
@@ -63,7 +63,7 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
|
|
|
63
63
|
/** Shader inputs, used to generated uniform buffers and bindings */
|
|
64
64
|
shaderInputs?: ShaderInputs;
|
|
65
65
|
/** Bindings */
|
|
66
|
-
bindings?: Record<string, Binding |
|
|
66
|
+
bindings?: Record<string, Binding | DynamicTexture>;
|
|
67
67
|
/** Parameters that are built into the pipeline */
|
|
68
68
|
parameters?: RenderPipelineParameters;
|
|
69
69
|
|
|
@@ -103,12 +103,19 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
106
|
+
* High level draw API for luma.gl.
|
|
107
|
+
*
|
|
108
|
+
* A `Model` encapsulates shaders, geometry attributes, bindings and render
|
|
109
|
+
* pipeline state into a single object. It automatically reuses and rebuilds
|
|
110
|
+
* pipelines as render parameters change and exposes convenient hooks for
|
|
111
|
+
* updating uniforms and attributes.
|
|
112
|
+
*
|
|
113
|
+
* Features:
|
|
114
|
+
* - Reuses and lazily recompiles {@link RenderPipeline | pipelines} as needed.
|
|
115
|
+
* - Integrates with `@luma.gl/shadertools` to assemble GLSL or WGSL from shader modules.
|
|
116
|
+
* - Manages geometry attributes and buffer bindings.
|
|
117
|
+
* - Accepts textures, samplers and uniform buffers as bindings, including `AsyncTexture`.
|
|
118
|
+
* - Provides detailed debug logging and optional shader source inspection.
|
|
112
119
|
*/
|
|
113
120
|
export class Model {
|
|
114
121
|
static defaultProps: Required<ModelProps> = {
|
|
@@ -141,16 +148,24 @@ export class Model {
|
|
|
141
148
|
disableWarnings: undefined!
|
|
142
149
|
};
|
|
143
150
|
|
|
151
|
+
/** Device that created this model */
|
|
144
152
|
readonly device: Device;
|
|
153
|
+
/** Application provided identifier */
|
|
145
154
|
readonly id: string;
|
|
155
|
+
/** WGSL shader source when using unified shader */
|
|
146
156
|
// @ts-expect-error assigned in function called from constructor
|
|
147
157
|
readonly source: string;
|
|
158
|
+
/** GLSL vertex shader source */
|
|
148
159
|
// @ts-expect-error assigned in function called from constructor
|
|
149
160
|
readonly vs: string;
|
|
161
|
+
/** GLSL fragment shader source */
|
|
150
162
|
// @ts-expect-error assigned in function called from constructor
|
|
151
163
|
readonly fs: string;
|
|
164
|
+
/** Factory used to create render pipelines */
|
|
152
165
|
readonly pipelineFactory: PipelineFactory;
|
|
166
|
+
/** Factory used to create shaders */
|
|
153
167
|
readonly shaderFactory: ShaderFactory;
|
|
168
|
+
/** User-supplied per-model data */
|
|
154
169
|
userData: {[key: string]: any} = {};
|
|
155
170
|
|
|
156
171
|
// Fixed properties (change can trigger pipeline rebuild)
|
|
@@ -179,7 +194,7 @@ export class Model {
|
|
|
179
194
|
/** Constant-valued attributes */
|
|
180
195
|
constantAttributes: Record<string, TypedArray> = {};
|
|
181
196
|
/** Bindings (textures, samplers, uniform buffers) */
|
|
182
|
-
bindings: Record<string, Binding |
|
|
197
|
+
bindings: Record<string, Binding | DynamicTexture> = {};
|
|
183
198
|
|
|
184
199
|
/**
|
|
185
200
|
* VertexArray
|
|
@@ -262,7 +277,8 @@ export class Model {
|
|
|
262
277
|
// @ts-expect-error
|
|
263
278
|
this._getModuleUniforms = getUniforms;
|
|
264
279
|
// Extract shader layout after modules have been added to WGSL source, to include any bindings added by modules
|
|
265
|
-
|
|
280
|
+
// @ts-expect-error Method on WebGPUDevice
|
|
281
|
+
this.props.shaderLayout ||= device.getShaderLayout(this.source);
|
|
266
282
|
} else {
|
|
267
283
|
// GLSL
|
|
268
284
|
const {vs, fs, getUniforms} = this.props.shaderAssembler.assembleGLSLShaderPair({
|
|
@@ -333,9 +349,6 @@ export class Model {
|
|
|
333
349
|
if (props.transformFeedback) {
|
|
334
350
|
this.transformFeedback = props.transformFeedback;
|
|
335
351
|
}
|
|
336
|
-
|
|
337
|
-
// Catch any access to non-standard props
|
|
338
|
-
Object.seal(this);
|
|
339
352
|
}
|
|
340
353
|
|
|
341
354
|
destroy(): void {
|
|
@@ -372,6 +385,7 @@ export class Model {
|
|
|
372
385
|
this._needsRedraw ||= reason;
|
|
373
386
|
}
|
|
374
387
|
|
|
388
|
+
/** Update uniforms and pipeline state prior to drawing. */
|
|
375
389
|
predraw(): void {
|
|
376
390
|
// Update uniform buffers if needed
|
|
377
391
|
this.updateShaderInputs();
|
|
@@ -379,6 +393,11 @@ export class Model {
|
|
|
379
393
|
this.pipeline = this._updatePipeline();
|
|
380
394
|
}
|
|
381
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Issue one draw call.
|
|
398
|
+
* @param renderPass - render pass to draw into
|
|
399
|
+
* @returns `true` if the draw call was executed, `false` if resources were not ready.
|
|
400
|
+
*/
|
|
382
401
|
draw(renderPass: RenderPass): boolean {
|
|
383
402
|
const loadingBinding = this._areBindingsLoading();
|
|
384
403
|
if (loadingBinding) {
|
|
@@ -571,7 +590,7 @@ export class Model {
|
|
|
571
590
|
/**
|
|
572
591
|
* Sets bindings (textures, samplers, uniform buffers)
|
|
573
592
|
*/
|
|
574
|
-
setBindings(bindings: Record<string, Binding |
|
|
593
|
+
setBindings(bindings: Record<string, Binding | DynamicTexture>): void {
|
|
575
594
|
Object.assign(this.bindings, bindings);
|
|
576
595
|
this.setNeedsRedraw('bindings');
|
|
577
596
|
}
|
|
@@ -678,7 +697,7 @@ export class Model {
|
|
|
678
697
|
/** Check that bindings are loaded. Returns id of first binding that is still loading. */
|
|
679
698
|
_areBindingsLoading(): string | false {
|
|
680
699
|
for (const binding of Object.values(this.bindings)) {
|
|
681
|
-
if (binding instanceof
|
|
700
|
+
if (binding instanceof DynamicTexture && !binding.isReady) {
|
|
682
701
|
return binding.id;
|
|
683
702
|
}
|
|
684
703
|
}
|
|
@@ -690,7 +709,7 @@ export class Model {
|
|
|
690
709
|
const validBindings: Record<string, Binding> = {};
|
|
691
710
|
|
|
692
711
|
for (const [name, binding] of Object.entries(this.bindings)) {
|
|
693
|
-
if (binding instanceof
|
|
712
|
+
if (binding instanceof DynamicTexture) {
|
|
694
713
|
// Check that async textures are loaded
|
|
695
714
|
if (binding.isReady) {
|
|
696
715
|
validBindings[name] = binding.texture;
|
|
@@ -711,7 +730,7 @@ export class Model {
|
|
|
711
730
|
timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
|
|
712
731
|
} else if (binding instanceof Buffer || binding instanceof Texture) {
|
|
713
732
|
timestamp = Math.max(timestamp, binding.updateTimestamp);
|
|
714
|
-
} else if (binding instanceof
|
|
733
|
+
} else if (binding instanceof DynamicTexture) {
|
|
715
734
|
timestamp = binding.texture
|
|
716
735
|
? Math.max(timestamp, binding.texture.updateTimestamp)
|
|
717
736
|
: // The texture will become available in the future
|