@luma.gl/engine 9.0.0-beta.6 → 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.
@@ -0,0 +1,95 @@
1
+ import type { TypedArray } from '@luma.gl/core';
2
+ import type { ComputePipelineProps, Shader, Binding } from '@luma.gl/core';
3
+ import { Device, Buffer, ComputePipeline, ComputePass, UniformStore } from '@luma.gl/core';
4
+ import type { ShaderModule, PlatformInfo } from '@luma.gl/shadertools';
5
+ import { ShaderAssembler } from '@luma.gl/shadertools';
6
+ import { ShaderInputs } from "./shader-inputs.js";
7
+ import { PipelineFactory } from "./lib/pipeline-factory.js";
8
+ import { ShaderFactory } from "./lib/shader-factory.js";
9
+ export type ComputationProps = Omit<ComputePipelineProps, 'shader'> & {
10
+ source?: string;
11
+ /** shadertool shader modules (added to shader code) */
12
+ modules?: ShaderModule[];
13
+ /** Shadertool module defines (configures shader code)*/
14
+ defines?: Record<string, string | number | boolean>;
15
+ /** Shader inputs, used to generated uniform buffers and bindings */
16
+ shaderInputs?: ShaderInputs;
17
+ /** Bindings */
18
+ bindings?: Record<string, Binding>;
19
+ /** Show shader source in browser? */
20
+ debugShaders?: 'never' | 'errors' | 'warnings' | 'always';
21
+ /** Factory used to create a {@link ComputePipeline}. Defaults to {@link Device} default factory. */
22
+ pipelineFactory?: PipelineFactory;
23
+ /** Factory used to create a {@link Shader}. Defaults to {@link Device} default factory. */
24
+ shaderFactory?: ShaderFactory;
25
+ /** Shader assembler. Defaults to the ShaderAssembler.getShaderAssembler() */
26
+ shaderAssembler?: ShaderAssembler;
27
+ };
28
+ /**
29
+ * v9 Model API
30
+ * A model
31
+ * - automatically reuses pipelines (programs) when possible
32
+ * - automatically rebuilds pipelines if necessary to accommodate changed settings
33
+ * shadertools integration
34
+ * - accepts modules and performs shader transpilation
35
+ */
36
+ export declare class Computation {
37
+ static defaultProps: Required<ComputationProps>;
38
+ readonly device: Device;
39
+ readonly id: string;
40
+ readonly pipelineFactory: PipelineFactory;
41
+ readonly shaderFactory: ShaderFactory;
42
+ userData: {
43
+ [key: string]: any;
44
+ };
45
+ /** Bindings (textures, samplers, uniform buffers) */
46
+ bindings: Record<string, Binding>;
47
+ /** The underlying GPU "program". @note May be recreated if parameters change */
48
+ pipeline: ComputePipeline;
49
+ /** the underlying compiled compute shader */
50
+ shader: Shader;
51
+ source: string;
52
+ /** ShaderInputs instance */
53
+ shaderInputs: ShaderInputs;
54
+ _uniformStore: UniformStore;
55
+ _pipelineNeedsUpdate: string | false;
56
+ private _getModuleUniforms;
57
+ private props;
58
+ private _destroyed;
59
+ constructor(device: Device, props: ComputationProps);
60
+ destroy(): void;
61
+ predraw(): void;
62
+ dispatch(computePass: ComputePass, x: number, y?: number, z?: number): void;
63
+ /**
64
+ * Updates the vertex count (used in draw calls)
65
+ * @note Any attributes with stepMode=vertex need to be at least this big
66
+ */
67
+ setVertexCount(vertexCount: number): void;
68
+ /**
69
+ * Updates the instance count (used in draw calls)
70
+ * @note Any attributes with stepMode=instance need to be at least this big
71
+ */
72
+ setInstanceCount(instanceCount: number): void;
73
+ setShaderInputs(shaderInputs: ShaderInputs): void;
74
+ /**
75
+ * Updates shader module settings (which results in uniforms being set)
76
+ */
77
+ setShaderModuleProps(props: Record<string, any>): void;
78
+ updateShaderInputs(): void;
79
+ /**
80
+ * Sets bindings (textures, samplers, uniform buffers)
81
+ */
82
+ setBindings(bindings: Record<string, Binding>): void;
83
+ _setPipelineNeedsUpdate(reason: string): void;
84
+ _updatePipeline(): ComputePipeline;
85
+ /** Throttle draw call logging */
86
+ _lastLogTime: number;
87
+ _logOpen: boolean;
88
+ _logDrawCallStart(): void;
89
+ _logDrawCallEnd(): void;
90
+ protected _drawCount: number;
91
+ _getBufferOrConstantValues(attribute: Buffer | TypedArray, dataType: any): string;
92
+ }
93
+ /** Create a shadertools platform info from the Device */
94
+ export declare function getPlatformInfo(device: Device): PlatformInfo;
95
+ //# sourceMappingURL=computation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computation.d.ts","sourceRoot":"","sources":["../src/computation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,248 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ import { Buffer, ComputePipeline, UniformStore } from '@luma.gl/core';
5
+ import { log, uid, isNumberArray } from '@luma.gl/core';
6
+ import { getTypedArrayFromDataType } from '@luma.gl/core';
7
+ import { ShaderAssembler, getShaderLayoutFromWGSL } from '@luma.gl/shadertools';
8
+ import { ShaderInputs } from "./shader-inputs.js";
9
+ import { PipelineFactory } from "./lib/pipeline-factory.js";
10
+ import { ShaderFactory } from "./lib/shader-factory.js";
11
+ // import {getDebugTableForShaderLayout} from '../debug/debug-shader-layout';
12
+ const LOG_DRAW_PRIORITY = 2;
13
+ const LOG_DRAW_TIMEOUT = 10000;
14
+ /**
15
+ * v9 Model API
16
+ * A model
17
+ * - automatically reuses pipelines (programs) when possible
18
+ * - automatically rebuilds pipelines if necessary to accommodate changed settings
19
+ * shadertools integration
20
+ * - accepts modules and performs shader transpilation
21
+ */
22
+ export class Computation {
23
+ static defaultProps = {
24
+ ...ComputePipeline.defaultProps,
25
+ id: 'unnamed',
26
+ handle: undefined,
27
+ userData: {},
28
+ source: '',
29
+ modules: [],
30
+ defines: {},
31
+ bindings: undefined,
32
+ shaderInputs: undefined,
33
+ pipelineFactory: undefined,
34
+ shaderFactory: undefined,
35
+ shaderAssembler: ShaderAssembler.getDefaultShaderAssembler(),
36
+ debugShaders: undefined
37
+ };
38
+ device;
39
+ id;
40
+ pipelineFactory;
41
+ shaderFactory;
42
+ userData = {};
43
+ /** Bindings (textures, samplers, uniform buffers) */
44
+ bindings = {};
45
+ /** The underlying GPU "program". @note May be recreated if parameters change */
46
+ pipeline;
47
+ /** the underlying compiled compute shader */
48
+ shader;
49
+ source;
50
+ /** ShaderInputs instance */
51
+ shaderInputs;
52
+ _uniformStore;
53
+ _pipelineNeedsUpdate = 'newly created';
54
+ _getModuleUniforms;
55
+ props;
56
+ _destroyed = false;
57
+ constructor(device, props) {
58
+ if (device.type !== 'webgpu') {
59
+ throw new Error('Computation is only supported in WebGPU');
60
+ }
61
+ this.props = { ...Computation.defaultProps, ...props };
62
+ props = this.props;
63
+ this.id = props.id || uid('model');
64
+ this.device = device;
65
+ Object.assign(this.userData, props.userData);
66
+ // Setup shader module inputs
67
+ const moduleMap = Object.fromEntries(this.props.modules?.map(module => [module.name, module]) || []);
68
+ this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
69
+ // Support WGSL shader layout introspection
70
+ // TODO - Don't modify props!!
71
+ this.props.shaderLayout ||= getShaderLayoutFromWGSL(this.props.source);
72
+ // Setup shader assembler
73
+ const platformInfo = getPlatformInfo(device);
74
+ // Extract modules from shader inputs if not supplied
75
+ const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
76
+ this.pipelineFactory =
77
+ props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
78
+ this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
79
+ const { source, getUniforms } = this.props.shaderAssembler.assembleShader({
80
+ platformInfo,
81
+ ...this.props,
82
+ modules
83
+ });
84
+ this.source = source;
85
+ this._getModuleUniforms = getUniforms;
86
+ // Create the pipeline
87
+ // @note order is important
88
+ this.pipeline = this._updatePipeline();
89
+ // Apply any dynamic settings that will not trigger pipeline change
90
+ if (props.bindings) {
91
+ this.setBindings(props.bindings);
92
+ }
93
+ // Catch any access to non-standard props
94
+ Object.seal(this);
95
+ }
96
+ destroy() {
97
+ if (this._destroyed)
98
+ return;
99
+ this.pipelineFactory.release(this.pipeline);
100
+ this.shaderFactory.release(this.shader);
101
+ this._uniformStore.destroy();
102
+ this._destroyed = true;
103
+ }
104
+ // Draw call
105
+ predraw() {
106
+ // Update uniform buffers if needed
107
+ this.updateShaderInputs();
108
+ }
109
+ dispatch(computePass, x, y, z) {
110
+ try {
111
+ this._logDrawCallStart();
112
+ // Check if the pipeline is invalidated
113
+ // TODO - this is likely the worst place to do this from performance perspective. Perhaps add a predraw()?
114
+ this.pipeline = this._updatePipeline();
115
+ // Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
116
+ // Any caching needs to be done inside the pipeline functions
117
+ this.pipeline.setBindings(this.bindings);
118
+ computePass.setPipeline(this.pipeline);
119
+ // @ts-expect-error
120
+ computePass.setBindings([]);
121
+ computePass.dispatch(x, y, z);
122
+ }
123
+ finally {
124
+ this._logDrawCallEnd();
125
+ }
126
+ }
127
+ // Update fixed fields (can trigger pipeline rebuild)
128
+ // Update dynamic fields
129
+ /**
130
+ * Updates the vertex count (used in draw calls)
131
+ * @note Any attributes with stepMode=vertex need to be at least this big
132
+ */
133
+ setVertexCount(vertexCount) {
134
+ // this.vertexCount = vertexCount;
135
+ }
136
+ /**
137
+ * Updates the instance count (used in draw calls)
138
+ * @note Any attributes with stepMode=instance need to be at least this big
139
+ */
140
+ setInstanceCount(instanceCount) {
141
+ // this.instanceCount = instanceCount;
142
+ }
143
+ setShaderInputs(shaderInputs) {
144
+ this.shaderInputs = shaderInputs;
145
+ this._uniformStore = new UniformStore(this.shaderInputs.modules);
146
+ // Create uniform buffer bindings for all modules
147
+ for (const moduleName of Object.keys(this.shaderInputs.modules)) {
148
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
149
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
150
+ }
151
+ }
152
+ /**
153
+ * Updates shader module settings (which results in uniforms being set)
154
+ */
155
+ setShaderModuleProps(props) {
156
+ const uniforms = this._getModuleUniforms(props);
157
+ // Extract textures & framebuffers set by the modules
158
+ // TODO better way to extract bindings
159
+ const keys = Object.keys(uniforms).filter(k => {
160
+ const uniform = uniforms[k];
161
+ return !isNumberArray(uniform) && typeof uniform !== 'number' && typeof uniform !== 'boolean';
162
+ });
163
+ const bindings = {};
164
+ for (const k of keys) {
165
+ bindings[k] = uniforms[k];
166
+ delete uniforms[k];
167
+ }
168
+ }
169
+ updateShaderInputs() {
170
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
171
+ }
172
+ /**
173
+ * Sets bindings (textures, samplers, uniform buffers)
174
+ */
175
+ setBindings(bindings) {
176
+ Object.assign(this.bindings, bindings);
177
+ }
178
+ _setPipelineNeedsUpdate(reason) {
179
+ this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
180
+ }
181
+ _updatePipeline() {
182
+ if (this._pipelineNeedsUpdate) {
183
+ let prevShader = null;
184
+ if (this.pipeline) {
185
+ log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
186
+ prevShader = this.shader;
187
+ }
188
+ this._pipelineNeedsUpdate = false;
189
+ this.shader = this.shaderFactory.createShader({
190
+ id: `${this.id}-fragment`,
191
+ stage: 'compute',
192
+ source: this.source,
193
+ debug: this.props.debugShaders
194
+ });
195
+ this.pipeline = this.pipelineFactory.createComputePipeline({
196
+ ...this.props,
197
+ shader: this.shader
198
+ });
199
+ if (prevShader) {
200
+ this.shaderFactory.release(prevShader);
201
+ }
202
+ }
203
+ return this.pipeline;
204
+ }
205
+ /** Throttle draw call logging */
206
+ _lastLogTime = 0;
207
+ _logOpen = false;
208
+ _logDrawCallStart() {
209
+ // IF level is 4 or higher, log every frame.
210
+ const logDrawTimeout = log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
211
+ if (log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
212
+ return;
213
+ }
214
+ this._lastLogTime = Date.now();
215
+ this._logOpen = true;
216
+ log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: log.level <= 2 })();
217
+ }
218
+ _logDrawCallEnd() {
219
+ if (this._logOpen) {
220
+ // const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.props.shaderLayout, this.id);
221
+ // log.table(logLevel, attributeTable)();
222
+ // log.table(logLevel, uniformTable)();
223
+ // log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
224
+ const uniformTable = this.shaderInputs.getDebugTable();
225
+ log.table(LOG_DRAW_PRIORITY, uniformTable)();
226
+ log.groupEnd(LOG_DRAW_PRIORITY)();
227
+ this._logOpen = false;
228
+ }
229
+ }
230
+ _drawCount = 0;
231
+ // TODO - fix typing of luma data types
232
+ _getBufferOrConstantValues(attribute, dataType) {
233
+ const TypedArrayConstructor = getTypedArrayFromDataType(dataType);
234
+ const typedArray = attribute instanceof Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
235
+ return typedArray.toString();
236
+ }
237
+ }
238
+ /** Create a shadertools platform info from the Device */
239
+ export function getPlatformInfo(device) {
240
+ return {
241
+ type: device.type,
242
+ shaderLanguage: device.info.shadingLanguage,
243
+ shaderLanguageVersion: device.info.shadingLanguageVersion,
244
+ gpu: device.info.gpu,
245
+ // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
246
+ features: device.features
247
+ };
248
+ }
@@ -23,7 +23,7 @@ export function copyTextureToImage(source, options) {
23
23
  * @param options
24
24
  */
25
25
  export function copyTextureToDataUrl(source, options = {}) {
26
- const { sourceAttachment = GL.COLOR_ATTACHMENT0, // TODO - support gl.readBuffer
26
+ const { sourceAttachment = 36064, // TODO - support gl.readBuffer
27
27
  targetMaxHeight = Number.MAX_SAFE_INTEGER } = options;
28
28
  let data = source.device.readPixelsToArrayWebGL(source, { sourceAttachment });
29
29
  // Scale down