@luma.gl/engine 9.0.0-beta.1 → 9.0.0-beta.10
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/key-frames.js +54 -54
- package/dist/animation/timeline.d.ts.map +1 -1
- package/dist/animation/timeline.js +95 -100
- 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 +19 -5
- 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 +433 -356
- 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/animation-props.js +0 -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 +28 -24
- 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 +39 -42
- package/dist/debug/debug-framebuffer.d.ts.map +1 -1
- package/dist/debug/debug-framebuffer.js +43 -40
- package/dist/debug/debug-shader-layout.js +24 -25
- package/dist/debug/pixel-data-utils.d.ts.map +1 -1
- package/dist/debug/pixel-data-utils.js +34 -36
- package/dist/dist.dev.js +2538 -3027
- package/dist/dist.min.js +102 -0
- 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 +11 -17
- 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 +190 -61
- 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 +9 -14
- 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 +141 -160
- 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 +92 -110
- 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 +76 -95
- 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 +99 -117
- package/dist/geometry/geometry-table.d.ts.map +1 -1
- package/dist/geometry/geometry-table.js +3 -1
- package/dist/geometry/geometry-utils.js +35 -32
- package/dist/geometry/geometry.d.ts.map +1 -1
- package/dist/geometry/geometry.js +80 -71
- 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 +79 -99
- package/dist/geometry/gpu-table.js +41 -1
- package/dist/index.cjs +725 -409
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +43 -40
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/lib/clip-space.d.ts +2 -2
- package/dist/lib/clip-space.d.ts.map +1 -1
- package/dist/lib/clip-space.js +28 -34
- package/dist/lib/pipeline-factory.d.ts +13 -14
- package/dist/lib/pipeline-factory.d.ts.map +1 -1
- package/dist/lib/pipeline-factory.js +86 -85
- 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 +59 -46
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +635 -411
- 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 +73 -83
- 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 +31 -24
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
- package/dist/scenegraph/scenegraph-node.js +136 -124
- package/dist/shader-inputs.d.ts.map +1 -1
- package/dist/shader-inputs.js +99 -58
- 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 +65 -57
- 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 +109 -114
- package/dist.min.js +3 -271
- package/package.json +10 -9
- 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 +9 -11
- 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 +16 -0
- package/src/geometry/geometry.ts +9 -6
- package/src/geometry/gpu-geometry.ts +18 -11
- package/src/index.ts +4 -1
- package/src/lib/clip-space.ts +16 -19
- package/src/lib/pipeline-factory.ts +71 -64
- package/src/lib/shader-factory.ts +57 -0
- package/src/model/model.ts +255 -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 +16 -8
- package/src/transform/texture-transform.ts +14 -15
- package/dist/animation/key-frames.js.map +0 -1
- package/dist/animation/timeline.js.map +0 -1
- package/dist/animation-loop/animation-loop-template.js.map +0 -1
- package/dist/animation-loop/animation-loop.js.map +0 -1
- package/dist/animation-loop/animation-props.js.map +0 -1
- package/dist/animation-loop/make-animation-loop.js.map +0 -1
- package/dist/debug/copy-texture-to-image.js.map +0 -1
- package/dist/debug/debug-framebuffer.js.map +0 -1
- package/dist/debug/debug-shader-layout.js.map +0 -1
- package/dist/debug/pixel-data-utils.js.map +0 -1
- package/dist/geometries/cone-geometry.js.map +0 -1
- package/dist/geometries/cube-geometry.js.map +0 -1
- package/dist/geometries/cylinder-geometry.js.map +0 -1
- package/dist/geometries/ico-sphere-geometry.js.map +0 -1
- package/dist/geometries/plane-geometry.js.map +0 -1
- package/dist/geometries/sphere-geometry.js.map +0 -1
- package/dist/geometries/truncated-cone-geometry.js.map +0 -1
- package/dist/geometry/geometry-table.js.map +0 -1
- package/dist/geometry/geometry-utils.js.map +0 -1
- package/dist/geometry/geometry.js.map +0 -1
- package/dist/geometry/gpu-geometry.js.map +0 -1
- package/dist/geometry/gpu-table.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/clip-space.js.map +0 -1
- package/dist/lib/pipeline-factory.js.map +0 -1
- package/dist/model/model.js.map +0 -1
- package/dist/scenegraph/group-node.js.map +0 -1
- package/dist/scenegraph/model-node.js.map +0 -1
- package/dist/scenegraph/scenegraph-node.js.map +0 -1
- package/dist/shader-inputs.js.map +0 -1
- package/dist/transform/buffer-transform.js.map +0 -1
- package/dist/transform/texture-transform.js.map +0 -1
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,14 +55,26 @@ 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
|
-
/** @internal For use with {@link TransformFeedback}, WebGL
|
|
61
|
+
/** @internal For use with {@link TransformFeedback}, WebGL only. */
|
|
64
62
|
varyings?: string[];
|
|
65
63
|
|
|
66
64
|
transformFeedback?: TransformFeedback;
|
|
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,35 +194,40 @@ 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);
|
|
199
|
+
|
|
200
|
+
// Extract modules from shader inputs if not supplied
|
|
195
201
|
const modules =
|
|
196
202
|
(this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
|
|
197
|
-
const {vs, fs, getUniforms} = this.props.shaderAssembler.assembleShaders({
|
|
198
|
-
platformInfo,
|
|
199
|
-
...this.props,
|
|
200
|
-
modules
|
|
201
|
-
});
|
|
202
203
|
|
|
203
|
-
this.
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
const isWebGPU = this.device.type === 'webgpu';
|
|
205
|
+
|
|
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
|
+
}
|
|
206
231
|
|
|
207
232
|
this.vertexCount = this.props.vertexCount;
|
|
208
233
|
this.instanceCount = this.props.instanceCount;
|
|
@@ -213,11 +238,12 @@ export class Model {
|
|
|
213
238
|
|
|
214
239
|
// Geometry, if provided, sets topology and vertex cound
|
|
215
240
|
if (props.geometry) {
|
|
216
|
-
this.
|
|
241
|
+
this.setGeometry(props.geometry);
|
|
217
242
|
}
|
|
218
243
|
|
|
219
244
|
this.pipelineFactory =
|
|
220
245
|
props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
|
|
246
|
+
this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
|
|
221
247
|
|
|
222
248
|
// Create the pipeline
|
|
223
249
|
// @note order is important
|
|
@@ -247,7 +273,9 @@ export class Model {
|
|
|
247
273
|
this.setIndexBuffer(props.indexBuffer);
|
|
248
274
|
}
|
|
249
275
|
if (props.attributes) {
|
|
250
|
-
this.setAttributes(props.attributes
|
|
276
|
+
this.setAttributes(props.attributes, {
|
|
277
|
+
ignoreUnknownAttributes: props.ignoreUnknownAttributes
|
|
278
|
+
});
|
|
251
279
|
}
|
|
252
280
|
if (props.constantAttributes) {
|
|
253
281
|
this.setConstantAttributes(props.constantAttributes);
|
|
@@ -266,51 +294,93 @@ export class Model {
|
|
|
266
294
|
this.transformFeedback = props.transformFeedback;
|
|
267
295
|
}
|
|
268
296
|
|
|
269
|
-
// WebGL1?
|
|
270
|
-
// this.setUniforms(this._getModuleUniforms()); // Get all default module uniforms
|
|
271
|
-
|
|
272
297
|
// Catch any access to non-standard props
|
|
273
298
|
Object.seal(this);
|
|
274
299
|
}
|
|
275
300
|
|
|
276
301
|
destroy(): void {
|
|
302
|
+
if (this._destroyed) return;
|
|
277
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
|
+
}
|
|
278
308
|
this._uniformStore.destroy();
|
|
309
|
+
// TODO - mark resource as managed and destroyIfManaged() ?
|
|
310
|
+
this._gpuGeometry?.destroy();
|
|
311
|
+
this._destroyed = true;
|
|
279
312
|
}
|
|
280
313
|
|
|
281
314
|
// Draw call
|
|
282
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
|
+
|
|
283
332
|
predraw() {
|
|
284
333
|
// Update uniform buffers if needed
|
|
285
334
|
this.updateShaderInputs();
|
|
335
|
+
// Check if the pipeline is invalidated
|
|
336
|
+
this.pipeline = this._updatePipeline();
|
|
286
337
|
}
|
|
287
338
|
|
|
288
|
-
draw(renderPass: RenderPass):
|
|
339
|
+
draw(renderPass: RenderPass): boolean {
|
|
289
340
|
this.predraw();
|
|
290
341
|
|
|
342
|
+
let drawSuccess: boolean;
|
|
291
343
|
try {
|
|
292
344
|
this._logDrawCallStart();
|
|
293
345
|
|
|
294
|
-
//
|
|
295
|
-
// 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.
|
|
296
349
|
this.pipeline = this._updatePipeline();
|
|
297
350
|
|
|
298
351
|
// Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
|
|
299
352
|
// Any caching needs to be done inside the pipeline functions
|
|
300
353
|
this.pipeline.setBindings(this.bindings);
|
|
301
|
-
|
|
354
|
+
if (!isObjectEmpty(this.uniforms)) {
|
|
355
|
+
this.pipeline.setUniformsWebGL(this.uniforms);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const {indexBuffer} = this.vertexArray;
|
|
359
|
+
const indexCount = indexBuffer
|
|
360
|
+
? indexBuffer.byteLength / (indexBuffer.indexType === 'uint32' ? 4 : 2)
|
|
361
|
+
: undefined;
|
|
302
362
|
|
|
303
|
-
this.pipeline.draw({
|
|
363
|
+
drawSuccess = this.pipeline.draw({
|
|
304
364
|
renderPass,
|
|
305
365
|
vertexArray: this.vertexArray,
|
|
306
366
|
vertexCount: this.vertexCount,
|
|
307
367
|
instanceCount: this.instanceCount,
|
|
368
|
+
indexCount,
|
|
308
369
|
transformFeedback: this.transformFeedback
|
|
309
370
|
});
|
|
310
371
|
} finally {
|
|
311
372
|
this._logDrawCallEnd();
|
|
312
373
|
}
|
|
313
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;
|
|
314
384
|
}
|
|
315
385
|
|
|
316
386
|
// Update fixed fields (can trigger pipeline rebuild)
|
|
@@ -320,38 +390,17 @@ export class Model {
|
|
|
320
390
|
* Geometry, set topology and bufferLayout
|
|
321
391
|
* @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
|
|
322
392
|
*/
|
|
323
|
-
setGeometry(geometry: GPUGeometry | Geometry):
|
|
393
|
+
setGeometry(geometry: GPUGeometry | Geometry | null): void {
|
|
394
|
+
this._gpuGeometry?.destroy();
|
|
324
395
|
const gpuGeometry = geometry && makeGPUGeometry(this.device, geometry);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
this.
|
|
329
|
-
|
|
330
|
-
return gpuGeometry;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Updates the optional geometry attributes
|
|
335
|
-
* Geometry, sets several attributes, indexBuffer, and also vertex count
|
|
336
|
-
* @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
|
|
337
|
-
*/
|
|
338
|
-
_setGeometryAttributes(gpuGeometry: GPUGeometry): void {
|
|
339
|
-
// Filter geometry attribute so that we don't issue warnings for unused attributes
|
|
340
|
-
const attributes = {...gpuGeometry.attributes};
|
|
341
|
-
for (const [attributeName] of Object.entries(attributes)) {
|
|
342
|
-
if (
|
|
343
|
-
!this.pipeline.shaderLayout.attributes.find(layout => layout.name === attributeName) &&
|
|
344
|
-
attributeName !== 'positions'
|
|
345
|
-
) {
|
|
346
|
-
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);
|
|
347
401
|
}
|
|
348
402
|
}
|
|
349
|
-
|
|
350
|
-
// TODO - delete previous geometry?
|
|
351
|
-
this.vertexCount = gpuGeometry.vertexCount;
|
|
352
|
-
this.setIndexBuffer(gpuGeometry.indices);
|
|
353
|
-
this.setAttributes(gpuGeometry.attributes, 'ignore-unknown');
|
|
354
|
-
this.setAttributes(attributes);
|
|
403
|
+
this._gpuGeometry = gpuGeometry;
|
|
355
404
|
}
|
|
356
405
|
|
|
357
406
|
/**
|
|
@@ -367,7 +416,7 @@ export class Model {
|
|
|
367
416
|
|
|
368
417
|
/**
|
|
369
418
|
* Updates the buffer layout.
|
|
370
|
-
* @note Triggers a pipeline rebuild / pipeline cache fetch
|
|
419
|
+
* @note Triggers a pipeline rebuild / pipeline cache fetch
|
|
371
420
|
*/
|
|
372
421
|
setBufferLayout(bufferLayout: BufferLayout[]): void {
|
|
373
422
|
this.bufferLayout = this._gpuGeometry
|
|
@@ -410,6 +459,7 @@ export class Model {
|
|
|
410
459
|
*/
|
|
411
460
|
setVertexCount(vertexCount: number): void {
|
|
412
461
|
this.vertexCount = vertexCount;
|
|
462
|
+
this.setNeedsRedraw('vertexCount');
|
|
413
463
|
}
|
|
414
464
|
|
|
415
465
|
/**
|
|
@@ -418,6 +468,7 @@ export class Model {
|
|
|
418
468
|
*/
|
|
419
469
|
setInstanceCount(instanceCount: number): void {
|
|
420
470
|
this.instanceCount = instanceCount;
|
|
471
|
+
this.setNeedsRedraw('instanceCount');
|
|
421
472
|
}
|
|
422
473
|
|
|
423
474
|
setShaderInputs(shaderInputs: ShaderInputs): void {
|
|
@@ -428,39 +479,13 @@ export class Model {
|
|
|
428
479
|
const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
|
|
429
480
|
this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
|
|
430
481
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Updates shader module settings (which results in uniforms being set)
|
|
435
|
-
*/
|
|
436
|
-
setShaderModuleProps(props: Record<string, any>): void {
|
|
437
|
-
const uniforms = this._getModuleUniforms(props);
|
|
438
|
-
|
|
439
|
-
// Extract textures & framebuffers set by the modules
|
|
440
|
-
// TODO better way to extract bindings
|
|
441
|
-
const keys = Object.keys(uniforms).filter(k => {
|
|
442
|
-
const uniform = uniforms[k];
|
|
443
|
-
return !isNumberArray(uniform) && typeof uniform !== 'number' && typeof uniform !== 'boolean';
|
|
444
|
-
});
|
|
445
|
-
const bindings: Record<string, Binding> = {};
|
|
446
|
-
for (const k of keys) {
|
|
447
|
-
bindings[k] = uniforms[k];
|
|
448
|
-
delete uniforms[k];
|
|
449
|
-
}
|
|
482
|
+
this.setNeedsRedraw('shaderInputs');
|
|
450
483
|
}
|
|
451
484
|
|
|
452
485
|
updateShaderInputs(): void {
|
|
453
486
|
this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* @deprecated Updates shader module settings (which results in uniforms being set)
|
|
458
|
-
*/
|
|
459
|
-
updateModuleSettings(props: Record<string, any>): void {
|
|
460
|
-
log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')();
|
|
461
|
-
const {bindings, uniforms} = splitUniformsAndBindings(this._getModuleUniforms(props));
|
|
462
|
-
Object.assign(this.bindings, bindings);
|
|
463
|
-
Object.assign(this.uniforms, uniforms);
|
|
487
|
+
// TODO - this is already tracked through buffer/texture update times?
|
|
488
|
+
this.setNeedsRedraw('shaderInputs');
|
|
464
489
|
}
|
|
465
490
|
|
|
466
491
|
/**
|
|
@@ -468,17 +493,15 @@ export class Model {
|
|
|
468
493
|
*/
|
|
469
494
|
setBindings(bindings: Record<string, Binding>): void {
|
|
470
495
|
Object.assign(this.bindings, bindings);
|
|
496
|
+
this.setNeedsRedraw('bindings');
|
|
471
497
|
}
|
|
472
498
|
|
|
473
499
|
/**
|
|
474
|
-
*
|
|
475
|
-
* @deprecated WebGL only, use uniform buffers for portability
|
|
476
|
-
* @param uniforms
|
|
477
|
-
* @returns self for chaining
|
|
500
|
+
* Updates optional transform feedback. WebGL only.
|
|
478
501
|
*/
|
|
479
|
-
|
|
480
|
-
this.
|
|
481
|
-
|
|
502
|
+
setTransformFeedback(transformFeedback: TransformFeedback | null): void {
|
|
503
|
+
this.transformFeedback = transformFeedback;
|
|
504
|
+
this.setNeedsRedraw('transformFeedback');
|
|
482
505
|
}
|
|
483
506
|
|
|
484
507
|
/**
|
|
@@ -487,27 +510,26 @@ export class Model {
|
|
|
487
510
|
*/
|
|
488
511
|
setIndexBuffer(indexBuffer: Buffer | null): void {
|
|
489
512
|
this.vertexArray.setIndexBuffer(indexBuffer);
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Updates optional transform feedback. WebGL 2 only.
|
|
494
|
-
*/
|
|
495
|
-
setTransformFeedback(transformFeedback: TransformFeedback | null): void {
|
|
496
|
-
this.transformFeedback = transformFeedback;
|
|
513
|
+
this.setNeedsRedraw('indexBuffer');
|
|
497
514
|
}
|
|
498
515
|
|
|
499
516
|
/**
|
|
500
517
|
* Sets attributes (buffers)
|
|
501
518
|
* @note Overrides any attributes previously set with the same name
|
|
502
519
|
*/
|
|
503
|
-
setAttributes(
|
|
520
|
+
setAttributes(
|
|
521
|
+
buffers: Record<string, Buffer>,
|
|
522
|
+
options?: {ignoreUnknownAttributes?: boolean}
|
|
523
|
+
): void {
|
|
504
524
|
if (buffers.indices) {
|
|
505
525
|
log.warn(
|
|
506
526
|
`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
|
|
507
527
|
)();
|
|
508
528
|
}
|
|
509
529
|
for (const [bufferName, buffer] of Object.entries(buffers)) {
|
|
510
|
-
const bufferLayout = this.bufferLayout.find(layout =>
|
|
530
|
+
const bufferLayout = this.bufferLayout.find(layout =>
|
|
531
|
+
getAttributeNames(layout).includes(bufferName)
|
|
532
|
+
);
|
|
511
533
|
if (!bufferLayout) {
|
|
512
534
|
log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
|
|
513
535
|
continue; // eslint-disable-line no-continue
|
|
@@ -523,12 +545,13 @@ export class Model {
|
|
|
523
545
|
set = true;
|
|
524
546
|
}
|
|
525
547
|
}
|
|
526
|
-
if (!set &&
|
|
548
|
+
if (!set && (options?.ignoreUnknownAttributes || this.props.ignoreUnknownAttributes)) {
|
|
527
549
|
log.warn(
|
|
528
550
|
`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
|
|
529
551
|
)();
|
|
530
552
|
}
|
|
531
553
|
}
|
|
554
|
+
this.setNeedsRedraw('attributes');
|
|
532
555
|
}
|
|
533
556
|
|
|
534
557
|
/**
|
|
@@ -543,45 +566,127 @@ export class Model {
|
|
|
543
566
|
for (const [attributeName, value] of Object.entries(attributes)) {
|
|
544
567
|
const attributeInfo = this._attributeInfos[attributeName];
|
|
545
568
|
if (attributeInfo) {
|
|
546
|
-
this.vertexArray.
|
|
569
|
+
this.vertexArray.setConstantWebGL(attributeInfo.location, value);
|
|
547
570
|
} else {
|
|
548
571
|
log.warn(
|
|
549
572
|
`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
|
|
550
573
|
)();
|
|
551
574
|
}
|
|
552
575
|
}
|
|
576
|
+
this.setNeedsRedraw('constants');
|
|
553
577
|
}
|
|
554
578
|
|
|
579
|
+
// DEPRECATED METHODS
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Sets individual uniforms
|
|
583
|
+
* @deprecated WebGL only, use uniform buffers for portability
|
|
584
|
+
* @param uniforms
|
|
585
|
+
*/
|
|
586
|
+
setUniforms(uniforms: Record<string, UniformValue>): void {
|
|
587
|
+
if (!isObjectEmpty(uniforms)) {
|
|
588
|
+
this.pipeline.setUniformsWebGL(uniforms);
|
|
589
|
+
Object.assign(this.uniforms, uniforms);
|
|
590
|
+
}
|
|
591
|
+
this.setNeedsRedraw('uniforms');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* @deprecated Updates shader module settings (which results in uniforms being set)
|
|
596
|
+
*/
|
|
597
|
+
updateModuleSettings(props: Record<string, any>): void {
|
|
598
|
+
log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')();
|
|
599
|
+
const {bindings, uniforms} = splitUniformsAndBindings(this._getModuleUniforms(props));
|
|
600
|
+
Object.assign(this.bindings, bindings);
|
|
601
|
+
Object.assign(this.uniforms, uniforms);
|
|
602
|
+
this.setNeedsRedraw('moduleSettings');
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Internal methods
|
|
606
|
+
|
|
607
|
+
/** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
|
|
608
|
+
_getBindingsUpdateTimestamp(): number {
|
|
609
|
+
let timestamp = 0;
|
|
610
|
+
for (const binding of Object.values(this.bindings)) {
|
|
611
|
+
if (binding instanceof TextureView) {
|
|
612
|
+
timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
|
|
613
|
+
} else if (binding instanceof Buffer || binding instanceof Texture) {
|
|
614
|
+
timestamp = Math.max(timestamp, binding.updateTimestamp);
|
|
615
|
+
} else if (!(binding instanceof Sampler)) {
|
|
616
|
+
timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return timestamp;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Updates the optional geometry attributes
|
|
624
|
+
* Geometry, sets several attributes, indexBuffer, and also vertex count
|
|
625
|
+
* @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
|
|
626
|
+
*/
|
|
627
|
+
_setGeometryAttributes(gpuGeometry: GPUGeometry): void {
|
|
628
|
+
// Filter geometry attribute so that we don't issue warnings for unused attributes
|
|
629
|
+
const attributes = {...gpuGeometry.attributes};
|
|
630
|
+
for (const [attributeName] of Object.entries(attributes)) {
|
|
631
|
+
if (
|
|
632
|
+
!this.pipeline.shaderLayout.attributes.find(layout => layout.name === attributeName) &&
|
|
633
|
+
attributeName !== 'positions'
|
|
634
|
+
) {
|
|
635
|
+
delete attributes[attributeName];
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// TODO - delete previous geometry?
|
|
640
|
+
this.vertexCount = gpuGeometry.vertexCount;
|
|
641
|
+
this.setIndexBuffer(gpuGeometry.indices);
|
|
642
|
+
this.setAttributes(gpuGeometry.attributes, {ignoreUnknownAttributes: true});
|
|
643
|
+
this.setAttributes(attributes, {ignoreUnknownAttributes: this.props.ignoreUnknownAttributes});
|
|
644
|
+
|
|
645
|
+
this.setNeedsRedraw('geometry attributes');
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/** Mark pipeline as needing update */
|
|
555
649
|
_setPipelineNeedsUpdate(reason: string): void {
|
|
556
|
-
this._pipelineNeedsUpdate
|
|
650
|
+
this._pipelineNeedsUpdate ||= reason;
|
|
651
|
+
this.setNeedsRedraw(reason);
|
|
557
652
|
}
|
|
558
653
|
|
|
654
|
+
/** Update pipeline if needed */
|
|
559
655
|
_updatePipeline(): RenderPipeline {
|
|
560
656
|
if (this._pipelineNeedsUpdate) {
|
|
657
|
+
let prevShaderVs: Shader | null = null;
|
|
658
|
+
let prevShaderFs: Shader | null = null;
|
|
561
659
|
if (this.pipeline) {
|
|
562
660
|
log.log(
|
|
563
661
|
1,
|
|
564
662
|
`Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
|
|
565
663
|
)();
|
|
664
|
+
prevShaderVs = this.pipeline.vs;
|
|
665
|
+
prevShaderFs = this.pipeline.fs;
|
|
566
666
|
}
|
|
567
|
-
|
|
667
|
+
|
|
568
668
|
this._pipelineNeedsUpdate = false;
|
|
569
669
|
|
|
570
|
-
const vs = this.
|
|
670
|
+
const vs = this.shaderFactory.createShader({
|
|
571
671
|
id: `${this.id}-vertex`,
|
|
572
672
|
stage: 'vertex',
|
|
573
|
-
source: this.vs
|
|
673
|
+
source: this.source || this.vs,
|
|
674
|
+
debug: this.props.debugShaders
|
|
574
675
|
});
|
|
575
676
|
|
|
576
|
-
|
|
577
|
-
|
|
677
|
+
let fs: Shader | null = null;
|
|
678
|
+
if (this.source) {
|
|
679
|
+
fs = vs;
|
|
680
|
+
} else if (this.fs) {
|
|
681
|
+
fs = this.shaderFactory.createShader({
|
|
578
682
|
id: `${this.id}-fragment`,
|
|
579
683
|
stage: 'fragment',
|
|
580
|
-
source: this.fs
|
|
581
|
-
|
|
582
|
-
|
|
684
|
+
source: this.source || this.fs,
|
|
685
|
+
debug: this.props.debugShaders
|
|
686
|
+
});
|
|
687
|
+
}
|
|
583
688
|
|
|
584
|
-
this.pipeline = this.
|
|
689
|
+
this.pipeline = this.pipelineFactory.createRenderPipeline({
|
|
585
690
|
...this.props,
|
|
586
691
|
bufferLayout: this.bufferLayout,
|
|
587
692
|
topology: this.topology,
|
|
@@ -594,6 +699,9 @@ export class Model {
|
|
|
594
699
|
this.pipeline.shaderLayout,
|
|
595
700
|
this.bufferLayout
|
|
596
701
|
);
|
|
702
|
+
|
|
703
|
+
if (prevShaderVs) this.shaderFactory.release(prevShaderVs);
|
|
704
|
+
if (prevShaderFs) this.shaderFactory.release(prevShaderFs);
|
|
597
705
|
}
|
|
598
706
|
return this.pipeline;
|
|
599
707
|
}
|
|
@@ -644,7 +752,7 @@ export class Model {
|
|
|
644
752
|
const debugFramebuffers = log.get('framebuffer');
|
|
645
753
|
this._drawCount++;
|
|
646
754
|
// Update first 3 frames and then every 60 frames
|
|
647
|
-
if (!debugFramebuffers || (
|
|
755
|
+
if (!debugFramebuffers || (this._drawCount++ > 3 && this._drawCount % 60)) {
|
|
648
756
|
return;
|
|
649
757
|
}
|
|
650
758
|
// TODO - display framebuffer output in debug window
|
|
@@ -710,11 +818,12 @@ function mergeBufferLayouts(layouts1: BufferLayout[], layouts2: BufferLayout[]):
|
|
|
710
818
|
/** Create a shadertools platform info from the Device */
|
|
711
819
|
export function getPlatformInfo(device: Device): PlatformInfo {
|
|
712
820
|
return {
|
|
713
|
-
type: device.
|
|
821
|
+
type: device.type,
|
|
714
822
|
shaderLanguage: device.info.shadingLanguage,
|
|
715
823
|
shaderLanguageVersion: device.info.shadingLanguageVersion as 100 | 300,
|
|
716
824
|
gpu: device.info.gpu,
|
|
717
|
-
|
|
825
|
+
// HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
|
|
826
|
+
features: device.features as unknown as Set<DeviceFeature>
|
|
718
827
|
};
|
|
719
828
|
}
|
|
720
829
|
|