@luma.gl/engine 9.3.0-alpha.4 → 9.3.0-alpha.6
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 +8 -4
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +70 -43
- package/dist/animation-loop/animation-loop.js.map +1 -1
- package/dist/animation-loop/make-animation-loop.js +7 -1
- package/dist/animation-loop/make-animation-loop.js.map +1 -1
- package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
- package/dist/animation-loop/request-animation-frame.js +23 -6
- package/dist/animation-loop/request-animation-frame.js.map +1 -1
- package/dist/dist.dev.js +679 -927
- package/dist/dist.min.js +39 -240
- package/dist/dynamic-texture/dynamic-texture.d.ts +3 -3
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -1
- package/dist/dynamic-texture/dynamic-texture.js +170 -52
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -1
- package/dist/dynamic-texture/texture-data.d.ts +4 -0
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -1
- package/dist/dynamic-texture/texture-data.js +8 -0
- package/dist/dynamic-texture/texture-data.js.map +1 -1
- package/dist/factories/pipeline-factory.d.ts +7 -5
- package/dist/factories/pipeline-factory.d.ts.map +1 -1
- package/dist/factories/pipeline-factory.js +71 -36
- package/dist/factories/pipeline-factory.js.map +1 -1
- package/dist/factories/shader-factory.d.ts +0 -3
- package/dist/factories/shader-factory.d.ts.map +1 -1
- package/dist/factories/shader-factory.js +13 -19
- package/dist/factories/shader-factory.js.map +1 -1
- package/dist/geometries/cone-geometry.d.ts +3 -1
- package/dist/geometries/cone-geometry.d.ts.map +1 -1
- package/dist/geometries/cone-geometry.js.map +1 -1
- package/dist/geometries/cylinder-geometry.d.ts +2 -1
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
- package/dist/geometries/cylinder-geometry.js.map +1 -1
- package/dist/index.cjs +683 -922
- package/dist/index.cjs.map +4 -4
- package/dist/model/model.d.ts +3 -1
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +11 -9
- package/dist/model/model.js.map +1 -1
- package/dist/models/billboard-texture-model.d.ts.map +1 -1
- package/dist/models/billboard-texture-model.js +10 -8
- package/dist/models/billboard-texture-model.js.map +1 -1
- package/dist/models/clip-space.js +7 -7
- package/dist/modules/picking/index-picking.d.ts +1 -1
- package/dist/modules/picking/index-picking.d.ts.map +1 -1
- package/dist/modules/picking/index-picking.js +0 -6
- package/dist/modules/picking/index-picking.js.map +1 -1
- package/dist/passes/get-fragment-shader.js +11 -30
- package/dist/passes/get-fragment-shader.js.map +1 -1
- package/dist/passes/shader-pass-renderer.d.ts +0 -2
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
- package/dist/passes/shader-pass-renderer.js +4 -31
- package/dist/passes/shader-pass-renderer.js.map +1 -1
- package/dist/scenegraph/group-node.d.ts +5 -0
- package/dist/scenegraph/group-node.d.ts.map +1 -1
- package/dist/scenegraph/group-node.js +12 -0
- package/dist/scenegraph/group-node.js.map +1 -1
- package/dist/scenegraph/model-node.d.ts +2 -2
- package/dist/scenegraph/model-node.d.ts.map +1 -1
- package/dist/scenegraph/model-node.js.map +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
- package/dist/scenegraph/scenegraph-node.js +23 -15
- package/dist/scenegraph/scenegraph-node.js.map +1 -1
- package/package.json +2 -2
- package/src/animation-loop/animation-loop.ts +75 -46
- package/src/animation-loop/make-animation-loop.ts +13 -5
- package/src/animation-loop/request-animation-frame.ts +32 -6
- package/src/dynamic-texture/dynamic-texture.ts +226 -65
- package/src/dynamic-texture/texture-data.ts +14 -0
- package/src/factories/pipeline-factory.ts +87 -46
- package/src/factories/shader-factory.ts +16 -20
- package/src/geometries/cone-geometry.ts +6 -1
- package/src/geometries/cylinder-geometry.ts +5 -1
- package/src/model/model.ts +14 -10
- package/src/models/billboard-texture-model.ts +10 -8
- package/src/models/clip-space.ts +7 -7
- package/src/modules/picking/index-picking.ts +0 -6
- package/src/passes/get-fragment-shader.ts +11 -30
- package/src/passes/shader-pass-renderer.ts +4 -33
- package/src/scenegraph/group-node.ts +16 -0
- package/src/scenegraph/model-node.ts +2 -2
- package/src/scenegraph/scenegraph-node.ts +27 -16
- package/dist/dynamic-texture/mipmaps.d.ts +0 -6
- package/dist/dynamic-texture/mipmaps.d.ts.map +0 -1
- package/dist/dynamic-texture/mipmaps.js +0 -441
- package/dist/dynamic-texture/mipmaps.js.map +0 -1
- package/src/dynamic-texture/mipmaps.ts +0 -517
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {RenderPipelineProps, ComputePipelineProps} from '@luma.gl/core';
|
|
6
|
-
import {Device, RenderPipeline, ComputePipeline, log} from '@luma.gl/core';
|
|
5
|
+
import type {RenderPipelineProps, ComputePipelineProps, SharedRenderPipeline} from '@luma.gl/core';
|
|
6
|
+
import {Device, RenderPipeline, ComputePipeline, Resource, log} from '@luma.gl/core';
|
|
7
7
|
import type {EngineModuleState} from '../types';
|
|
8
8
|
import {uid} from '../utils/uid';
|
|
9
9
|
|
|
10
10
|
export type PipelineFactoryProps = RenderPipelineProps;
|
|
11
11
|
|
|
12
|
-
type
|
|
13
|
-
type ComputePipelineCacheItem = {pipeline: ComputePipeline; useCount: number};
|
|
12
|
+
type CacheItem<ResourceT extends Resource<any>> = {resource: ResourceT; useCount: number};
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Efficiently creates / caches pipelines
|
|
@@ -26,14 +25,12 @@ export class PipelineFactory {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
readonly device: Device;
|
|
29
|
-
readonly cachingEnabled: boolean;
|
|
30
|
-
readonly destroyPolicy: 'unused' | 'never';
|
|
31
|
-
readonly debug: boolean;
|
|
32
28
|
|
|
33
29
|
private _hashCounter: number = 0;
|
|
34
30
|
private readonly _hashes: Record<string, number> = {};
|
|
35
|
-
private readonly _renderPipelineCache: Record<string,
|
|
36
|
-
private readonly _computePipelineCache: Record<string,
|
|
31
|
+
private readonly _renderPipelineCache: Record<string, CacheItem<RenderPipeline>> = {};
|
|
32
|
+
private readonly _computePipelineCache: Record<string, CacheItem<ComputePipeline>> = {};
|
|
33
|
+
private readonly _sharedRenderPipelineCache: Record<string, CacheItem<SharedRenderPipeline>> = {};
|
|
37
34
|
|
|
38
35
|
get [Symbol.toStringTag](): string {
|
|
39
36
|
return 'PipelineFactory';
|
|
@@ -45,14 +42,11 @@ export class PipelineFactory {
|
|
|
45
42
|
|
|
46
43
|
constructor(device: Device) {
|
|
47
44
|
this.device = device;
|
|
48
|
-
this.cachingEnabled = device.props._cachePipelines;
|
|
49
|
-
this.destroyPolicy = device.props._cacheDestroyPolicy;
|
|
50
|
-
this.debug = device.props.debugFactories;
|
|
51
45
|
}
|
|
52
46
|
|
|
53
47
|
/** Return a RenderPipeline matching supplied props. Reuses an equivalent pipeline if already created. */
|
|
54
48
|
createRenderPipeline(props: RenderPipelineProps): RenderPipeline {
|
|
55
|
-
if (!this.
|
|
49
|
+
if (!this.device.props._cachePipelines) {
|
|
56
50
|
return this.device.createRenderPipeline(props);
|
|
57
51
|
}
|
|
58
52
|
|
|
@@ -61,23 +55,28 @@ export class PipelineFactory {
|
|
|
61
55
|
const cache = this._renderPipelineCache;
|
|
62
56
|
const hash = this._hashRenderPipeline(allProps);
|
|
63
57
|
|
|
64
|
-
let pipeline: RenderPipeline = cache[hash]?.
|
|
58
|
+
let pipeline: RenderPipeline = cache[hash]?.resource;
|
|
65
59
|
if (!pipeline) {
|
|
60
|
+
const sharedRenderPipeline =
|
|
61
|
+
this.device.type === 'webgl' && this.device.props._sharePipelines
|
|
62
|
+
? this.createSharedRenderPipeline(allProps)
|
|
63
|
+
: undefined;
|
|
66
64
|
pipeline = this.device.createRenderPipeline({
|
|
67
65
|
...allProps,
|
|
68
|
-
id: allProps.id ? `${allProps.id}-cached` : uid('unnamed-cached')
|
|
66
|
+
id: allProps.id ? `${allProps.id}-cached` : uid('unnamed-cached'),
|
|
67
|
+
_sharedRenderPipeline: sharedRenderPipeline
|
|
69
68
|
});
|
|
70
69
|
pipeline.hash = hash;
|
|
71
|
-
cache[hash] = {pipeline, useCount: 1};
|
|
72
|
-
if (this.
|
|
70
|
+
cache[hash] = {resource: pipeline, useCount: 1};
|
|
71
|
+
if (this.device.props.debugFactories) {
|
|
73
72
|
log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
|
|
74
73
|
}
|
|
75
74
|
} else {
|
|
76
75
|
cache[hash].useCount++;
|
|
77
|
-
if (this.
|
|
76
|
+
if (this.device.props.debugFactories) {
|
|
78
77
|
log.log(
|
|
79
78
|
3,
|
|
80
|
-
`${this}: ${cache[hash].
|
|
79
|
+
`${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`
|
|
81
80
|
)();
|
|
82
81
|
}
|
|
83
82
|
}
|
|
@@ -87,7 +86,7 @@ export class PipelineFactory {
|
|
|
87
86
|
|
|
88
87
|
/** Return a ComputePipeline matching supplied props. Reuses an equivalent pipeline if already created. */
|
|
89
88
|
createComputePipeline(props: ComputePipelineProps): ComputePipeline {
|
|
90
|
-
if (!this.
|
|
89
|
+
if (!this.device.props._cachePipelines) {
|
|
91
90
|
return this.device.createComputePipeline(props);
|
|
92
91
|
}
|
|
93
92
|
|
|
@@ -96,23 +95,23 @@ export class PipelineFactory {
|
|
|
96
95
|
const cache = this._computePipelineCache;
|
|
97
96
|
const hash = this._hashComputePipeline(allProps);
|
|
98
97
|
|
|
99
|
-
let pipeline: ComputePipeline = cache[hash]?.
|
|
98
|
+
let pipeline: ComputePipeline = cache[hash]?.resource;
|
|
100
99
|
if (!pipeline) {
|
|
101
100
|
pipeline = this.device.createComputePipeline({
|
|
102
101
|
...allProps,
|
|
103
102
|
id: allProps.id ? `${allProps.id}-cached` : undefined
|
|
104
103
|
});
|
|
105
104
|
pipeline.hash = hash;
|
|
106
|
-
cache[hash] = {pipeline, useCount: 1};
|
|
107
|
-
if (this.
|
|
105
|
+
cache[hash] = {resource: pipeline, useCount: 1};
|
|
106
|
+
if (this.device.props.debugFactories) {
|
|
108
107
|
log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
|
|
109
108
|
}
|
|
110
109
|
} else {
|
|
111
110
|
cache[hash].useCount++;
|
|
112
|
-
if (this.
|
|
111
|
+
if (this.device.props.debugFactories) {
|
|
113
112
|
log.log(
|
|
114
113
|
3,
|
|
115
|
-
`${this}: ${cache[hash].
|
|
114
|
+
`${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`
|
|
116
115
|
)();
|
|
117
116
|
}
|
|
118
117
|
}
|
|
@@ -121,7 +120,7 @@ export class PipelineFactory {
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
release(pipeline: RenderPipeline | ComputePipeline): void {
|
|
124
|
-
if (!this.
|
|
123
|
+
if (!this.device.props._cachePipelines) {
|
|
125
124
|
pipeline.destroy();
|
|
126
125
|
return;
|
|
127
126
|
}
|
|
@@ -132,40 +131,72 @@ export class PipelineFactory {
|
|
|
132
131
|
cache[hash].useCount--;
|
|
133
132
|
if (cache[hash].useCount === 0) {
|
|
134
133
|
this._destroyPipeline(pipeline);
|
|
135
|
-
if (this.
|
|
134
|
+
if (this.device.props.debugFactories) {
|
|
136
135
|
log.log(3, `${this}: ${pipeline} released and destroyed`)();
|
|
137
136
|
}
|
|
138
137
|
} else if (cache[hash].useCount < 0) {
|
|
139
138
|
log.error(`${this}: ${pipeline} released, useCount < 0, resetting`)();
|
|
140
139
|
cache[hash].useCount = 0;
|
|
141
|
-
} else if (this.
|
|
140
|
+
} else if (this.device.props.debugFactories) {
|
|
142
141
|
log.log(3, `${this}: ${pipeline} released, count=${cache[hash].useCount}`)();
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
|
|
145
|
+
createSharedRenderPipeline(props: RenderPipelineProps): SharedRenderPipeline {
|
|
146
|
+
const sharedPipelineHash = this._hashSharedRenderPipeline(props);
|
|
147
|
+
let sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
|
|
148
|
+
if (!sharedCacheItem) {
|
|
149
|
+
const sharedRenderPipeline = this.device._createSharedRenderPipelineWebGL(props);
|
|
150
|
+
sharedCacheItem = {resource: sharedRenderPipeline, useCount: 0};
|
|
151
|
+
this._sharedRenderPipelineCache[sharedPipelineHash] = sharedCacheItem;
|
|
152
|
+
}
|
|
153
|
+
sharedCacheItem.useCount++;
|
|
154
|
+
return sharedCacheItem.resource;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
releaseSharedRenderPipeline(pipeline: RenderPipeline): void {
|
|
158
|
+
if (!pipeline.sharedRenderPipeline) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const sharedPipelineHash = this._hashSharedRenderPipeline(pipeline.sharedRenderPipeline.props);
|
|
163
|
+
const sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
|
|
164
|
+
if (!sharedCacheItem) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
sharedCacheItem.useCount--;
|
|
169
|
+
if (sharedCacheItem.useCount === 0) {
|
|
170
|
+
sharedCacheItem.resource.destroy();
|
|
171
|
+
delete this._sharedRenderPipelineCache[sharedPipelineHash];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
146
175
|
// PRIVATE
|
|
147
176
|
|
|
148
|
-
/** Destroy a cached pipeline, removing it from the cache
|
|
177
|
+
/** Destroy a cached pipeline, removing it from the cache if configured to do so. */
|
|
149
178
|
private _destroyPipeline(pipeline: RenderPipeline | ComputePipeline): boolean {
|
|
150
179
|
const cache = this._getCache(pipeline);
|
|
151
180
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
181
|
+
if (!this.device.props._destroyPipelines) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
delete cache[pipeline.hash];
|
|
186
|
+
pipeline.destroy();
|
|
187
|
+
if (pipeline instanceof RenderPipeline) {
|
|
188
|
+
this.releaseSharedRenderPipeline(pipeline);
|
|
159
189
|
}
|
|
190
|
+
return true;
|
|
160
191
|
}
|
|
161
192
|
|
|
162
193
|
/** Get the appropriate cache for the type of pipeline */
|
|
163
194
|
private _getCache(
|
|
164
195
|
pipeline: RenderPipeline | ComputePipeline
|
|
165
|
-
): Record<string,
|
|
196
|
+
): Record<string, CacheItem<RenderPipeline>> | Record<string, CacheItem<ComputePipeline>> {
|
|
166
197
|
let cache:
|
|
167
|
-
| Record<string,
|
|
168
|
-
| Record<string,
|
|
198
|
+
| Record<string, CacheItem<RenderPipeline>>
|
|
199
|
+
| Record<string, CacheItem<ComputePipeline>>
|
|
169
200
|
| undefined;
|
|
170
201
|
if (pipeline instanceof ComputePipeline) {
|
|
171
202
|
cache = this._computePipelineCache;
|
|
@@ -193,18 +224,16 @@ export class PipelineFactory {
|
|
|
193
224
|
private _hashRenderPipeline(props: RenderPipelineProps): string {
|
|
194
225
|
const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
|
|
195
226
|
const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
|
|
196
|
-
|
|
197
|
-
// WebGL specific
|
|
198
|
-
// const {varyings = [], bufferMode = {}} = props;
|
|
199
|
-
// const varyingHashes = varyings.map((v) => this._getHash(v));
|
|
200
|
-
const varyingHash = '-'; // `${varyingHashes.join('/')}B${bufferMode}`
|
|
227
|
+
const varyingHash = this._getWebGLVaryingHash(props);
|
|
201
228
|
const bufferLayoutHash = this._getHash(JSON.stringify(props.bufferLayout));
|
|
202
229
|
|
|
203
230
|
const {type} = this.device;
|
|
204
231
|
switch (type) {
|
|
205
232
|
case 'webgl':
|
|
206
|
-
// WebGL
|
|
207
|
-
|
|
233
|
+
// WebGL wrappers preserve default topology and parameter semantics for direct
|
|
234
|
+
// callers, even though the underlying linked program may be shared separately.
|
|
235
|
+
const webglParameterHash = this._getHash(JSON.stringify(props.parameters));
|
|
236
|
+
return `${type}/R/${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${webglParameterHash}BL${bufferLayoutHash}`;
|
|
208
237
|
|
|
209
238
|
case 'webgpu':
|
|
210
239
|
default:
|
|
@@ -216,10 +245,22 @@ export class PipelineFactory {
|
|
|
216
245
|
}
|
|
217
246
|
}
|
|
218
247
|
|
|
248
|
+
private _hashSharedRenderPipeline(props: RenderPipelineProps): string {
|
|
249
|
+
const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
|
|
250
|
+
const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
|
|
251
|
+
const varyingHash = this._getWebGLVaryingHash(props);
|
|
252
|
+
return `webgl/S/${vsHash}/${fsHash}V${varyingHash}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
219
255
|
private _getHash(key: string): number {
|
|
220
256
|
if (this._hashes[key] === undefined) {
|
|
221
257
|
this._hashes[key] = this._hashCounter++;
|
|
222
258
|
}
|
|
223
259
|
return this._hashes[key];
|
|
224
260
|
}
|
|
261
|
+
|
|
262
|
+
private _getWebGLVaryingHash(props: RenderPipelineProps): number {
|
|
263
|
+
const {varyings = [], bufferMode = null} = props;
|
|
264
|
+
return this._getHash(JSON.stringify({varyings, bufferMode}));
|
|
265
|
+
}
|
|
225
266
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import {Device, Shader, ShaderProps, log} from '@luma.gl/core';
|
|
6
6
|
import type {EngineModuleState} from '../types';
|
|
7
7
|
|
|
8
|
+
type CacheItem = {resource: Shader; useCount: number};
|
|
9
|
+
|
|
8
10
|
/** Manages a cached pool of Shaders for reuse. */
|
|
9
11
|
export class ShaderFactory {
|
|
10
12
|
static readonly defaultProps: Required<ShaderProps> = {...Shader.defaultProps};
|
|
@@ -17,11 +19,8 @@ export class ShaderFactory {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
public readonly device: Device;
|
|
20
|
-
readonly cachingEnabled: boolean;
|
|
21
|
-
readonly destroyPolicy: 'unused' | 'never';
|
|
22
|
-
readonly debug: boolean;
|
|
23
22
|
|
|
24
|
-
private readonly _cache: Record<string,
|
|
23
|
+
private readonly _cache: Record<string, CacheItem> = {};
|
|
25
24
|
|
|
26
25
|
get [Symbol.toStringTag](): string {
|
|
27
26
|
return 'ShaderFactory';
|
|
@@ -34,14 +33,11 @@ export class ShaderFactory {
|
|
|
34
33
|
/** @internal */
|
|
35
34
|
constructor(device: Device) {
|
|
36
35
|
this.device = device;
|
|
37
|
-
this.cachingEnabled = device.props._cacheShaders;
|
|
38
|
-
this.destroyPolicy = device.props._cacheDestroyPolicy;
|
|
39
|
-
this.debug = true; // device.props.debugFactories;
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
/** Requests a {@link Shader} from the cache, creating a new Shader only if necessary. */
|
|
43
39
|
createShader(props: ShaderProps): Shader {
|
|
44
|
-
if (!this.
|
|
40
|
+
if (!this.device.props._cacheShaders) {
|
|
45
41
|
return this.device.createShader(props);
|
|
46
42
|
}
|
|
47
43
|
|
|
@@ -49,30 +45,30 @@ export class ShaderFactory {
|
|
|
49
45
|
|
|
50
46
|
let cacheEntry = this._cache[key];
|
|
51
47
|
if (!cacheEntry) {
|
|
52
|
-
const
|
|
48
|
+
const resource = this.device.createShader({
|
|
53
49
|
...props,
|
|
54
50
|
id: props.id ? `${props.id}-cached` : undefined
|
|
55
51
|
});
|
|
56
|
-
this._cache[key] = cacheEntry = {
|
|
57
|
-
if (this.
|
|
58
|
-
log.log(3, `${this}: Created new shader ${
|
|
52
|
+
this._cache[key] = cacheEntry = {resource, useCount: 1};
|
|
53
|
+
if (this.device.props.debugFactories) {
|
|
54
|
+
log.log(3, `${this}: Created new shader ${resource.id}`)();
|
|
59
55
|
}
|
|
60
56
|
} else {
|
|
61
57
|
cacheEntry.useCount++;
|
|
62
|
-
if (this.
|
|
58
|
+
if (this.device.props.debugFactories) {
|
|
63
59
|
log.log(
|
|
64
60
|
3,
|
|
65
|
-
`${this}: Reusing shader ${cacheEntry.
|
|
61
|
+
`${this}: Reusing shader ${cacheEntry.resource.id} count=${cacheEntry.useCount}`
|
|
66
62
|
)();
|
|
67
63
|
}
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
return cacheEntry.
|
|
66
|
+
return cacheEntry.resource;
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
/** Releases a previously-requested {@link Shader}, destroying it if no users remain. */
|
|
74
70
|
release(shader: Shader): void {
|
|
75
|
-
if (!this.
|
|
71
|
+
if (!this.device.props._cacheShaders) {
|
|
76
72
|
shader.destroy();
|
|
77
73
|
return;
|
|
78
74
|
}
|
|
@@ -82,16 +78,16 @@ export class ShaderFactory {
|
|
|
82
78
|
if (cacheEntry) {
|
|
83
79
|
cacheEntry.useCount--;
|
|
84
80
|
if (cacheEntry.useCount === 0) {
|
|
85
|
-
if (this.
|
|
81
|
+
if (this.device.props._destroyShaders) {
|
|
86
82
|
delete this._cache[key];
|
|
87
|
-
cacheEntry.
|
|
88
|
-
if (this.
|
|
83
|
+
cacheEntry.resource.destroy();
|
|
84
|
+
if (this.device.props.debugFactories) {
|
|
89
85
|
log.log(3, `${this}: Releasing shader ${shader.id}, destroyed`)();
|
|
90
86
|
}
|
|
91
87
|
}
|
|
92
88
|
} else if (cacheEntry.useCount < 0) {
|
|
93
89
|
throw new Error(`ShaderFactory: Shader ${shader.id} released too many times`);
|
|
94
|
-
} else if (this.
|
|
90
|
+
} else if (this.device.props.debugFactories) {
|
|
95
91
|
log.log(3, `${this}: Releasing shader ${shader.id} count=${cacheEntry.useCount}`)();
|
|
96
92
|
}
|
|
97
93
|
}
|
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
+
import type {TruncatedConeGeometryProps} from './truncated-cone-geometry';
|
|
5
6
|
import {TruncatedConeGeometry} from './truncated-cone-geometry';
|
|
6
7
|
import {uid} from '../utils/uid';
|
|
7
8
|
|
|
8
|
-
export type ConeGeometryProps =
|
|
9
|
+
export type ConeGeometryProps = Omit<
|
|
10
|
+
TruncatedConeGeometryProps,
|
|
11
|
+
'topRadius' | 'bottomRadius' | 'topCap' | 'bottomCap'
|
|
12
|
+
> & {
|
|
9
13
|
id?: string;
|
|
10
14
|
radius?: number;
|
|
11
15
|
cap?: boolean;
|
|
16
|
+
attributes?: any;
|
|
12
17
|
};
|
|
13
18
|
|
|
14
19
|
export class ConeGeometry extends TruncatedConeGeometry {
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
+
import type {TruncatedConeGeometryProps} from './truncated-cone-geometry';
|
|
5
6
|
import {TruncatedConeGeometry} from './truncated-cone-geometry';
|
|
6
7
|
import {uid} from '../utils/uid';
|
|
7
8
|
|
|
8
|
-
export type CylinderGeometryProps =
|
|
9
|
+
export type CylinderGeometryProps = Omit<
|
|
10
|
+
TruncatedConeGeometryProps,
|
|
11
|
+
'topRadius' | 'bottomRadius'
|
|
12
|
+
> & {
|
|
9
13
|
id?: string;
|
|
10
14
|
radius?: number;
|
|
11
15
|
attributes?: any;
|
package/src/model/model.ts
CHANGED
|
@@ -64,6 +64,8 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
|
|
|
64
64
|
shaderInputs?: ShaderInputs;
|
|
65
65
|
/** Bindings */
|
|
66
66
|
bindings?: Record<string, Binding | DynamicTexture>;
|
|
67
|
+
/** WebGL-only uniforms */
|
|
68
|
+
uniforms?: Record<string, unknown>;
|
|
67
69
|
/** Parameters that are built into the pipeline */
|
|
68
70
|
parameters?: RenderPipelineParameters;
|
|
69
71
|
|
|
@@ -114,7 +116,7 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
|
|
|
114
116
|
* - Reuses and lazily recompiles {@link RenderPipeline | pipelines} as needed.
|
|
115
117
|
* - Integrates with `@luma.gl/shadertools` to assemble GLSL or WGSL from shader modules.
|
|
116
118
|
* - Manages geometry attributes and buffer bindings.
|
|
117
|
-
* - Accepts textures, samplers and uniform buffers as bindings, including `
|
|
119
|
+
* - Accepts textures, samplers and uniform buffers as bindings, including `DynamicTexture`.
|
|
118
120
|
* - Provides detailed debug logging and optional shader source inspection.
|
|
119
121
|
*/
|
|
120
122
|
export class Model {
|
|
@@ -132,6 +134,8 @@ export class Model {
|
|
|
132
134
|
indexBuffer: null,
|
|
133
135
|
attributes: {},
|
|
134
136
|
constantAttributes: {},
|
|
137
|
+
bindings: {},
|
|
138
|
+
uniforms: {},
|
|
135
139
|
varyings: [],
|
|
136
140
|
|
|
137
141
|
isInstanced: undefined!,
|
|
@@ -357,7 +361,7 @@ export class Model {
|
|
|
357
361
|
this.pipelineFactory.release(this.pipeline);
|
|
358
362
|
// Release the shaders
|
|
359
363
|
this.shaderFactory.release(this.pipeline.vs);
|
|
360
|
-
if (this.pipeline.fs) {
|
|
364
|
+
if (this.pipeline.fs && this.pipeline.fs !== this.pipeline.vs) {
|
|
361
365
|
this.shaderFactory.release(this.pipeline.fs);
|
|
362
366
|
}
|
|
363
367
|
this._uniformStore.destroy();
|
|
@@ -422,14 +426,7 @@ export class Model {
|
|
|
422
426
|
// Application can call Model.predraw() to avoid this.
|
|
423
427
|
this.pipeline = this._updatePipeline();
|
|
424
428
|
|
|
425
|
-
// Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
|
|
426
|
-
// Any caching needs to be done inside the pipeline functions
|
|
427
|
-
// TODO this is a busy initialized check for all bindings every frame
|
|
428
|
-
|
|
429
429
|
const syncBindings = this._getBindings();
|
|
430
|
-
this.pipeline.setBindings(syncBindings, {
|
|
431
|
-
disableWarnings: this.props.disableWarnings
|
|
432
|
-
});
|
|
433
430
|
|
|
434
431
|
const {indexBuffer} = this.vertexArray;
|
|
435
432
|
const indexCount = indexBuffer
|
|
@@ -444,6 +441,11 @@ export class Model {
|
|
|
444
441
|
instanceCount: this.instanceCount,
|
|
445
442
|
indexCount,
|
|
446
443
|
transformFeedback: this.transformFeedback || undefined,
|
|
444
|
+
// Pipelines may be shared across models when caching is enabled, so bindings
|
|
445
|
+
// and WebGL uniforms must be supplied on every draw instead of being stored
|
|
446
|
+
// on the pipeline instance.
|
|
447
|
+
bindings: syncBindings,
|
|
448
|
+
uniforms: this.props.uniforms,
|
|
447
449
|
// WebGL shares underlying cached pipelines even for models that have different parameters and topology,
|
|
448
450
|
// so we must provide our unique parameters to each draw
|
|
449
451
|
// (In WebGPU most parameters are encoded in the pipeline and cannot be changed per draw call)
|
|
@@ -827,7 +829,9 @@ export class Model {
|
|
|
827
829
|
);
|
|
828
830
|
|
|
829
831
|
if (prevShaderVs) this.shaderFactory.release(prevShaderVs);
|
|
830
|
-
if (prevShaderFs
|
|
832
|
+
if (prevShaderFs && prevShaderFs !== prevShaderVs) {
|
|
833
|
+
this.shaderFactory.release(prevShaderFs);
|
|
834
|
+
}
|
|
831
835
|
}
|
|
832
836
|
return this.pipeline;
|
|
833
837
|
}
|
|
@@ -22,15 +22,15 @@ struct backgroundUniforms {
|
|
|
22
22
|
};
|
|
23
23
|
@group(0) @binding(2) var<uniform> background: backgroundUniforms;
|
|
24
24
|
|
|
25
|
-
fn billboardTexture_getTextureUV(
|
|
25
|
+
fn billboardTexture_getTextureUV(uv: vec2<f32>) -> vec2<f32> {
|
|
26
26
|
let scale: vec2<f32> = background.scale;
|
|
27
|
-
var position: vec2<f32> = (
|
|
27
|
+
var position: vec2<f32> = (uv - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
|
|
28
28
|
return position;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
@fragment
|
|
32
32
|
fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
|
|
33
|
-
let position: vec2<f32> = billboardTexture_getTextureUV(inputs.
|
|
33
|
+
let position: vec2<f32> = billboardTexture_getTextureUV(inputs.uv);
|
|
34
34
|
return textureSample(backgroundTexture, backgroundTextureSampler, position);
|
|
35
35
|
}
|
|
36
36
|
`;
|
|
@@ -79,21 +79,23 @@ export class BackgroundTextureModel extends ClipSpace {
|
|
|
79
79
|
|
|
80
80
|
constructor(device: Device, props: BackgroundTextureModelProps) {
|
|
81
81
|
super(device, {
|
|
82
|
+
...props,
|
|
82
83
|
id: props.id || 'background-texture-model',
|
|
83
84
|
source: BACKGROUND_FS_WGSL,
|
|
84
85
|
fs: BACKGROUND_FS,
|
|
85
|
-
modules: [backgroundModule],
|
|
86
|
+
modules: [...(props.modules || []), backgroundModule],
|
|
86
87
|
parameters: {
|
|
87
88
|
depthWriteEnabled: false,
|
|
89
|
+
...(props.parameters || {}),
|
|
88
90
|
...(props.blend
|
|
89
91
|
? {
|
|
90
92
|
blend: true,
|
|
91
93
|
blendColorOperation: 'add',
|
|
92
94
|
blendAlphaOperation: 'add',
|
|
93
|
-
blendColorSrcFactor: 'one',
|
|
94
|
-
blendColorDstFactor: 'one
|
|
95
|
-
blendAlphaSrcFactor: 'one',
|
|
96
|
-
blendAlphaDstFactor: 'one
|
|
95
|
+
blendColorSrcFactor: 'one-minus-dst-alpha',
|
|
96
|
+
blendColorDstFactor: 'one',
|
|
97
|
+
blendAlphaSrcFactor: 'one-minus-dst-alpha',
|
|
98
|
+
blendAlphaDstFactor: 'one'
|
|
97
99
|
}
|
|
98
100
|
: {})
|
|
99
101
|
}
|
package/src/models/clip-space.ts
CHANGED
|
@@ -10,9 +10,9 @@ import {uid} from '../utils/uid';
|
|
|
10
10
|
|
|
11
11
|
const CLIPSPACE_VERTEX_SHADER_WGSL = /* wgsl */ `\
|
|
12
12
|
struct VertexInputs {
|
|
13
|
-
@location(0)
|
|
14
|
-
@location(1)
|
|
15
|
-
@location(2)
|
|
13
|
+
@location(0) clipSpacePositions: vec2<f32>,
|
|
14
|
+
@location(1) texCoords: vec2<f32>,
|
|
15
|
+
@location(2) coordinates: vec2<f32>
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
struct FragmentInputs {
|
|
@@ -25,10 +25,10 @@ struct FragmentInputs {
|
|
|
25
25
|
@vertex
|
|
26
26
|
fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
|
|
27
27
|
var outputs: FragmentInputs;
|
|
28
|
-
outputs.Position = vec4(inputs.
|
|
29
|
-
outputs.position = inputs.
|
|
30
|
-
outputs.coordinate = inputs.
|
|
31
|
-
outputs.uv = inputs.
|
|
28
|
+
outputs.Position = vec4(inputs.clipSpacePositions, 0., 1.);
|
|
29
|
+
outputs.position = inputs.clipSpacePositions;
|
|
30
|
+
outputs.coordinate = inputs.coordinates;
|
|
31
|
+
outputs.uv = inputs.texCoords;
|
|
32
32
|
return outputs;
|
|
33
33
|
}
|
|
34
34
|
`;
|
|
@@ -16,12 +16,6 @@ const INDEX_PICKING_MODE_INSTANCE = 0;
|
|
|
16
16
|
const INDEX_PICKING_MODE_CUSTOM = 1;
|
|
17
17
|
const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
|
|
18
18
|
|
|
19
|
-
struct indexPickingFragmentInputs = {
|
|
20
|
-
objectIndex: int32;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
let indexPickingFragmentInputs: indexPickingFragmentInputs;
|
|
24
|
-
|
|
25
19
|
/**
|
|
26
20
|
* Vertex shaders should call this function to set the object index.
|
|
27
21
|
* If using instance or vertex mode, argument will be ignored, 0 can be supplied.
|
|
@@ -36,26 +36,16 @@ export function getFragmentShaderForRenderPass(options: {
|
|
|
36
36
|
/** Get a filtering WGSL fragment shader */
|
|
37
37
|
function getFilterShaderWGSL(func: string) {
|
|
38
38
|
return /* wgsl */ `\
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@group(0) @binding(1) var texture: texture_2d<f32>;
|
|
42
|
-
@group(0) @binding(2) var textureSampler: sampler;
|
|
43
|
-
|
|
44
|
-
// This needs to be aligned with
|
|
45
|
-
// struct FragmentInputs {
|
|
46
|
-
// @location(0) fragUV: vec2f,
|
|
47
|
-
// @location(1) fragPosition: vec4f,
|
|
48
|
-
// @location(2) fragCoordinate: vec4f
|
|
49
|
-
// };
|
|
39
|
+
@group(0) @binding(0) var sourceTexture: texture_2d<f32>;
|
|
40
|
+
@group(0) @binding(2) var sourceTextureSampler: sampler;
|
|
50
41
|
|
|
51
42
|
@fragment
|
|
52
43
|
fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
|
|
53
|
-
let
|
|
54
|
-
let
|
|
55
|
-
let texSize = vec2f(textureDimensions(texture, 0));
|
|
44
|
+
let texCoord = inputs.coordinate;
|
|
45
|
+
let texSize = vec2f(textureDimensions(sourceTexture));
|
|
56
46
|
|
|
57
|
-
var fragColor = textureSample(
|
|
58
|
-
fragColor = ${func}(fragColor, texSize,
|
|
47
|
+
var fragColor = textureSample(sourceTexture, sourceTextureSampler, texCoord);
|
|
48
|
+
fragColor = ${func}(fragColor, texSize, texCoord);
|
|
59
49
|
return fragColor;
|
|
60
50
|
}
|
|
61
51
|
`;
|
|
@@ -64,23 +54,14 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
|
|
|
64
54
|
/** Get a sampling WGSL fragment shader */
|
|
65
55
|
function getSamplerShaderWGSL(func: string) {
|
|
66
56
|
return /* wgsl */ `\
|
|
67
|
-
|
|
68
|
-
@group(0) @binding(
|
|
69
|
-
@group(0) @binding(1) var texture: texture_2d<f32>;
|
|
70
|
-
@group(0) @binding(2) var sampler: sampler;
|
|
71
|
-
|
|
72
|
-
struct FragmentInputs = {
|
|
73
|
-
@location(0) fragUV: vec2f,
|
|
74
|
-
@location(1) fragPosition: vec4f,
|
|
75
|
-
@location(2) fragCoordinate: vec4f
|
|
76
|
-
};
|
|
57
|
+
@group(0) @binding(0) var sourceTexture: texture_2d<f32>;
|
|
58
|
+
@group(0) @binding(2) var sourceTextureSampler: sampler;
|
|
77
59
|
|
|
78
60
|
@fragment
|
|
79
61
|
fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
|
|
80
|
-
let
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return fragColor;
|
|
62
|
+
let texCoord = inputs.coordinate;
|
|
63
|
+
let texSize = vec2f(textureDimensions(sourceTexture));
|
|
64
|
+
return ${func}(sourceTexture, sourceTextureSampler, texSize, texCoord);
|
|
84
65
|
}
|
|
85
66
|
`;
|
|
86
67
|
}
|