Avoid colliding user variables with internal names

This commit is contained in:
Tim Schaub
2021-02-10 18:16:16 -07:00
parent ebc8864680
commit a7684d8fbf
4 changed files with 45 additions and 18 deletions

View File

@@ -378,6 +378,15 @@ Operators['get'] = {
}, },
}; };
/**
* Get the uniform name given a variable name.
* @param {string} variableName The variable name.
* @return {string} The uniform name.
*/
export function uniformNameForVariable(variableName) {
return 'u_var_' + variableName;
}
Operators['var'] = { Operators['var'] = {
getReturnType: function (args) { getReturnType: function (args) {
return ValueTypes.ANY; return ValueTypes.ANY;
@@ -389,7 +398,7 @@ Operators['var'] = {
if (context.variables.indexOf(value) === -1) { if (context.variables.indexOf(value) === -1) {
context.variables.push(value); context.variables.push(value);
} }
return `u_${value}`; return uniformNameForVariable(value);
}, },
}; };

View File

@@ -7,6 +7,7 @@ import {
ValueTypes, ValueTypes,
expressionToGlsl, expressionToGlsl,
getStringNumberEquivalent, getStringNumberEquivalent,
uniformNameForVariable,
} from '../style/expressions.js'; } from '../style/expressions.js';
/** /**
@@ -527,8 +528,9 @@ export function parseLiteralStyle(style) {
// define one uniform per variable // define one uniform per variable
fragContext.variables.forEach(function (varName) { fragContext.variables.forEach(function (varName) {
builder.addUniform(`float u_${varName}`); const uniformName = uniformNameForVariable(varName);
uniforms[`u_${varName}`] = function () { builder.addUniform(`float ${uniformName}`);
uniforms[uniformName] = function () {
if (!style.variables || style.variables[varName] === undefined) { if (!style.variables || style.variables[varName] === undefined) {
throw new Error( throw new Error(
`The following variable is missing from the style: ${varName}` `The following variable is missing from the style: ${varName}`

View File

@@ -7,6 +7,7 @@ import {
isTypeUnique, isTypeUnique,
numberToGlsl, numberToGlsl,
stringToGlsl, stringToGlsl,
uniformNameForVariable,
} from '../../../../src/ol/style/expressions.js'; } from '../../../../src/ol/style/expressions.js';
describe('ol.style.expressions', function () { describe('ol.style.expressions', function () {
@@ -211,7 +212,9 @@ describe('ol.style.expressions', function () {
it('correctly converts expressions to GLSL', function () { it('correctly converts expressions to GLSL', function () {
expect(expressionToGlsl(context, ['get', 'myAttr'])).to.eql('a_myAttr'); expect(expressionToGlsl(context, ['get', 'myAttr'])).to.eql('a_myAttr');
expect(expressionToGlsl(context, ['var', 'myValue'])).to.eql('u_myValue'); expect(expressionToGlsl(context, ['var', 'myValue'])).to.eql(
uniformNameForVariable('myValue')
);
expect(expressionToGlsl(context, ['time'])).to.eql('u_time'); expect(expressionToGlsl(context, ['time'])).to.eql('u_time');
expect(expressionToGlsl(context, ['zoom'])).to.eql('u_zoom'); expect(expressionToGlsl(context, ['zoom'])).to.eql('u_zoom');
expect(expressionToGlsl(context, ['resolution'])).to.eql('u_resolution'); expect(expressionToGlsl(context, ['resolution'])).to.eql('u_resolution');

View File

@@ -6,6 +6,7 @@ import {
arrayToGlsl, arrayToGlsl,
colorToGlsl, colorToGlsl,
numberToGlsl, numberToGlsl,
uniformNameForVariable,
} from '../../../../src/ol/style/expressions.js'; } from '../../../../src/ol/style/expressions.js';
describe('ol.webgl.ShaderBuilder', function () { describe('ol.webgl.ShaderBuilder', function () {
@@ -477,9 +478,11 @@ void main(void) {
}, },
}); });
const lowerUniformName = uniformNameForVariable('lower');
const higherUniformName = uniformNameForVariable('higher');
expect(result.builder.uniforms).to.eql([ expect(result.builder.uniforms).to.eql([
'float u_lower', `float ${lowerUniformName}`,
'float u_higher', `float ${higherUniformName}`,
]); ]);
expect(result.builder.attributes).to.eql(['float a_population']); expect(result.builder.attributes).to.eql(['float a_population']);
expect(result.builder.varyings).to.eql([ expect(result.builder.varyings).to.eql([
@@ -493,7 +496,7 @@ void main(void) {
'vec4(vec4(0.2, 0.4, 0.6, 1.0).rgb, vec4(0.2, 0.4, 0.6, 1.0).a * 0.5 * 1.0)' 'vec4(vec4(0.2, 0.4, 0.6, 1.0).rgb, vec4(0.2, 0.4, 0.6, 1.0).a * 0.5 * 1.0)'
); );
expect(result.builder.sizeExpression).to.eql( expect(result.builder.sizeExpression).to.eql(
'vec2(mix(4.0, 8.0, pow(clamp((a_population - u_lower) / (u_higher - u_lower), 0.0, 1.0), 1.0)))' `vec2(mix(4.0, 8.0, pow(clamp((a_population - ${lowerUniformName}) / (${higherUniformName} - ${lowerUniformName}), 0.0, 1.0), 1.0)))`
); );
expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)'); expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)');
expect(result.builder.texCoordExpression).to.eql( expect(result.builder.texCoordExpression).to.eql(
@@ -502,8 +505,8 @@ void main(void) {
expect(result.builder.rotateWithView).to.eql(false); expect(result.builder.rotateWithView).to.eql(false);
expect(result.attributes.length).to.eql(1); expect(result.attributes.length).to.eql(1);
expect(result.attributes[0].name).to.eql('population'); expect(result.attributes[0].name).to.eql('population');
expect(result.uniforms).to.have.property('u_lower'); expect(result.uniforms).to.have.property(lowerUniformName);
expect(result.uniforms).to.have.property('u_higher'); expect(result.uniforms).to.have.property(higherUniformName);
}); });
it('parses a style with a filter', function () { it('parses a style with a filter', function () {
@@ -541,6 +544,8 @@ void main(void) {
}); });
it('parses a style with a color interpolation', function () { it('parses a style with a color interpolation', function () {
const varName = 'ratio';
const uniformName = uniformNameForVariable(varName);
const result = parseLiteralStyle({ const result = parseLiteralStyle({
symbol: { symbol: {
symbolType: 'square', symbolType: 'square',
@@ -548,7 +553,7 @@ void main(void) {
color: [ color: [
'interpolate', 'interpolate',
['linear'], ['linear'],
['var', 'ratio'], ['var', varName],
0, 0,
[255, 255, 0], [255, 255, 0],
1, 1,
@@ -560,7 +565,7 @@ void main(void) {
expect(result.builder.attributes).to.eql([]); expect(result.builder.attributes).to.eql([]);
expect(result.builder.varyings).to.eql([]); expect(result.builder.varyings).to.eql([]);
expect(result.builder.colorExpression).to.eql( expect(result.builder.colorExpression).to.eql(
'vec4(mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), pow(clamp((u_ratio - 0.0) / (1.0 - 0.0), 0.0, 1.0), 1.0)).rgb, mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), pow(clamp((u_ratio - 0.0) / (1.0 - 0.0), 0.0, 1.0), 1.0)).a * 1.0 * 1.0)' `vec4(mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), pow(clamp((${uniformName} - 0.0) / (1.0 - 0.0), 0.0, 1.0), 1.0)).rgb, mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), pow(clamp((${uniformName} - 0.0) / (1.0 - 0.0), 0.0, 1.0), 1.0)).a * 1.0 * 1.0)`
); );
expect(result.builder.sizeExpression).to.eql('vec2(6.0)'); expect(result.builder.sizeExpression).to.eql('vec2(6.0)');
expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)'); expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)');
@@ -569,7 +574,7 @@ void main(void) {
); );
expect(result.builder.rotateWithView).to.eql(false); expect(result.builder.rotateWithView).to.eql(false);
expect(result.attributes).to.eql([]); expect(result.attributes).to.eql([]);
expect(result.uniforms).to.have.property('u_ratio'); expect(result.uniforms).to.have.property(uniformName);
}); });
it('parses a style with a rotation expression using an attribute', function () { it('parses a style with a rotation expression using an attribute', function () {
@@ -587,32 +592,38 @@ void main(void) {
}); });
it('correctly adds string variables to the string literals mapping', function () { it('correctly adds string variables to the string literals mapping', function () {
const varName = 'mySize';
const uniformName = uniformNameForVariable(varName);
const result = parseLiteralStyle({ const result = parseLiteralStyle({
variables: { variables: {
mySize: 'abcdef', mySize: 'abcdef',
}, },
symbol: { symbol: {
symbolType: 'square', symbolType: 'square',
size: ['match', ['var', 'mySize'], 'abc', 10, 'def', 20, 30], size: ['match', ['var', varName], 'abc', 10, 'def', 20, 30],
color: 'red', color: 'red',
}, },
}); });
expect(result.uniforms['u_mySize']()).to.be.greaterThan(0); expect(result.uniforms[uniformName]()).to.be.greaterThan(0);
}); });
it('throws when a variable is requested but not present in the style', function (done) { it('throws when a variable is requested but not present in the style', function (done) {
const varName = 'mySize';
const uniformName = uniformNameForVariable(varName);
const result = parseLiteralStyle({ const result = parseLiteralStyle({
variables: {}, variables: {},
symbol: { symbol: {
symbolType: 'square', symbolType: 'square',
size: ['var', 'mySize'], size: ['var', varName],
color: 'red', color: 'red',
}, },
}); });
try { try {
result.uniforms['u_mySize'](); result.uniforms[uniformName]();
} catch (e) { } catch (e) {
done(); done();
} }
@@ -620,16 +631,18 @@ void main(void) {
}); });
it('throws when a variable is requested but the style does not have a variables dict', function (done) { it('throws when a variable is requested but the style does not have a variables dict', function (done) {
const variableName = 'mySize';
const uniformName = uniformNameForVariable(variableName);
const result = parseLiteralStyle({ const result = parseLiteralStyle({
symbol: { symbol: {
symbolType: 'square', symbolType: 'square',
size: ['var', 'mySize'], size: ['var', variableName],
color: 'red', color: 'red',
}, },
}); });
try { try {
result.uniforms['u_mySize'](); result.uniforms[uniformName]();
} catch (e) { } catch (e) {
done(); done();
} }