@luma.gl/engine 9.0.0-beta.5 → 9.0.0-beta.7
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/timeline.d.ts.map +1 -1
- package/dist/animation/timeline.js +3 -3
- package/dist/animation-loop/animation-loop-template.d.ts +1 -1
- package/dist/animation-loop/animation-loop-template.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop-template.js +3 -1
- package/dist/animation-loop/animation-loop.d.ts +2 -2
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +14 -6
- package/dist/animation-loop/animation-props.d.ts +2 -2
- package/dist/animation-loop/animation-props.d.ts.map +1 -1
- package/dist/animation-loop/make-animation-loop.d.ts +2 -2
- package/dist/animation-loop/make-animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/make-animation-loop.js +4 -2
- package/dist/computation.d.ts +95 -0
- package/dist/computation.d.ts.map +1 -0
- package/dist/computation.js +248 -0
- package/dist/debug/copy-texture-to-image.d.ts.map +1 -1
- package/dist/debug/copy-texture-to-image.js +5 -2
- package/dist/debug/debug-framebuffer.d.ts.map +1 -1
- package/dist/debug/debug-framebuffer.js +0 -1
- package/dist/debug/pixel-data-utils.d.ts.map +1 -1
- package/dist/debug/pixel-data-utils.js +2 -1
- package/dist/dist.dev.js +713 -329
- package/dist/geometries/cone-geometry.d.ts +1 -1
- package/dist/geometries/cone-geometry.d.ts.map +1 -1
- package/dist/geometries/cone-geometry.js +1 -1
- package/dist/geometries/cube-geometry.d.ts +1 -1
- package/dist/geometries/cube-geometry.d.ts.map +1 -1
- package/dist/geometries/cube-geometry.js +16 -14
- package/dist/geometries/cylinder-geometry.d.ts +1 -1
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
- package/dist/geometries/cylinder-geometry.js +1 -1
- package/dist/geometries/ico-sphere-geometry.d.ts +1 -1
- package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -1
- package/dist/geometries/ico-sphere-geometry.js +1 -1
- package/dist/geometries/plane-geometry.d.ts +1 -1
- package/dist/geometries/plane-geometry.d.ts.map +1 -1
- package/dist/geometries/plane-geometry.js +2 -2
- package/dist/geometries/sphere-geometry.d.ts +1 -1
- package/dist/geometries/sphere-geometry.d.ts.map +1 -1
- package/dist/geometries/sphere-geometry.js +1 -1
- package/dist/geometries/truncated-cone-geometry.d.ts +1 -1
- package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -1
- package/dist/geometries/truncated-cone-geometry.js +1 -1
- package/dist/geometry/geometry-table.d.ts.map +1 -1
- package/dist/geometry/geometry-table.js +3 -0
- package/dist/geometry/geometry.d.ts.map +1 -1
- package/dist/geometry/geometry.js +3 -0
- package/dist/geometry/gpu-geometry.d.ts +1 -1
- package/dist/geometry/gpu-geometry.d.ts.map +1 -1
- package/dist/geometry/gpu-geometry.js +4 -5
- package/dist/index.cjs +661 -291
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +43 -40
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -23
- package/dist/lib/clip-space.d.ts +1 -1
- package/dist/lib/clip-space.d.ts.map +1 -1
- package/dist/lib/clip-space.js +8 -10
- package/dist/lib/pipeline-factory.d.ts +10 -6
- package/dist/lib/pipeline-factory.d.ts.map +1 -1
- package/dist/lib/pipeline-factory.js +47 -22
- package/dist/lib/shader-factory.d.ts +17 -0
- package/dist/lib/shader-factory.d.ts.map +1 -0
- package/dist/lib/shader-factory.js +46 -0
- package/dist/model/model.d.ts +58 -45
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +213 -120
- package/dist/scenegraph/group-node.d.ts +1 -1
- package/dist/scenegraph/group-node.d.ts.map +1 -1
- package/dist/scenegraph/group-node.js +10 -5
- package/dist/scenegraph/model-node.d.ts +3 -3
- package/dist/scenegraph/model-node.d.ts.map +1 -1
- package/dist/scenegraph/model-node.js +2 -2
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
- package/dist/shader-inputs.d.ts.map +1 -1
- package/dist/shader-inputs.js +3 -0
- package/dist/transform/buffer-transform.d.ts +1 -1
- package/dist/transform/buffer-transform.d.ts.map +1 -1
- package/dist/transform/buffer-transform.js +7 -6
- package/dist/transform/texture-transform.d.ts +1 -1
- package/dist/transform/texture-transform.d.ts.map +1 -1
- package/dist/transform/texture-transform.js +10 -8
- package/dist.min.js +2 -2
- package/package.json +2 -2
- package/src/animation/timeline.ts +20 -20
- package/src/animation-loop/animation-loop-template.ts +10 -8
- package/src/animation-loop/animation-loop.ts +20 -10
- package/src/animation-loop/animation-props.ts +1 -1
- package/src/animation-loop/make-animation-loop.ts +17 -8
- package/src/computation.ts +346 -0
- package/src/debug/copy-texture-to-image.ts +8 -6
- package/src/debug/debug-framebuffer.ts +16 -3
- package/src/debug/debug-shader-layout.ts +1 -1
- package/src/debug/pixel-data-utils.ts +3 -6
- package/src/geometries/cube-geometry.ts +17 -13
- package/src/geometries/ico-sphere-geometry.ts +1 -1
- package/src/geometries/plane-geometry.ts +1 -1
- package/src/geometries/sphere-geometry.ts +1 -1
- package/src/geometries/truncated-cone-geometry.ts +2 -1
- package/src/geometry/geometry-table.ts +9 -6
- package/src/geometry/geometry-utils.ts +1 -1
- package/src/geometry/geometry.ts +9 -6
- package/src/geometry/gpu-geometry.ts +18 -11
- package/src/index.ts +3 -0
- package/src/lib/clip-space.ts +14 -18
- package/src/lib/pipeline-factory.ts +62 -28
- package/src/lib/shader-factory.ts +57 -0
- package/src/model/model.ts +249 -146
- package/src/scenegraph/group-node.ts +14 -10
- package/src/scenegraph/model-node.ts +2 -2
- package/src/scenegraph/scenegraph-node.ts +2 -2
- package/src/shader-inputs.ts +19 -12
- package/src/transform/buffer-transform.ts +15 -7
- package/src/transform/texture-transform.ts +14 -13
package/src/model/model.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
// luma.gl
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
2
3
|
// Copyright (c) vis.gl contributors
|
|
3
4
|
|
|
5
|
+
// A lot of imports, but then Model is where it all comes together...
|
|
4
6
|
import type {TypedArray, RenderPipelineProps, RenderPipelineParameters} from '@luma.gl/core';
|
|
5
|
-
import type {BufferLayout, VertexArray, TransformFeedback} from '@luma.gl/core';
|
|
7
|
+
import type {BufferLayout, Shader, VertexArray, TransformFeedback} from '@luma.gl/core';
|
|
6
8
|
import type {AttributeInfo, Binding, UniformValue, PrimitiveTopology} from '@luma.gl/core';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
UniformStore,
|
|
13
|
-
getTypedArrayFromDataType
|
|
14
|
-
} from '@luma.gl/core';
|
|
15
|
-
import {log, uid, deepEqual, splitUniformsAndBindings, isNumberArray} from '@luma.gl/core';
|
|
16
|
-
import {getAttributeInfosFromLayouts} from '@luma.gl/core';
|
|
9
|
+
import {Device, DeviceFeature, Buffer, Texture, TextureView, Sampler} from '@luma.gl/core';
|
|
10
|
+
import {RenderPipeline, RenderPass, UniformStore} from '@luma.gl/core';
|
|
11
|
+
import {log, uid, deepEqual, isObjectEmpty, splitUniformsAndBindings} from '@luma.gl/core';
|
|
12
|
+
import {getTypedArrayFromDataType, getAttributeInfosFromLayouts} from '@luma.gl/core';
|
|
13
|
+
|
|
17
14
|
import type {ShaderModule, PlatformInfo} from '@luma.gl/shadertools';
|
|
18
15
|
import {ShaderAssembler, getShaderLayoutFromWGSL} from '@luma.gl/shadertools';
|
|
19
|
-
|
|
16
|
+
|
|
20
17
|
import type {Geometry} from '../geometry/geometry';
|
|
21
18
|
import {GPUGeometry, makeGPUGeometry} from '../geometry/gpu-geometry';
|
|
19
|
+
import {ShaderInputs} from '../shader-inputs';
|
|
22
20
|
import {PipelineFactory} from '../lib/pipeline-factory';
|
|
21
|
+
import {ShaderFactory} from '../lib/shader-factory';
|
|
23
22
|
import {getDebugTableForShaderLayout} from '../debug/debug-shader-layout';
|
|
24
23
|
import {debugFramebuffer} from '../debug/debug-framebuffer';
|
|
25
24
|
|
|
@@ -28,8 +27,9 @@ const LOG_DRAW_TIMEOUT = 10000;
|
|
|
28
27
|
|
|
29
28
|
export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs'> & {
|
|
30
29
|
source?: string;
|
|
31
|
-
vs:
|
|
32
|
-
fs:
|
|
30
|
+
vs: string | null;
|
|
31
|
+
fs: string | null;
|
|
32
|
+
|
|
33
33
|
/** shadertool shader modules (added to shader code) */
|
|
34
34
|
modules?: ShaderModule[];
|
|
35
35
|
/** Shadertool module defines (configures shader code)*/
|
|
@@ -38,10 +38,6 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs'> & {
|
|
|
38
38
|
|
|
39
39
|
/** Shader inputs, used to generated uniform buffers and bindings */
|
|
40
40
|
shaderInputs?: ShaderInputs;
|
|
41
|
-
/** pipeline factory to use to create render pipelines. Defaults to default factory for the device */
|
|
42
|
-
pipelineFactory?: PipelineFactory;
|
|
43
|
-
/** Shader assembler. Defaults to the ShaderAssembler.getShaderAssembler() */
|
|
44
|
-
shaderAssembler?: ShaderAssembler;
|
|
45
41
|
|
|
46
42
|
/** Parameters that are built into the pipeline */
|
|
47
43
|
parameters?: RenderPipelineParameters;
|
|
@@ -59,6 +55,8 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs'> & {
|
|
|
59
55
|
attributes?: Record<string, Buffer>;
|
|
60
56
|
/** */
|
|
61
57
|
constantAttributes?: Record<string, TypedArray>;
|
|
58
|
+
/** Some applications intentionally supply unused attributes */
|
|
59
|
+
ignoreUnknownAttributes?: boolean;
|
|
62
60
|
|
|
63
61
|
/** @internal For use with {@link TransformFeedback}, WebGL only. */
|
|
64
62
|
varyings?: string[];
|
|
@@ -67,6 +65,16 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs'> & {
|
|
|
67
65
|
|
|
68
66
|
/** Mapped uniforms for shadertool modules */
|
|
69
67
|
moduleSettings?: Record<string, Record<string, any>>;
|
|
68
|
+
|
|
69
|
+
/** Show shader source in browser? */
|
|
70
|
+
debugShaders?: 'never' | 'errors' | 'warnings' | 'always';
|
|
71
|
+
|
|
72
|
+
/** Factory used to create a {@link RenderPipeline}. Defaults to {@link Device} default factory. */
|
|
73
|
+
pipelineFactory?: PipelineFactory;
|
|
74
|
+
/** Factory used to create a {@link Shader}. Defaults to {@link Device} default factory. */
|
|
75
|
+
shaderFactory?: ShaderFactory;
|
|
76
|
+
/** Shader assembler. Defaults to the ShaderAssembler.getShaderAssembler() */
|
|
77
|
+
shaderAssembler?: ShaderAssembler;
|
|
70
78
|
};
|
|
71
79
|
|
|
72
80
|
/**
|
|
@@ -97,15 +105,21 @@ export class Model {
|
|
|
97
105
|
|
|
98
106
|
shaderInputs: undefined!,
|
|
99
107
|
pipelineFactory: undefined!,
|
|
108
|
+
shaderFactory: undefined!,
|
|
100
109
|
transformFeedback: undefined,
|
|
101
|
-
shaderAssembler: ShaderAssembler.getDefaultShaderAssembler()
|
|
110
|
+
shaderAssembler: ShaderAssembler.getDefaultShaderAssembler(),
|
|
111
|
+
|
|
112
|
+
debugShaders: undefined,
|
|
113
|
+
ignoreUnknownAttributes: undefined
|
|
102
114
|
};
|
|
103
115
|
|
|
104
116
|
readonly device: Device;
|
|
105
117
|
readonly id: string;
|
|
118
|
+
readonly source: string;
|
|
106
119
|
readonly vs: string;
|
|
107
120
|
readonly fs: string;
|
|
108
121
|
readonly pipelineFactory: PipelineFactory;
|
|
122
|
+
readonly shaderFactory: ShaderFactory;
|
|
109
123
|
userData: {[key: string]: any} = {};
|
|
110
124
|
|
|
111
125
|
// Fixed properties (change can trigger pipeline rebuild)
|
|
@@ -154,12 +168,18 @@ export class Model {
|
|
|
154
168
|
|
|
155
169
|
_uniformStore: UniformStore;
|
|
156
170
|
|
|
157
|
-
_pipelineNeedsUpdate: string | false = 'newly created';
|
|
158
171
|
_attributeInfos: Record<string, AttributeInfo> = {};
|
|
159
172
|
_gpuGeometry: GPUGeometry | null = null;
|
|
160
173
|
private _getModuleUniforms: (props?: Record<string, Record<string, any>>) => Record<string, any>;
|
|
161
174
|
private props: Required<ModelProps>;
|
|
162
175
|
|
|
176
|
+
_pipelineNeedsUpdate: string | false = 'newly created';
|
|
177
|
+
private _needsRedraw: string | false = 'initializing';
|
|
178
|
+
private _destroyed = false;
|
|
179
|
+
|
|
180
|
+
/** "Time" of last draw. Monotonically increasing timestamp */
|
|
181
|
+
_lastDrawTimestamp: number = -1;
|
|
182
|
+
|
|
163
183
|
constructor(device: Device, props: ModelProps) {
|
|
164
184
|
this.props = {...Model.defaultProps, ...props};
|
|
165
185
|
props = this.props;
|
|
@@ -174,22 +194,6 @@ export class Model {
|
|
|
174
194
|
);
|
|
175
195
|
this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
|
|
176
196
|
|
|
177
|
-
const isWebGPU = this.device.info.type === 'webgpu';
|
|
178
|
-
// TODO - hack to support unified WGSL shader
|
|
179
|
-
// TODO - this is wrong, compile a single shader
|
|
180
|
-
if (this.props.source) {
|
|
181
|
-
if (isWebGPU) {
|
|
182
|
-
this.props.shaderLayout ||= getShaderLayoutFromWGSL(this.props.source);
|
|
183
|
-
}
|
|
184
|
-
this.props.fs = this.props.source;
|
|
185
|
-
this.props.vs = this.props.source;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Support WGSL shader layout introspection
|
|
189
|
-
if (isWebGPU && typeof this.props.vs !== 'string') {
|
|
190
|
-
this.props.shaderLayout ||= getShaderLayoutFromWGSL(this.props.vs.wgsl);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
197
|
// Setup shader assembler
|
|
194
198
|
const platformInfo = getPlatformInfo(device);
|
|
195
199
|
|
|
@@ -197,15 +201,33 @@ export class Model {
|
|
|
197
201
|
const modules =
|
|
198
202
|
(this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
|
|
199
203
|
|
|
200
|
-
const
|
|
201
|
-
platformInfo,
|
|
202
|
-
...this.props,
|
|
203
|
-
modules
|
|
204
|
-
});
|
|
204
|
+
const isWebGPU = this.device.type === 'webgpu';
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this
|
|
206
|
+
// WebGPU
|
|
207
|
+
// TODO - hack to support unified WGSL shader
|
|
208
|
+
// TODO - this is wrong, compile a single shader
|
|
209
|
+
if (isWebGPU && this.props.source) {
|
|
210
|
+
// WGSL
|
|
211
|
+
this.props.shaderLayout ||= getShaderLayoutFromWGSL(this.props.source);
|
|
212
|
+
const {source, getUniforms} = this.props.shaderAssembler.assembleShader({
|
|
213
|
+
platformInfo,
|
|
214
|
+
...this.props,
|
|
215
|
+
modules
|
|
216
|
+
});
|
|
217
|
+
this.source = source;
|
|
218
|
+
this._getModuleUniforms = getUniforms;
|
|
219
|
+
} else {
|
|
220
|
+
// GLSL
|
|
221
|
+
const {vs, fs, getUniforms} = this.props.shaderAssembler.assembleShaderPair({
|
|
222
|
+
platformInfo,
|
|
223
|
+
...this.props,
|
|
224
|
+
modules
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
this.vs = vs;
|
|
228
|
+
this.fs = fs;
|
|
229
|
+
this._getModuleUniforms = getUniforms;
|
|
230
|
+
}
|
|
209
231
|
|
|
210
232
|
this.vertexCount = this.props.vertexCount;
|
|
211
233
|
this.instanceCount = this.props.instanceCount;
|
|
@@ -216,11 +238,12 @@ export class Model {
|
|
|
216
238
|
|
|
217
239
|
// Geometry, if provided, sets topology and vertex cound
|
|
218
240
|
if (props.geometry) {
|
|
219
|
-
this.
|
|
241
|
+
this.setGeometry(props.geometry);
|
|
220
242
|
}
|
|
221
243
|
|
|
222
244
|
this.pipelineFactory =
|
|
223
245
|
props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
|
|
246
|
+
this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
|
|
224
247
|
|
|
225
248
|
// Create the pipeline
|
|
226
249
|
// @note order is important
|
|
@@ -250,7 +273,9 @@ export class Model {
|
|
|
250
273
|
this.setIndexBuffer(props.indexBuffer);
|
|
251
274
|
}
|
|
252
275
|
if (props.attributes) {
|
|
253
|
-
this.setAttributes(props.attributes
|
|
276
|
+
this.setAttributes(props.attributes, {
|
|
277
|
+
ignoreUnknownAttributes: props.ignoreUnknownAttributes
|
|
278
|
+
});
|
|
254
279
|
}
|
|
255
280
|
if (props.constantAttributes) {
|
|
256
281
|
this.setConstantAttributes(props.constantAttributes);
|
|
@@ -269,44 +294,73 @@ export class Model {
|
|
|
269
294
|
this.transformFeedback = props.transformFeedback;
|
|
270
295
|
}
|
|
271
296
|
|
|
272
|
-
// TODO - restore?
|
|
273
|
-
// this.setUniforms(this._getModuleUniforms()); // Get all default module uniforms
|
|
274
|
-
|
|
275
297
|
// Catch any access to non-standard props
|
|
276
298
|
Object.seal(this);
|
|
277
299
|
}
|
|
278
300
|
|
|
279
301
|
destroy(): void {
|
|
302
|
+
if (this._destroyed) return;
|
|
280
303
|
this.pipelineFactory.release(this.pipeline);
|
|
304
|
+
this.shaderFactory.release(this.pipeline.vs);
|
|
305
|
+
if (this.pipeline.fs) {
|
|
306
|
+
this.shaderFactory.release(this.pipeline.fs);
|
|
307
|
+
}
|
|
281
308
|
this._uniformStore.destroy();
|
|
309
|
+
// TODO - mark resource as managed and destroyIfManaged() ?
|
|
310
|
+
this._gpuGeometry?.destroy();
|
|
311
|
+
this._destroyed = true;
|
|
282
312
|
}
|
|
283
313
|
|
|
284
314
|
// Draw call
|
|
285
315
|
|
|
316
|
+
/** Query redraw status. Clears the status. */
|
|
317
|
+
needsRedraw(): false | string {
|
|
318
|
+
// Catch any writes to already bound resources
|
|
319
|
+
if (this._getBindingsUpdateTimestamp() > this._lastDrawTimestamp) {
|
|
320
|
+
this.setNeedsRedraw('contents of bound textures or buffers updated');
|
|
321
|
+
}
|
|
322
|
+
const needsRedraw = this._needsRedraw;
|
|
323
|
+
this._needsRedraw = false;
|
|
324
|
+
return needsRedraw;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Mark the model as needing a redraw */
|
|
328
|
+
setNeedsRedraw(reason: string): void {
|
|
329
|
+
this._needsRedraw ||= reason;
|
|
330
|
+
}
|
|
331
|
+
|
|
286
332
|
predraw() {
|
|
287
333
|
// Update uniform buffers if needed
|
|
288
334
|
this.updateShaderInputs();
|
|
335
|
+
// Check if the pipeline is invalidated
|
|
336
|
+
this.pipeline = this._updatePipeline();
|
|
289
337
|
}
|
|
290
338
|
|
|
291
|
-
draw(renderPass: RenderPass):
|
|
339
|
+
draw(renderPass: RenderPass): boolean {
|
|
292
340
|
this.predraw();
|
|
293
341
|
|
|
342
|
+
let drawSuccess: boolean;
|
|
294
343
|
try {
|
|
295
344
|
this._logDrawCallStart();
|
|
296
345
|
|
|
297
|
-
//
|
|
298
|
-
// TODO -
|
|
346
|
+
// Update the pipeline if invalidated
|
|
347
|
+
// TODO - inside RenderPass is likely the worst place to do this from performance perspective.
|
|
348
|
+
// Application can call Model.predraw() to avoid this.
|
|
299
349
|
this.pipeline = this._updatePipeline();
|
|
300
350
|
|
|
301
351
|
// Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
|
|
302
352
|
// Any caching needs to be done inside the pipeline functions
|
|
303
353
|
this.pipeline.setBindings(this.bindings);
|
|
304
|
-
|
|
354
|
+
if (!isObjectEmpty(this.uniforms)) {
|
|
355
|
+
this.pipeline.setUniformsWebGL(this.uniforms);
|
|
356
|
+
}
|
|
305
357
|
|
|
306
358
|
const {indexBuffer} = this.vertexArray;
|
|
307
|
-
const indexCount = indexBuffer
|
|
308
|
-
|
|
309
|
-
|
|
359
|
+
const indexCount = indexBuffer
|
|
360
|
+
? indexBuffer.byteLength / (indexBuffer.indexType === 'uint32' ? 4 : 2)
|
|
361
|
+
: undefined;
|
|
362
|
+
|
|
363
|
+
drawSuccess = this.pipeline.draw({
|
|
310
364
|
renderPass,
|
|
311
365
|
vertexArray: this.vertexArray,
|
|
312
366
|
vertexCount: this.vertexCount,
|
|
@@ -318,6 +372,15 @@ export class Model {
|
|
|
318
372
|
this._logDrawCallEnd();
|
|
319
373
|
}
|
|
320
374
|
this._logFramebuffer(renderPass);
|
|
375
|
+
|
|
376
|
+
// Update needsRedraw flag
|
|
377
|
+
if (drawSuccess) {
|
|
378
|
+
this._lastDrawTimestamp = this.device.timestamp;
|
|
379
|
+
this._needsRedraw = false;
|
|
380
|
+
} else {
|
|
381
|
+
this._needsRedraw = 'waiting for resource initialization';
|
|
382
|
+
}
|
|
383
|
+
return drawSuccess;
|
|
321
384
|
}
|
|
322
385
|
|
|
323
386
|
// Update fixed fields (can trigger pipeline rebuild)
|
|
@@ -327,38 +390,17 @@ export class Model {
|
|
|
327
390
|
* Geometry, set topology and bufferLayout
|
|
328
391
|
* @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
|
|
329
392
|
*/
|
|
330
|
-
setGeometry(geometry: GPUGeometry | Geometry):
|
|
393
|
+
setGeometry(geometry: GPUGeometry | Geometry | null): void {
|
|
394
|
+
this._gpuGeometry?.destroy();
|
|
331
395
|
const gpuGeometry = geometry && makeGPUGeometry(this.device, geometry);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
this.
|
|
336
|
-
|
|
337
|
-
return gpuGeometry;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Updates the optional geometry attributes
|
|
342
|
-
* Geometry, sets several attributes, indexBuffer, and also vertex count
|
|
343
|
-
* @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
|
|
344
|
-
*/
|
|
345
|
-
_setGeometryAttributes(gpuGeometry: GPUGeometry): void {
|
|
346
|
-
// Filter geometry attribute so that we don't issue warnings for unused attributes
|
|
347
|
-
const attributes = {...gpuGeometry.attributes};
|
|
348
|
-
for (const [attributeName] of Object.entries(attributes)) {
|
|
349
|
-
if (
|
|
350
|
-
!this.pipeline.shaderLayout.attributes.find(layout => layout.name === attributeName) &&
|
|
351
|
-
attributeName !== 'positions'
|
|
352
|
-
) {
|
|
353
|
-
delete attributes[attributeName];
|
|
396
|
+
if (gpuGeometry) {
|
|
397
|
+
this.setTopology(gpuGeometry.topology || 'triangle-list');
|
|
398
|
+
this.bufferLayout = mergeBufferLayouts(gpuGeometry.bufferLayout, this.bufferLayout);
|
|
399
|
+
if (this.vertexArray) {
|
|
400
|
+
this._setGeometryAttributes(gpuGeometry);
|
|
354
401
|
}
|
|
355
402
|
}
|
|
356
|
-
|
|
357
|
-
// TODO - delete previous geometry?
|
|
358
|
-
this.vertexCount = gpuGeometry.vertexCount;
|
|
359
|
-
this.setIndexBuffer(gpuGeometry.indices);
|
|
360
|
-
this.setAttributes(gpuGeometry.attributes, 'ignore-unknown');
|
|
361
|
-
this.setAttributes(attributes);
|
|
403
|
+
this._gpuGeometry = gpuGeometry;
|
|
362
404
|
}
|
|
363
405
|
|
|
364
406
|
/**
|
|
@@ -374,13 +416,12 @@ export class Model {
|
|
|
374
416
|
|
|
375
417
|
/**
|
|
376
418
|
* Updates the buffer layout.
|
|
377
|
-
* @note Triggers a pipeline rebuild / pipeline cache fetch
|
|
419
|
+
* @note Triggers a pipeline rebuild / pipeline cache fetch
|
|
378
420
|
*/
|
|
379
421
|
setBufferLayout(bufferLayout: BufferLayout[]): void {
|
|
380
422
|
this.bufferLayout = this._gpuGeometry
|
|
381
423
|
? mergeBufferLayouts(bufferLayout, this._gpuGeometry.bufferLayout)
|
|
382
424
|
: bufferLayout;
|
|
383
|
-
this._setPipelineNeedsUpdate('bufferLayout');
|
|
384
425
|
|
|
385
426
|
// Recreate the pipeline
|
|
386
427
|
this.pipeline = this._updatePipeline();
|
|
@@ -395,6 +436,8 @@ export class Model {
|
|
|
395
436
|
if (this._gpuGeometry) {
|
|
396
437
|
this._setGeometryAttributes(this._gpuGeometry);
|
|
397
438
|
}
|
|
439
|
+
|
|
440
|
+
this._setPipelineNeedsUpdate('bufferLayout');
|
|
398
441
|
}
|
|
399
442
|
|
|
400
443
|
/**
|
|
@@ -417,6 +460,7 @@ export class Model {
|
|
|
417
460
|
*/
|
|
418
461
|
setVertexCount(vertexCount: number): void {
|
|
419
462
|
this.vertexCount = vertexCount;
|
|
463
|
+
this.setNeedsRedraw('vertexCount');
|
|
420
464
|
}
|
|
421
465
|
|
|
422
466
|
/**
|
|
@@ -425,6 +469,7 @@ export class Model {
|
|
|
425
469
|
*/
|
|
426
470
|
setInstanceCount(instanceCount: number): void {
|
|
427
471
|
this.instanceCount = instanceCount;
|
|
472
|
+
this.setNeedsRedraw('instanceCount');
|
|
428
473
|
}
|
|
429
474
|
|
|
430
475
|
setShaderInputs(shaderInputs: ShaderInputs): void {
|
|
@@ -435,39 +480,13 @@ export class Model {
|
|
|
435
480
|
const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
|
|
436
481
|
this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
|
|
437
482
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Updates shader module settings (which results in uniforms being set)
|
|
442
|
-
*/
|
|
443
|
-
setShaderModuleProps(props: Record<string, any>): void {
|
|
444
|
-
const uniforms = this._getModuleUniforms(props);
|
|
445
|
-
|
|
446
|
-
// Extract textures & framebuffers set by the modules
|
|
447
|
-
// TODO better way to extract bindings
|
|
448
|
-
const keys = Object.keys(uniforms).filter(k => {
|
|
449
|
-
const uniform = uniforms[k];
|
|
450
|
-
return !isNumberArray(uniform) && typeof uniform !== 'number' && typeof uniform !== 'boolean';
|
|
451
|
-
});
|
|
452
|
-
const bindings: Record<string, Binding> = {};
|
|
453
|
-
for (const k of keys) {
|
|
454
|
-
bindings[k] = uniforms[k];
|
|
455
|
-
delete uniforms[k];
|
|
456
|
-
}
|
|
483
|
+
this.setNeedsRedraw('shaderInputs');
|
|
457
484
|
}
|
|
458
485
|
|
|
459
486
|
updateShaderInputs(): void {
|
|
460
487
|
this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* @deprecated Updates shader module settings (which results in uniforms being set)
|
|
465
|
-
*/
|
|
466
|
-
updateModuleSettings(props: Record<string, any>): void {
|
|
467
|
-
log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')();
|
|
468
|
-
const {bindings, uniforms} = splitUniformsAndBindings(this._getModuleUniforms(props));
|
|
469
|
-
Object.assign(this.bindings, bindings);
|
|
470
|
-
Object.assign(this.uniforms, uniforms);
|
|
488
|
+
// TODO - this is already tracked through buffer/texture update times?
|
|
489
|
+
this.setNeedsRedraw('shaderInputs');
|
|
471
490
|
}
|
|
472
491
|
|
|
473
492
|
/**
|
|
@@ -475,17 +494,15 @@ export class Model {
|
|
|
475
494
|
*/
|
|
476
495
|
setBindings(bindings: Record<string, Binding>): void {
|
|
477
496
|
Object.assign(this.bindings, bindings);
|
|
497
|
+
this.setNeedsRedraw('bindings');
|
|
478
498
|
}
|
|
479
499
|
|
|
480
500
|
/**
|
|
481
|
-
*
|
|
482
|
-
* @deprecated WebGL only, use uniform buffers for portability
|
|
483
|
-
* @param uniforms
|
|
484
|
-
* @returns self for chaining
|
|
501
|
+
* Updates optional transform feedback. WebGL only.
|
|
485
502
|
*/
|
|
486
|
-
|
|
487
|
-
this.
|
|
488
|
-
|
|
503
|
+
setTransformFeedback(transformFeedback: TransformFeedback | null): void {
|
|
504
|
+
this.transformFeedback = transformFeedback;
|
|
505
|
+
this.setNeedsRedraw('transformFeedback');
|
|
489
506
|
}
|
|
490
507
|
|
|
491
508
|
/**
|
|
@@ -494,27 +511,26 @@ export class Model {
|
|
|
494
511
|
*/
|
|
495
512
|
setIndexBuffer(indexBuffer: Buffer | null): void {
|
|
496
513
|
this.vertexArray.setIndexBuffer(indexBuffer);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Updates optional transform feedback. WebGL only.
|
|
501
|
-
*/
|
|
502
|
-
setTransformFeedback(transformFeedback: TransformFeedback | null): void {
|
|
503
|
-
this.transformFeedback = transformFeedback;
|
|
514
|
+
this.setNeedsRedraw('indexBuffer');
|
|
504
515
|
}
|
|
505
516
|
|
|
506
517
|
/**
|
|
507
518
|
* Sets attributes (buffers)
|
|
508
519
|
* @note Overrides any attributes previously set with the same name
|
|
509
520
|
*/
|
|
510
|
-
setAttributes(
|
|
521
|
+
setAttributes(
|
|
522
|
+
buffers: Record<string, Buffer>,
|
|
523
|
+
options?: {ignoreUnknownAttributes?: boolean}
|
|
524
|
+
): void {
|
|
511
525
|
if (buffers.indices) {
|
|
512
526
|
log.warn(
|
|
513
527
|
`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
|
|
514
528
|
)();
|
|
515
529
|
}
|
|
516
530
|
for (const [bufferName, buffer] of Object.entries(buffers)) {
|
|
517
|
-
const bufferLayout = this.bufferLayout.find(layout =>
|
|
531
|
+
const bufferLayout = this.bufferLayout.find(layout =>
|
|
532
|
+
getAttributeNames(layout).includes(bufferName)
|
|
533
|
+
);
|
|
518
534
|
if (!bufferLayout) {
|
|
519
535
|
log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
|
|
520
536
|
continue; // eslint-disable-line no-continue
|
|
@@ -530,12 +546,13 @@ export class Model {
|
|
|
530
546
|
set = true;
|
|
531
547
|
}
|
|
532
548
|
}
|
|
533
|
-
if (!set &&
|
|
549
|
+
if (!set && (options?.ignoreUnknownAttributes || this.props.ignoreUnknownAttributes)) {
|
|
534
550
|
log.warn(
|
|
535
551
|
`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
|
|
536
552
|
)();
|
|
537
553
|
}
|
|
538
554
|
}
|
|
555
|
+
this.setNeedsRedraw('attributes');
|
|
539
556
|
}
|
|
540
557
|
|
|
541
558
|
/**
|
|
@@ -550,43 +567,125 @@ export class Model {
|
|
|
550
567
|
for (const [attributeName, value] of Object.entries(attributes)) {
|
|
551
568
|
const attributeInfo = this._attributeInfos[attributeName];
|
|
552
569
|
if (attributeInfo) {
|
|
553
|
-
this.vertexArray.
|
|
570
|
+
this.vertexArray.setConstantWebGL(attributeInfo.location, value);
|
|
554
571
|
} else {
|
|
555
572
|
log.warn(
|
|
556
573
|
`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
|
|
557
574
|
)();
|
|
558
575
|
}
|
|
559
576
|
}
|
|
577
|
+
this.setNeedsRedraw('constants');
|
|
560
578
|
}
|
|
561
579
|
|
|
580
|
+
// DEPRECATED METHODS
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Sets individual uniforms
|
|
584
|
+
* @deprecated WebGL only, use uniform buffers for portability
|
|
585
|
+
* @param uniforms
|
|
586
|
+
*/
|
|
587
|
+
setUniforms(uniforms: Record<string, UniformValue>): void {
|
|
588
|
+
if (!isObjectEmpty(uniforms)) {
|
|
589
|
+
this.pipeline.setUniformsWebGL(uniforms);
|
|
590
|
+
Object.assign(this.uniforms, uniforms);
|
|
591
|
+
}
|
|
592
|
+
this.setNeedsRedraw('uniforms');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* @deprecated Updates shader module settings (which results in uniforms being set)
|
|
597
|
+
*/
|
|
598
|
+
updateModuleSettings(props: Record<string, any>): void {
|
|
599
|
+
log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')();
|
|
600
|
+
const {bindings, uniforms} = splitUniformsAndBindings(this._getModuleUniforms(props));
|
|
601
|
+
Object.assign(this.bindings, bindings);
|
|
602
|
+
Object.assign(this.uniforms, uniforms);
|
|
603
|
+
this.setNeedsRedraw('moduleSettings');
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Internal methods
|
|
607
|
+
|
|
608
|
+
/** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
|
|
609
|
+
_getBindingsUpdateTimestamp(): number {
|
|
610
|
+
let timestamp = 0;
|
|
611
|
+
for (const binding of Object.values(this.bindings)) {
|
|
612
|
+
if (binding instanceof TextureView) {
|
|
613
|
+
timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
|
|
614
|
+
} else if (binding instanceof Buffer || binding instanceof Texture) {
|
|
615
|
+
timestamp = Math.max(timestamp, binding.updateTimestamp);
|
|
616
|
+
} else if (!(binding instanceof Sampler)) {
|
|
617
|
+
timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return timestamp;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Updates the optional geometry attributes
|
|
625
|
+
* Geometry, sets several attributes, indexBuffer, and also vertex count
|
|
626
|
+
* @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
|
|
627
|
+
*/
|
|
628
|
+
_setGeometryAttributes(gpuGeometry: GPUGeometry): void {
|
|
629
|
+
// Filter geometry attribute so that we don't issue warnings for unused attributes
|
|
630
|
+
const attributes = {...gpuGeometry.attributes};
|
|
631
|
+
for (const [attributeName] of Object.entries(attributes)) {
|
|
632
|
+
if (
|
|
633
|
+
!this.pipeline.shaderLayout.attributes.find(layout => layout.name === attributeName) &&
|
|
634
|
+
attributeName !== 'positions'
|
|
635
|
+
) {
|
|
636
|
+
delete attributes[attributeName];
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// TODO - delete previous geometry?
|
|
641
|
+
this.vertexCount = gpuGeometry.vertexCount;
|
|
642
|
+
this.setIndexBuffer(gpuGeometry.indices);
|
|
643
|
+
this.setAttributes(gpuGeometry.attributes, {ignoreUnknownAttributes: true});
|
|
644
|
+
this.setAttributes(attributes, {ignoreUnknownAttributes: this.props.ignoreUnknownAttributes});
|
|
645
|
+
|
|
646
|
+
this.setNeedsRedraw('geometry attributes');
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** Mark pipeline as needing update */
|
|
562
650
|
_setPipelineNeedsUpdate(reason: string): void {
|
|
563
|
-
this._pipelineNeedsUpdate
|
|
651
|
+
this._pipelineNeedsUpdate ||= reason;
|
|
652
|
+
this.setNeedsRedraw(reason);
|
|
564
653
|
}
|
|
565
654
|
|
|
655
|
+
/** Update pipeline if needed */
|
|
566
656
|
_updatePipeline(): RenderPipeline {
|
|
567
657
|
if (this._pipelineNeedsUpdate) {
|
|
658
|
+
let prevShaderVs: Shader | null = null;
|
|
659
|
+
let prevShaderFs: Shader | null = null;
|
|
568
660
|
if (this.pipeline) {
|
|
569
661
|
log.log(
|
|
570
662
|
1,
|
|
571
663
|
`Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
|
|
572
664
|
)();
|
|
665
|
+
prevShaderVs = this.pipeline.vs;
|
|
666
|
+
prevShaderFs = this.pipeline.fs;
|
|
573
667
|
}
|
|
574
668
|
|
|
575
669
|
this._pipelineNeedsUpdate = false;
|
|
576
670
|
|
|
577
|
-
const vs = this.
|
|
671
|
+
const vs = this.shaderFactory.createShader({
|
|
578
672
|
id: `${this.id}-vertex`,
|
|
579
673
|
stage: 'vertex',
|
|
580
|
-
source: this.vs
|
|
674
|
+
source: this.source || this.vs,
|
|
675
|
+
debug: this.props.debugShaders
|
|
581
676
|
});
|
|
582
677
|
|
|
583
|
-
|
|
584
|
-
|
|
678
|
+
let fs: Shader | null = null;
|
|
679
|
+
if (this.source) {
|
|
680
|
+
fs = vs;
|
|
681
|
+
} else if (this.fs) {
|
|
682
|
+
fs = this.shaderFactory.createShader({
|
|
585
683
|
id: `${this.id}-fragment`,
|
|
586
684
|
stage: 'fragment',
|
|
587
|
-
source: this.fs
|
|
588
|
-
|
|
589
|
-
|
|
685
|
+
source: this.source || this.fs,
|
|
686
|
+
debug: this.props.debugShaders
|
|
687
|
+
});
|
|
688
|
+
}
|
|
590
689
|
|
|
591
690
|
this.pipeline = this.pipelineFactory.createRenderPipeline({
|
|
592
691
|
...this.props,
|
|
@@ -601,6 +700,9 @@ export class Model {
|
|
|
601
700
|
this.pipeline.shaderLayout,
|
|
602
701
|
this.bufferLayout
|
|
603
702
|
);
|
|
703
|
+
|
|
704
|
+
if (prevShaderVs) this.shaderFactory.release(prevShaderVs);
|
|
705
|
+
if (prevShaderFs) this.shaderFactory.release(prevShaderFs);
|
|
604
706
|
}
|
|
605
707
|
return this.pipeline;
|
|
606
708
|
}
|
|
@@ -651,7 +753,7 @@ export class Model {
|
|
|
651
753
|
const debugFramebuffers = log.get('framebuffer');
|
|
652
754
|
this._drawCount++;
|
|
653
755
|
// Update first 3 frames and then every 60 frames
|
|
654
|
-
if (!debugFramebuffers || (
|
|
756
|
+
if (!debugFramebuffers || (this._drawCount++ > 3 && this._drawCount % 60)) {
|
|
655
757
|
return;
|
|
656
758
|
}
|
|
657
759
|
// TODO - display framebuffer output in debug window
|
|
@@ -717,11 +819,12 @@ function mergeBufferLayouts(layouts1: BufferLayout[], layouts2: BufferLayout[]):
|
|
|
717
819
|
/** Create a shadertools platform info from the Device */
|
|
718
820
|
export function getPlatformInfo(device: Device): PlatformInfo {
|
|
719
821
|
return {
|
|
720
|
-
type: device.
|
|
822
|
+
type: device.type,
|
|
721
823
|
shaderLanguage: device.info.shadingLanguage,
|
|
722
824
|
shaderLanguageVersion: device.info.shadingLanguageVersion as 100 | 300,
|
|
723
825
|
gpu: device.info.gpu,
|
|
724
|
-
|
|
826
|
+
// HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
|
|
827
|
+
features: device.features as unknown as Set<DeviceFeature>
|
|
725
828
|
};
|
|
726
829
|
}
|
|
727
830
|
|