Skip to content

Commit 655bd0b

Browse files
committed
Replaced the injected standalone with a proper UBO-backed shader module
Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
1 parent c72c558 commit 655bd0b

4 files changed

Lines changed: 113 additions & 104 deletions

File tree

src/deckgl-layers/src/line-layer/line-layer.ts

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ import {editShader} from '../';
88

99
type RGBAColor = Color;
1010

11+
const elevationScaleUniforms = {
12+
name: 'elevationScale',
13+
vs: `uniform elevationScaleUniforms {
14+
float elevationScale;
15+
} elevationScaleProps;
16+
`,
17+
uniformTypes: {
18+
elevationScale: 'f32'
19+
}
20+
};
21+
1122
const defaultProps = {
1223
...LineLayer.defaultProps,
1324
getTargetColor: x => x.color || [0, 0, 0, 255]
@@ -31,29 +42,20 @@ function addInstanceColorShader(vs) {
3142
}
3243

3344
function addElevationScale(vs) {
34-
let elevationVs = editShader(
45+
return editShader(
3546
vs,
36-
'line elevation scale 1 vs - inject elevation scale',
37-
'out vec2 uv;',
38-
`out vec2 uv;\nuniform float elevationScale;`
39-
);
40-
41-
elevationVs = editShader(
42-
elevationVs,
43-
'line elevation scale 2 vs - multiply by elevation scale',
47+
'line elevation scale vs - multiply by elevation scale',
4448
`geometry.worldPosition = instanceSourcePositions;
4549
geometry.worldPositionAlt = instanceTargetPositions;
4650
vec3 source_world = instanceSourcePositions;
4751
vec3 target_world = instanceTargetPositions;`,
4852
`vec3 source_world = instanceSourcePositions;
4953
vec3 target_world = instanceTargetPositions;
50-
source_world.z *= elevationScale;
51-
target_world.z *= elevationScale;
54+
source_world.z *= elevationScaleProps.elevationScale;
55+
target_world.z *= elevationScaleProps.elevationScale;
5256
geometry.worldPosition = source_world;
5357
geometry.worldPositionAlt = target_world;`
5458
);
55-
56-
return elevationVs;
5759
}
5860

5961
export default class EnhancedLineLayer extends LineLayer<
@@ -68,25 +70,19 @@ export default class EnhancedLineLayer extends LineLayer<
6870

6971
return {
7072
...shaders,
71-
vs
73+
vs,
74+
modules: [...(shaders.modules || []), elevationScaleUniforms]
7275
};
7376
}
7477

7578
draw(opts) {
76-
const {elevationScale} = this.props;
7779
const model = this.state.model;
78-
if (model && elevationScale !== undefined) {
79-
const gl = this.context.device?.gl || this.context.gl;
80-
if (gl) {
81-
const program = model.pipeline?.handle || model.handle;
82-
if (program) {
83-
gl.useProgram(program);
84-
const loc = gl.getUniformLocation(program, 'elevationScale');
85-
if (loc) {
86-
gl.uniform1f(loc, elevationScale);
87-
}
80+
if (model) {
81+
model.shaderInputs.setProps({
82+
elevationScale: {
83+
elevationScale: this.props.elevationScale ?? 1
8884
}
89-
}
85+
});
9086
}
9187
super.draw(opts);
9288
}

src/deckgl-layers/src/raster/raster-layer/raster-layer.ts

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {loadImages} from '../images';
1717
import type {RasterLayerAddedProps, ImageState} from '../types';
1818
import {modulesEqual} from '../util';
1919
import {patchPipelineValidation} from '../pipeline-validation-patch';
20+
import {setStandaloneUniforms, collectIntUniforms} from '../standalone-uniforms';
2021

2122
patchPipelineValidation();
2223

@@ -65,14 +66,13 @@ export default class RasterLayer extends BitmapLayer<RasterLayerAddedProps> {
6566
});
6667

6768
// Set props for each custom module through shaderInputs.
68-
// This routes textures to bindings and scalar values to module uniforms.
69+
// This routes textures to bindings and scalar values to standalone uniforms.
6970
const allModuleProps = {...moduleProps, ...images};
7071
const modules = this.props.modules || [];
7172
for (const mod of modules) {
7273
if (mod.getUniforms) {
7374
const uniforms = mod.getUniforms(allModuleProps);
7475
if (uniforms) {
75-
// Route texture bindings through shaderInputs
7676
const textureBindings: Record<string, any> = {};
7777
const scalarUniforms: Record<string, any> = {};
7878
for (const [key, value] of Object.entries(uniforms)) {
@@ -83,36 +83,12 @@ export default class RasterLayer extends BitmapLayer<RasterLayerAddedProps> {
8383
}
8484
}
8585

86-
// Set texture bindings through model.setBindings so they go through the pipeline
8786
if (Object.keys(textureBindings).length > 0) {
8887
model.setBindings(textureBindings);
8988
}
9089

91-
// Set standalone scalar uniforms via raw WebGL
92-
// (luma.gl 9 doesn't support standalone uniforms outside of UBOs)
9390
if (Object.keys(scalarUniforms).length > 0) {
94-
const gl = model.device?.gl;
95-
const program = model.pipeline?.handle;
96-
if (gl && program) {
97-
gl.useProgram(program);
98-
for (const [name, value] of Object.entries(scalarUniforms)) {
99-
const loc = gl.getUniformLocation(program, name);
100-
if (loc !== null) {
101-
if (typeof value === 'number') {
102-
if (Number.isInteger(value) && this._isIntUniform(mod, name)) {
103-
gl.uniform1i(loc, value);
104-
} else {
105-
gl.uniform1f(loc, value);
106-
}
107-
} else if (Array.isArray(value)) {
108-
if (value.length === 2) gl.uniform2fv(loc, value);
109-
else if (value.length === 3) gl.uniform3fv(loc, value);
110-
else if (value.length === 4) gl.uniform4fv(loc, value);
111-
else if (value.length === 16) gl.uniformMatrix4fv(loc, false, value);
112-
}
113-
}
114-
}
115-
}
91+
setStandaloneUniforms(model, scalarUniforms, collectIntUniforms(mod));
11692
}
11793
}
11894
}
@@ -140,15 +116,6 @@ export default class RasterLayer extends BitmapLayer<RasterLayerAddedProps> {
140116
});
141117
}
142118

143-
/**
144-
* Check if a uniform is declared as int in the module's shader source.
145-
*/
146-
_isIntUniform(mod: any, name: string): boolean {
147-
const fs = mod.fs2 || mod.fs || '';
148-
const regex = new RegExp(`uniform\\s+int\\s+${name}\\b`);
149-
return regex.test(fs);
150-
}
151-
152119
getShaders(): any {
153120
const {modules = []} = this.props;
154121

src/deckgl-layers/src/raster/raster-mesh-layer/raster-mesh-layer.ts

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {loadImages} from '../images';
2020
import type {RasterLayerAddedProps, ImageState} from '../types';
2121
import {modulesEqual} from '../util';
2222
import {patchPipelineValidation} from '../pipeline-validation-patch';
23+
import {setStandaloneUniforms, collectIntUniforms} from '../standalone-uniforms';
2324

2425
patchPipelineValidation();
2526

@@ -180,47 +181,21 @@ export default class RasterMeshLayer extends SimpleMeshLayer<any, RasterLayerAdd
180181
}
181182

182183
if (Object.keys(scalarUniforms).length > 0) {
183-
const gl = model.device?.gl;
184-
const program = model.pipeline?.handle;
185-
if (gl && program) {
186-
gl.useProgram(program);
187-
for (const [name, value] of Object.entries(scalarUniforms)) {
188-
const loc = gl.getUniformLocation(program, name);
189-
if (loc !== null) {
190-
if (typeof value === 'number') {
191-
if (Number.isInteger(value) && this._isIntUniform(mod, name)) {
192-
gl.uniform1i(loc, value);
193-
} else {
194-
gl.uniform1f(loc, value);
195-
}
196-
} else if (Array.isArray(value)) {
197-
if (value.length === 2) gl.uniform2fv(loc, value);
198-
else if (value.length === 3) gl.uniform3fv(loc, value);
199-
else if (value.length === 4) gl.uniform4fv(loc, value);
200-
else if (value.length === 16) gl.uniformMatrix4fv(loc, false, value);
201-
}
202-
}
203-
}
204-
}
184+
setStandaloneUniforms(model, scalarUniforms, collectIntUniforms(mod));
205185
}
206186
}
207187
}
208188
}
209189

210-
// Set mesh-specific uniforms via raw WebGL
211-
const gl = model.device?.gl;
212-
const program = model.pipeline?.handle;
213-
if (gl && program) {
214-
gl.useProgram(program);
215-
const opacityLoc = gl.getUniformLocation(program, 'meshOpacity');
216-
if (opacityLoc !== null) {
217-
gl.uniform1f(opacityLoc, this.props.opacity ?? 1);
218-
}
219-
const flatLoc = gl.getUniformLocation(program, 'meshFlatShading');
220-
if (flatLoc !== null) {
221-
gl.uniform1i(flatLoc, !this.state.hasNormals ? 1 : 0);
222-
}
223-
}
190+
// Set mesh-specific standalone uniforms
191+
setStandaloneUniforms(
192+
model,
193+
{
194+
meshOpacity: this.props.opacity ?? 1,
195+
meshFlatShading: !this.state.hasNormals ? 1 : 0
196+
},
197+
new Set(['meshFlatShading'])
198+
);
224199

225200
const drawSuccess = model.draw(this.context.renderPass);
226201
if (!drawSuccess) {
@@ -243,12 +218,6 @@ export default class RasterMeshLayer extends SimpleMeshLayer<any, RasterLayerAdd
243218
});
244219
}
245220

246-
_isIntUniform(mod: any, name: string): boolean {
247-
const fs = mod.fs2 || mod.fs || '';
248-
const regex = new RegExp(`uniform\\s+int\\s+${name}\\b`);
249-
return regex.test(fs);
250-
}
251-
252221
finalizeState(): void {
253222
super.finalizeState(this.context);
254223

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright contributors to the kepler.gl project
3+
4+
// @ts-nocheck
5+
6+
/**
7+
* Set standalone (non-UBO) uniforms on a luma.gl 9 Model's pipeline.
8+
*
9+
* luma.gl 9 only manages UBO-backed uniforms through shaderInputs. Legacy raster
10+
* shader modules declare standalone uniforms (e.g. `uniform int uHasCategoricalColors`)
11+
* that are not part of any uniform block. This helper sets those uniforms via the
12+
* WebGL2 API using the pipeline's own program handle, avoiding the need to call
13+
* gl.useProgram() with an externally obtained handle.
14+
*
15+
* Must be called during draw(), just before model.draw(). The pipeline's internal
16+
* gl.useProgram() in its draw() call will use the same program handle, so standalone
17+
* uniforms we set here persist correctly.
18+
*/
19+
export function setStandaloneUniforms(
20+
model: any,
21+
uniforms: Record<string, number | number[]>,
22+
intUniforms?: Set<string>
23+
): void {
24+
if (!uniforms || Object.keys(uniforms).length === 0) return;
25+
26+
const gl: WebGL2RenderingContext | undefined = model.device?.gl;
27+
const program: WebGLProgram | undefined = model.pipeline?.handle;
28+
if (!gl || !program) return;
29+
30+
gl.useProgram(program);
31+
32+
for (const [name, value] of Object.entries(uniforms)) {
33+
const loc = gl.getUniformLocation(program, name);
34+
if (loc === null) continue;
35+
36+
if (typeof value === 'number') {
37+
if (intUniforms?.has(name)) {
38+
gl.uniform1i(loc, value);
39+
} else {
40+
gl.uniform1f(loc, value);
41+
}
42+
} else if (Array.isArray(value)) {
43+
switch (value.length) {
44+
case 2:
45+
gl.uniform2fv(loc, value);
46+
break;
47+
case 3:
48+
gl.uniform3fv(loc, value);
49+
break;
50+
case 4:
51+
gl.uniform4fv(loc, value);
52+
break;
53+
case 16:
54+
gl.uniformMatrix4fv(loc, false, value);
55+
break;
56+
default:
57+
break;
58+
}
59+
}
60+
}
61+
}
62+
63+
/**
64+
* Collect the names of `int`-typed uniforms declared in a shader module's source.
65+
*/
66+
export function collectIntUniforms(mod: any): Set<string> {
67+
const result = new Set<string>();
68+
const sources = [mod.fs2, mod.fs, mod.vs].filter(Boolean);
69+
for (const src of sources) {
70+
const re = /uniform\s+(int|ivec[234]|uint|uvec[234])\s+(\w+)/g;
71+
let m;
72+
while ((m = re.exec(src)) !== null) {
73+
result.add(m[2]);
74+
}
75+
}
76+
return result;
77+
}

0 commit comments

Comments
 (0)