Skip to content

Commit 762299b

Browse files
underootgithub-actions[bot]
authored andcommitted
Move dependencies tracking code from StyleLayer
GitOrigin-RevId: 4004823dc4eec979ad80596d8d958ad1cc6221d4
1 parent a34efd2 commit 762299b

6 files changed

Lines changed: 251 additions & 58 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {createExpression} from '../style-spec/expression/index';
2+
import latest from '../style-spec/reference/latest';
3+
import assert from '../style-spec/util/assert';
4+
5+
import type StyleLayer from './style_layer';
6+
7+
type ExpressionDependencies = {
8+
configDependencies: Set<string>;
9+
isIndoorDependent: boolean;
10+
};
11+
12+
/**
13+
* A read-through view over the config options and other dynamic inputs a
14+
* layer's expressions depend on. Layout and paint dependencies are read live
15+
* from the layer's `Layout` and `Transitionable` containers, which track them
16+
* as property values are set. Dependencies of the layer's filter are computed
17+
* here, lazily on first query (compiling the static filter is not free), and
18+
* can be dropped with {@link invalidateFilter} when the filter changes.
19+
*
20+
* Lives outside `StyleLayer` on purpose: only `Style` queries dependencies,
21+
* so worker-side layers carry no dependency-tracking state or work.
22+
*
23+
* @private
24+
*/
25+
export class LayerExpressionDependencies {
26+
_layer: StyleLayer;
27+
// undefined = not yet computed; null = computed, layer's filter has no dependencies
28+
_filterDependencies: ExpressionDependencies | null | undefined;
29+
30+
constructor(layer: StyleLayer) {
31+
this._layer = layer;
32+
this._filterDependencies = undefined;
33+
}
34+
35+
invalidateFilter() {
36+
this._filterDependencies = undefined;
37+
}
38+
39+
get _filterDeps(): ExpressionDependencies | null {
40+
if (this._filterDependencies === undefined) {
41+
this._filterDependencies = this._computeFilterDependencies();
42+
}
43+
return this._filterDependencies;
44+
}
45+
46+
_computeFilterDependencies(): ExpressionDependencies | null {
47+
const layer = this._layer;
48+
if (!layer.type || layer.type === 'custom' || layer.type === 'background' || layer.type === 'sky' || layer.type === 'slot') return null;
49+
50+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
51+
const filterSpec = latest[`filter_${layer.type}`];
52+
assert(filterSpec);
53+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
54+
const compiledStaticFilter = createExpression(layer.filter, filterSpec);
55+
if (compiledStaticFilter.result === 'error') return null;
56+
57+
return {
58+
configDependencies: compiledStaticFilter.value.configDependencies,
59+
isIndoorDependent: compiledStaticFilter.value.isIndoorDependent
60+
};
61+
}
62+
63+
hasConfigDependency(key: string): boolean {
64+
const {_unevaluatedLayout, _transitionablePaint} = this._layer;
65+
if (_unevaluatedLayout && _unevaluatedLayout.configDependencies.has(key)) return true;
66+
if (_transitionablePaint && _transitionablePaint.configDependencies.has(key)) return true;
67+
const filterDeps = this._filterDeps;
68+
return filterDeps ? filterDeps.configDependencies.has(key) : false;
69+
}
70+
71+
get isConfigDependent(): boolean {
72+
const {_unevaluatedLayout, _transitionablePaint} = this._layer;
73+
if (_unevaluatedLayout && _unevaluatedLayout.configDependencies.size !== 0) return true;
74+
if (_transitionablePaint && _transitionablePaint.configDependencies.size !== 0) return true;
75+
const filterDeps = this._filterDeps;
76+
return filterDeps ? filterDeps.configDependencies.size !== 0 : false;
77+
}
78+
79+
get isIndoorDependent(): boolean {
80+
const {_unevaluatedLayout, _transitionablePaint} = this._layer;
81+
if (_unevaluatedLayout && _unevaluatedLayout.isIndoorDependent()) return true;
82+
if (_transitionablePaint && _transitionablePaint.isIndoorDependent()) return true;
83+
const filterDeps = this._filterDeps;
84+
return filterDeps ? filterDeps.isIndoorDependent : false;
85+
}
86+
}

src/style/style.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import murmur3 from '../util/murmur3';
33
import {Event, ErrorEvent, Evented} from '../util/evented';
44
import StyleChanges from './style_changes';
55
import createStyleLayer from './create_style_layer';
6+
import {LayerExpressionDependencies} from './layer_expression_dependencies';
67
import loadSprite from './load_sprite';
78
import ImageManager from '../render/image_manager';
89
import GlyphManager, {LocalGlyphMode} from '../render/glyph_manager';
@@ -262,8 +263,7 @@ export type StyleOptions = {
262263
initialConfig?: {
263264
[key: string]: ConfigSpecification;
264265
};
265-
configDependentLayers?: Set<string>;
266-
indoorDependentLayers?: Set<string>;
266+
layerExpressionDependencies?: Map<string, LayerExpressionDependencies>;
267267
};
268268

269269
export type StyleSetterOptions = {
@@ -384,8 +384,9 @@ class Style extends Evented<MapEvents> {
384384
_availableModels: StyleModelMap;
385385
_markersNeedUpdate: boolean;
386386
_brightness: number | null | undefined;
387-
_configDependentLayers: Set<string>;
388-
_indoorDependentLayers: Set<string>;
387+
// Expression dependencies of every layer in the style tree, keyed by layer
388+
// fqid and shared by reference between the root style and its fragments.
389+
_layerExpressionDependencies: Map<string, LayerExpressionDependencies>;
389390
_config: ConfigSpecification | null | undefined;
390391
_initialConfig: {
391392
[key: string]: ConfigSpecification;
@@ -535,8 +536,7 @@ class Style extends Evented<MapEvents> {
535536

536537
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
537538
this.options = options.configOptions ? options.configOptions : new Map();
538-
this._configDependentLayers = options.configDependentLayers ? options.configDependentLayers : new Set();
539-
this._indoorDependentLayers = options.indoorDependentLayers ? options.indoorDependentLayers : new Set();
539+
this._layerExpressionDependencies = options.layerExpressionDependencies ? options.layerExpressionDependencies : new Map<string, LayerExpressionDependencies>();
540540
this._config = options.config;
541541
this._styleColorTheme = {
542542
lut: null,
@@ -832,8 +832,7 @@ class Style extends Evented<MapEvents> {
832832
config,
833833
configOptions: this.options,
834834
colorThemeOverride: importSpec["color-theme"],
835-
configDependentLayers: this._configDependentLayers,
836-
indoorDependentLayers: this._indoorDependentLayers
835+
layerExpressionDependencies: this._layerExpressionDependencies
837836
});
838837

839838
// Bubble all events fired by the style to the map.
@@ -848,7 +847,7 @@ class Style extends Evented<MapEvents> {
848847
if (!initialLoad) {
849848
this.updateConfigDependencies();
850849
}
851-
this._updateLayers(this._indoorDependentLayers);
850+
this._updateLayers(this._dependentLayerIds((deps) => deps.isIndoorDependent));
852851
this.map._triggerCameraUpdate(this.camera);
853852

854853
// During root's initial-load batch, sibling fragments share a single
@@ -961,8 +960,7 @@ class Style extends Evented<MapEvents> {
961960
let hasPendingHdCoverage = false;
962961
for (const layer of layers) {
963962
const styleLayer = createStyleLayer(layer, this.scope, this._styleColorTheme.lut, this.options);
964-
if (styleLayer.expressionDependencies.configDependencies.size !== 0) this._configDependentLayers.add(styleLayer.fqid);
965-
if (styleLayer.expressionDependencies.isIndoorDependent) this._indoorDependentLayers.add(styleLayer.fqid);
963+
this._layerExpressionDependencies.set(styleLayer.fqid, new LayerExpressionDependencies(styleLayer));
966964
this._hasAppearances = this._hasAppearances || styleLayer.getAppearances().length !== 0;
967965
styleLayer.setEventedParent(this, {layer: {id: styleLayer.id}});
968966
this._layers[styleLayer.id] = styleLayer;
@@ -2778,7 +2776,7 @@ class Style extends Evented<MapEvents> {
27782776
}
27792777

27802778
updateIndoorDependentLayers() {
2781-
this._updateLayers(this._indoorDependentLayers);
2779+
this._updateLayers(this._dependentLayerIds((deps) => deps.isIndoorDependent));
27822780
this.map._styleDirty = true;
27832781
this.map.triggerRepaint();
27842782
}
@@ -2907,21 +2905,29 @@ class Style extends Evented<MapEvents> {
29072905
}
29082906
}
29092907

2910-
_updateLayers(layerIds: Set<string>, condition: (layer: TypedStyleLayer) => boolean = () => true) {
2908+
_updateLayers(layerIds: Iterable<string>) {
29112909
for (const id of layerIds) {
29122910
const layer = this.getLayer(id);
2913-
if (layer && condition(layer)) {
2911+
if (layer) {
29142912
layer.possiblyEvaluateVisibility();
29152913
this._updateLayer(layer);
29162914
this._changes.setDirty();
29172915
}
29182916
}
29192917
}
29202918

2919+
_dependentLayerIds(predicate: (deps: LayerExpressionDependencies) => boolean): Array<string> {
2920+
const ids: Array<string> = [];
2921+
for (const [id, deps] of this._layerExpressionDependencies) {
2922+
if (predicate(deps)) ids.push(id);
2923+
}
2924+
return ids;
2925+
}
2926+
29212927
updateConfigDependencies(configKey?: string) {
2922-
this._updateLayers(this._configDependentLayers, (layer) => {
2923-
return configKey ? layer.expressionDependencies.configDependencies.has(configKey) : true;
2924-
});
2928+
this._updateLayers(this._dependentLayerIds((deps) => {
2929+
return configKey ? deps.hasConfigDependency(configKey) : deps.isConfigDependent;
2930+
}));
29252931

29262932
if (this.ambientLight) {
29272933
this.ambientLight.updateConfig(this.options);
@@ -2998,9 +3004,7 @@ class Style extends Evented<MapEvents> {
29983004
layer.setEventedParent(this, {layer: {id}});
29993005
}
30003006

3001-
const fqid = makeFQID(layer.source, layer.scope);
3002-
if (layer.expressionDependencies.configDependencies.size !== 0) this._configDependentLayers.add(fqid);
3003-
if (layer.expressionDependencies.isIndoorDependent) this._indoorDependentLayers.add(fqid);
3007+
this._layerExpressionDependencies.set(layer.fqid, new LayerExpressionDependencies(layer));
30043008

30053009
let index = this._order.length;
30063010
if (before) {
@@ -3125,8 +3129,7 @@ class Style extends Evented<MapEvents> {
31253129
this._changes.setDirty();
31263130
this._handleLayerOrderChange();
31273131

3128-
this._configDependentLayers.delete(layer.fqid);
3129-
this._indoorDependentLayers.delete(layer.fqid);
3132+
this._layerExpressionDependencies.delete(layer.fqid);
31303133
this._changes.removeLayer(layer);
31313134

31323135
const sourceCache = this.getOwnLayerSourceCache(layer);
@@ -3232,8 +3235,11 @@ class Style extends Evented<MapEvents> {
32323235
return;
32333236
}
32343237

3238+
const dependencies = this._layerExpressionDependencies.get(layer.fqid);
3239+
32353240
if (filter === null || filter === undefined) {
32363241
layer.filter = undefined;
3242+
if (dependencies) dependencies.invalidateFilter();
32373243
this._updateLayer(layer);
32383244
return;
32393245
}
@@ -3243,6 +3249,7 @@ class Style extends Evented<MapEvents> {
32433249
}
32443250

32453251
layer.filter = structuredClone(filter);
3252+
if (dependencies) dependencies.invalidateFilter();
32463253
this._updateLayer(layer);
32473254
}
32483255

@@ -3282,8 +3289,6 @@ class Style extends Evented<MapEvents> {
32823289
}
32833290

32843291
layer.setLayoutProperty(name, value);
3285-
if (layer.expressionDependencies.configDependencies.size !== 0) this._configDependentLayers.add(layer.fqid);
3286-
if (layer.expressionDependencies.isIndoorDependent) this._indoorDependentLayers.add(layer.fqid);
32873292
this._updateLayer(layer);
32883293
}
32893294

@@ -3365,8 +3370,6 @@ class Style extends Evented<MapEvents> {
33653370
}
33663371

33673372
const requiresRelayout = layer.setPaintProperty(name, value);
3368-
if (layer.expressionDependencies.configDependencies.size !== 0) this._configDependentLayers.add(layer.fqid);
3369-
if (layer.expressionDependencies.isIndoorDependent) this._indoorDependentLayers.add(layer.fqid);
33703373
if (requiresRelayout) {
33713374
this._updateLayer(layer);
33723375
}

src/style/style_layer.ts

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import {Layout, Transitionable, PossiblyEvaluated, PossiblyEvaluatedPropertyValu
44
import {supportsPropertyExpression} from '../style-spec/util/properties';
55
import featureFilter from '../style-spec/feature_filter/index';
66
import {makeFQID} from '../util/fqid';
7-
import {createExpression, type FeatureState} from '../style-spec/expression/index';
7+
import {type FeatureState} from '../style-spec/expression/index';
88
import {isStateConstant} from '../style-spec/expression/is_constant';
9-
import latest from '../style-spec/reference/latest';
109
import assert from '../style-spec/util/assert';
1110
import SymbolAppearance from './appearance';
1211

@@ -49,11 +48,6 @@ type LayerRenderingStats = {
4948
// Symbols are draped only on native and for certain cases only
5049
const drapedLayers = new Set(['fill', 'line', 'background', 'hillshade', 'raster']);
5150

52-
type LayerExpressionDependencies = {
53-
isIndoorDependent: boolean;
54-
configDependencies: Set<string>;
55-
};
56-
5751
class StyleLayer extends Evented {
5852
id: string;
5953
fqid: string;
@@ -68,7 +62,6 @@ class StyleLayer extends Evented {
6862
maxzoom: number | null | undefined;
6963
filter: FilterSpecification | undefined;
7064
visibility: 'visible' | 'none' | undefined;
71-
expressionDependencies: LayerExpressionDependencies;
7265
iconImageUseTheme: string | null | undefined;
7366
appearances: Array<SymbolAppearance>;
7467
appearancesVersion: number;
@@ -114,10 +107,6 @@ class StyleLayer extends Evented {
114107

115108
this._featureFilter = {filter: () => true, needGeometry: false, needFeature: false};
116109
this._filterCompiled = false;
117-
this.expressionDependencies = {
118-
isIndoorDependent: false,
119-
configDependencies: new Set()
120-
};
121110

122111
if (layer.type === 'custom') return;
123112

@@ -129,16 +118,6 @@ class StyleLayer extends Evented {
129118
this.source = layer.source;
130119
this.sourceLayer = layer['source-layer'];
131120
this.filter = layer.filter;
132-
133-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
134-
const filterSpec = latest[`filter_${layer.type}`];
135-
assert(filterSpec);
136-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
137-
const compiledStaticFilter = createExpression(this.filter, filterSpec);
138-
if (compiledStaticFilter.result !== 'error') {
139-
this.expressionDependencies.configDependencies = new Set([...this.expressionDependencies.configDependencies, ...compiledStaticFilter.value.configDependencies]);
140-
this.expressionDependencies.isIndoorDependent = this.expressionDependencies.isIndoorDependent || compiledStaticFilter.value.isIndoorDependent;
141-
}
142121
}
143122

144123
if (layer.slot) this.slot = layer.slot;
@@ -149,8 +128,6 @@ class StyleLayer extends Evented {
149128

150129
if (properties.layout) {
151130
this._unevaluatedLayout = new Layout(properties.layout, this.scope, options, this.iconImageUseTheme);
152-
this.expressionDependencies.configDependencies = new Set([...this.expressionDependencies.configDependencies, ...this._unevaluatedLayout.configDependencies]);
153-
this.expressionDependencies.isIndoorDependent = this.expressionDependencies.isIndoorDependent || this._unevaluatedLayout.isIndoorDependent();
154131
}
155132

156133
if (properties.paint) {
@@ -164,9 +141,6 @@ class StyleLayer extends Evented {
164141
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
165142
this.setLayoutProperty(property, layer.layout[property]);
166143
}
167-
this.expressionDependencies.configDependencies = new Set([...this.expressionDependencies.configDependencies, ...this._transitionablePaint.configDependencies]);
168-
this.expressionDependencies.isIndoorDependent = this.expressionDependencies.isIndoorDependent || this._transitionablePaint.isIndoorDependent();
169-
170144
this._transitioningPaint = this._transitionablePaint.untransitioned();
171145
this.paint = new PossiblyEvaluated(properties.paint);
172146
}
@@ -203,8 +177,6 @@ class StyleLayer extends Evented {
203177
if (!specProps[name]) return; // skip unrecognized properties
204178

205179
layout.setValue(name, value);
206-
this.expressionDependencies.configDependencies = new Set([...this.expressionDependencies.configDependencies, ...layout.configDependencies]);
207-
this.expressionDependencies.isIndoorDependent = this.expressionDependencies.isIndoorDependent || layout.isIndoorDependent();
208180
if (name === 'visibility') {
209181
this.possiblyEvaluateVisibility();
210182
}
@@ -266,8 +238,6 @@ class StyleLayer extends Evented {
266238
const oldValue = transitionable.value;
267239

268240
paint.setValue(name, value as PropertyValueSpecification<unknown>);
269-
this.expressionDependencies.configDependencies = new Set([...this.expressionDependencies.configDependencies, ...paint.configDependencies]);
270-
this.expressionDependencies.isIndoorDependent = this.expressionDependencies.isIndoorDependent || paint.isIndoorDependent();
271241
this._handleSpecialPaintPropertyUpdate(name);
272242

273243
const newValue = paint._values[name].value;

0 commit comments

Comments
 (0)