Skip to content

Commit 9a6ee17

Browse files
committed
refactored fill texture creation. replace GradientFactory with TextureManager for gradient texture handling and us unified cache
Signed-off-by: Tim Deubler <[email protected]>
1 parent 933d988 commit 9a6ee17

File tree

6 files changed

+121
-67
lines changed

6 files changed

+121
-67
lines changed

packages/display/src/displays/webgl/Display.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,14 +229,7 @@ class WebGlDisplay extends BasicDisplay {
229229
}
230230

231231
private initSky(skyColor: Color | LinearGradient) {
232-
let color;
233-
if (this.factory.gradients.isGradient(skyColor)) {
234-
color = this.factory.gradients.getTexture((<unknown>skyColor as LinearGradient));
235-
} else {
236-
// TODO: Separate the texture cache from the gradient factory, and add support for caching simple color textures in the texture cache for efficiency.
237-
const hexColor = rgbaToHexString(toRGB(skyColor as Color, true));
238-
color = this.factory.gradients.getTexture({0: hexColor, 1: hexColor});
239-
}
232+
const color = this.factory.textureManager.getFillTexture(skyColor);
240233
this.render.setSkyColor(color);
241234
}
242235

packages/display/src/displays/webgl/GLRender.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ import {Attribute} from './buffer/Attribute';
6363
import {GLExtensions} from './GLExtensions';
6464
import {Texture} from './Texture';
6565
import {Color} from '@here/xyz-maps-common';
66-
import {GradientTexture} from './GradientFactory';
66+
import {FillTexture} from './TextureManager';
6767
import {ViewportTile} from '../BasicDisplay';
6868
import toRGB = Color.toRGB;
6969

@@ -274,7 +274,7 @@ export class GLRender implements BasicRender {
274274
this.gl?.clearColor(color[0], color[1], color[2], color[3] ?? 1.0);
275275
}
276276

277-
setSkyColor(color: Color.RGBA | GradientTexture) {
277+
setSkyColor(color: Color.RGBA | FillTexture) {
278278
this.skyBuffer.addUniform('u_fill', color);
279279
}
280280

packages/display/src/displays/webgl/GradientFactory.ts

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,9 @@
1717
* License-Filename: LICENSE
1818
*/
1919

20-
import {Texture} from './Texture';
2120
import {LinearGradient} from '@here/xyz-maps-core';
22-
23-
type LinearGradientStops = LinearGradient['stops'];
24-
25-
export class GradientTexture extends Texture {
26-
ref: number = 0;
27-
gradient: any;
28-
factory: GradientFactory;
29-
30-
destroy() {
31-
this.factory.dropTexture(this);
32-
super.destroy();
33-
}
34-
}
35-
36-
37-
// let gradientTextCache = new Map();
38-
// window._gradientTextCache = gradientTextCache;
39-
// const defaultGradientConfig = {0: 'rgba(0, 0, 255, 0)', 0.1: 'royalblue', 0.3: 'cyan', 0.5: 'lime', 0.7: 'yellow', 1.0: 'red'};
40-
21+
import {FillTexture} from './TextureManager';
22+
export type LinearGradientStops = LinearGradient['stops'];
4123

4224
export class GradientFactory {
4325
static canvas: HTMLCanvasElement = document.createElement('canvas');
@@ -46,7 +28,6 @@ export class GradientFactory {
4628
private width: number;
4729
private height: number;
4830
private gl: WebGLRenderingContext;
49-
private cache: Map<LinearGradientStops, GradientTexture> = new Map();
5031

5132
constructor(gl: WebGLRenderingContext, width: number = 256, height: number = 1) {
5233
this.gl = gl;
@@ -58,47 +39,32 @@ export class GradientFactory {
5839
return typeof gradient?.type == 'string' && typeof gradient.stops == 'object';
5940
}
6041

61-
getTexture(linearGradient: LinearGradientStops | LinearGradient, preprocessor?: (stops: LinearGradientStops) => LinearGradientStops): GradientTexture {
42+
createTexture(linearGradient: LinearGradientStops | LinearGradient, preprocessor?: (stops: LinearGradientStops) => LinearGradientStops): FillTexture {
6243
const gradientStops: LinearGradientStops = this.isGradient(linearGradient)
6344
? (linearGradient as LinearGradient).stops
6445
: linearGradient as LinearGradientStops;
6546

66-
let texture = this.cache.get(gradientStops);
67-
68-
if (!texture) {
69-
const canvas = GradientFactory.canvas;
70-
const ctx = GradientFactory.ctx;
71-
const {width, height} = this;
72-
canvas.width = width;
73-
canvas.height = height;
74-
75-
const gradient = ctx.createLinearGradient(0, 0, width, height);
47+
const canvas = GradientFactory.canvas;
48+
const ctx = GradientFactory.ctx;
49+
const {width, height} = this;
50+
canvas.width = width;
51+
canvas.height = height;
7652

77-
const stops = preprocessor?.(gradientStops) || gradientStops;
53+
const gradient = ctx.createLinearGradient(0, 0, width, height);
7854

79-
for (var key in stops) {
80-
gradient.addColorStop(Number(key), stops[key]);
81-
}
82-
// ctx.clearRect(0, 0, width, height);
83-
ctx.fillStyle = gradient;
84-
ctx.fillRect(0, 0, width, height);
85-
// return paletteCtx.getImageData(0, 0, 256, 1).data;
55+
const stops = preprocessor?.(gradientStops) || gradientStops;
8656

87-
const data = ctx.getImageData(0, 0, width, height).data;
88-
89-
texture = new GradientTexture(this.gl, {width, height, data}, {premultiplyAlpha: true});
90-
texture.gradient = gradientStops;
91-
texture.factory = this;
92-
93-
this.cache.set(gradientStops, texture);
57+
for (var key in stops) {
58+
gradient.addColorStop(Number(key), stops[key]);
9459
}
95-
texture.ref++;
60+
// ctx.clearRect(0, 0, width, height);
61+
ctx.fillStyle = gradient;
62+
ctx.fillRect(0, 0, width, height);
63+
// return paletteCtx.getImageData(0, 0, 256, 1).data;
9664

97-
return texture;
98-
}
65+
const data = ctx.getImageData(0, 0, width, height).data;
9966

100-
dropTexture(texture: GradientTexture) {
101-
this.cache.delete(texture.gradient);
67+
return new FillTexture(this.gl, {width, height, data}, {premultiplyAlpha: true});
10268
}
10369
}
10470

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (C) 2019-2025 HERE Europe B.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
import {Texture} from './Texture';
20+
import {GradientFactory, LinearGradientStops} from './GradientFactory';
21+
import {Color, LinearGradient} from '@here/xyz-maps-core';
22+
import {Color as Colors} from '@here/xyz-maps-common';
23+
24+
export class FillTexture extends Texture {
25+
ref: number = 0;
26+
id: Color | LinearGradientStops | LinearGradient;
27+
cache: TextureCache;
28+
29+
destroy() {
30+
this.cache.delete(this.id);
31+
super.destroy();
32+
}
33+
}
34+
35+
export type TextureCache = Map<Color | LinearGradientStops | LinearGradient, FillTexture>;
36+
37+
type TextureInput = Color | LinearGradientStops | LinearGradient;
38+
39+
export class TextureManager {
40+
private gradients: GradientFactory;
41+
private gl: WebGLRenderingContext;
42+
43+
constructor(gl: WebGLRenderingContext) {
44+
this.gradients = new GradientFactory(gl, 256, 1);
45+
this.gl = gl;
46+
}
47+
48+
private textures: Map<TextureInput, FillTexture> = new Map();
49+
50+
getFillTexture(color: TextureInput): FillTexture | undefined {
51+
const {gradients} = this;
52+
let texture = this.textures.get(color);
53+
if (!texture) {
54+
if (gradients.isGradient(color)) {
55+
texture = gradients.createTexture((<unknown>color as LinearGradient));
56+
} else {
57+
const rgba = Colors.toRGB(color as Color, true);
58+
texture = new FillTexture(this.gl, {data: new Uint8Array(rgba.map((c) => c * 255)), width: 1, height: 1});
59+
}
60+
this.addTexture(color, texture);
61+
}
62+
texture.ref++;
63+
return texture;
64+
}
65+
66+
getGradientTexture(gradient: LinearGradientStops | LinearGradient, preprocessor: (stops: LinearGradientStops) => LinearGradientStops): FillTexture | undefined {
67+
let texture = this.textures.get(gradient);
68+
if (!texture) {
69+
texture = this.gradients.createTexture(gradient, preprocessor);
70+
this.addTexture(gradient, texture);
71+
}
72+
texture.ref++;
73+
return texture;
74+
}
75+
76+
addTexture(id: TextureInput, texture: FillTexture): void {
77+
this.textures.set(id, texture);
78+
texture.id = id;
79+
texture.cache = this.textures;
80+
}
81+
82+
removeTexture(id: string): void {
83+
const texture = this.textures.get(id);
84+
if (texture) {
85+
texture.destroy();
86+
this.textures.delete(id);
87+
}
88+
}
89+
90+
clear(): void {
91+
for (const texture of this.textures.values()) {
92+
texture.destroy();
93+
}
94+
this.textures.clear();
95+
}
96+
};

packages/display/src/displays/webgl/buffer/FeatureFactory.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ import {ModelStyle, Color} from '@here/xyz-maps-core';
4949
import {ModelFactory} from './ModelFactory';
5050
import {ModelBuffer} from './templates/ModelBuffer';
5151
import {ImageInfo} from '../Atlas';
52-
import {GradientFactory} from '../GradientFactory';
5352
import {HeatmapBuffer} from './templates/HeatmapBuffer';
5453
import {TextureAtlasManager} from '../TextureAtlasManager';
5554
import {LineBuffer} from './templates/LineBuffer';
5655
import {Color as ColorUtils, Expression, ExpressionMode} from '@here/xyz-maps-common';
56+
import {TextureManager} from '../TextureManager';
5757

5858
const {toRGB} = ColorUtils;
5959
type RGBA = ColorUtils.RGBA;
@@ -148,18 +148,18 @@ export class FeatureFactory {
148148
pendingCollisions: CollisionGroup[] = [];
149149
z: number;
150150
private waitAndRefresh: (p: Promise<any>) => void;
151-
gradients: GradientFactory;
151+
textureManager: TextureManager;
152152
private zLayer: number;
153153

154154
constructor(gl: WebGLRenderingContext, collisionHandler, devicePixelRatio: number) {
155155
this.gl = gl;
156156
this.atlasManager = new TextureAtlasManager(gl);
157+
this.textureManager = new TextureManager(gl);
157158
this.dpr = devicePixelRatio;
158159
this.collisions = collisionHandler;
159160
this.lineFactory = new LineFactory(gl);
160161
this.modelFactory = new ModelFactory(gl);
161162

162-
this.gradients = new GradientFactory(gl, 256, 1);
163163

164164
const pixelCnt = 512 * 512;
165165
const pixelData = new Uint8Array(pixelCnt * 4);
@@ -868,7 +868,6 @@ export class FeatureFactory {
868868
const x = tile.lon2x((<GeoJSONCoordinate>coordinates)[0], tileSize);
869869
const y = tile.lat2y((<GeoJSONCoordinate>coordinates)[1], tileSize);
870870
const z = typeof altitude == 'number' ? altitude : altitude ? <number>coordinates[2] || 0 : null;
871-
// const z = extrude ? <number>coordinates[2] || 0 : null;
872871

873872
if (collisionGroup) {
874873
collisionData = this.collisions.insert(

packages/display/src/displays/webgl/buffer/createBuffer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ const createBuffer = (
334334

335335
const gradient = (<unknown>shared.fill as LinearGradient)?.stops || DEFAULT_HEATMAP_GRADIENT.stops;
336336

337-
const gradientTexture = factory.gradients.getTexture(gradient, HeatmapBuffer.verifyAndFixGradient);
337+
const gradientTexture = factory.textureManager.getGradientTexture(gradient, HeatmapBuffer.verifyAndFixGradient);
338338
geoBuffer.addUniform('u_gradient', gradientTexture);
339339
}
340340

0 commit comments

Comments
 (0)