Skip to content

Commit

Permalink
switch shaders to ES modules, minify them (#7368)
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner authored Oct 5, 2018
1 parent 8e57c1d commit dc53cd3
Show file tree
Hide file tree
Showing 7 changed files with 1,894 additions and 302 deletions.
32 changes: 27 additions & 5 deletions build/rollup_plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import unassert from 'rollup-plugin-unassert';
import json from 'rollup-plugin-json';
import browserifyPlugin from 'rollup-plugin-browserify-transform';
import brfs from 'brfs';
import uglify from 'rollup-plugin-uglify';
import minifyStyleSpec from './rollup_plugin_minify_style_spec';
import { createFilter } from 'rollup-pluginutils';

const production = process.env.BUILD === 'production';

Expand All @@ -19,15 +18,13 @@ export const plugins = () => [
flow(),
minifyStyleSpec(),
json(),
glsl('./src/shaders/*.glsl', production),
buble({transforms: {dangerousForOf: true}, objectAssign: "Object.assign"}),
production ? unassert() : false,
resolve({
browser: true,
preferBuiltins: false
}),
browserifyPlugin(brfs, {
include: 'src/shaders/index.js'
}),
commonjs({
// global keyword handling causes Webpack compatibility issues, so we disabled it:
// https://github.com/mapbox/mapbox-gl-js/pull/6956
Expand All @@ -48,3 +45,28 @@ export function flow() {
};
}

// Using this instead of rollup-plugin-string to add minification
function glsl(include, minify) {
const filter = createFilter(include);
return {
name: 'glsl',
transform(code, id) {
if (!filter(id)) return;

// barebones GLSL minification
if (minify) {
code = code.trim() // strip whitespace at the start/end
.replace(/\s*\/\/[^\n]*\n/g, '\n') // strip double-slash comments
.replace(/\n+/g, '\n') // collapse multi line breaks
.replace(/\n\s+/g, '\n') // strip identation
.replace(/\s?([+-\/*=,])\s?/g, '$1') // strip whitespace around operators
.replace(/([;\(\),\{\}])\n(?=[^#])/g, '$1'); // strip more line breaks
}

return {
code: `export default ${JSON.stringify(code)};`,
map: {mappings: ''}
};
}
};
}
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
"@mapbox/unitbezier": "^0.0.0",
"@mapbox/vector-tile": "^1.3.1",
"@mapbox/whoots-js": "^3.1.0",
"brfs": "^1.4.4",
"csscolorparser": "~1.0.2",
"earcut": "^2.1.3",
"esm": "^3.0.84",
"geojson-rewind": "^0.3.0",
"geojson-vt": "^3.2.0",
"gl-matrix": "^2.6.1",
Expand Down Expand Up @@ -56,7 +56,6 @@
"eslint-plugin-html": "^3.0.0",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-react": "^7.3.0",
"esm": "^3.0.72",
"execcommand-copy": "^1.1.0",
"flow-bin": "^0.77.0",
"flow-coverage-report": "^0.3.0",
Expand Down Expand Up @@ -86,8 +85,7 @@
"remark-html": "^5.0.1",
"remark-react": "^4.0.1",
"request": "^2.79.0",
"rollup": "^0.63.4",
"rollup-plugin-browserify-transform": "^1.0.1",
"rollup": "^0.66.2",
"rollup-plugin-buble": "^0.18.0",
"rollup-plugin-commonjs": "^9.1.6",
"rollup-plugin-json": "^2.3.0",
Expand All @@ -104,6 +102,7 @@
"tap": "^11.1.2"
},
"browser": {
"./src/shaders/index.js": "./src/shaders/shaders.js",
"./src/util/window.js": "./src/util/browser/window.js",
"./src/util/web_worker.js": "./src/util/browser/web_worker.js"
},
Expand Down
2 changes: 1 addition & 1 deletion src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import rasterBoundsAttributes from '../data/raster_bounds_attributes';
import posAttributes from '../data/pos_attributes';
import ProgramConfiguration from '../data/program_configuration';
import CrossTileSymbolIndex from '../symbol/cross_tile_symbol_index';
import shaders from '../shaders';
import * as shaders from '../shaders';
import Program from './program';
import { programUniforms } from './program/program_uniforms';
import Context from '../gl/context';
Expand Down
6 changes: 3 additions & 3 deletions src/render/program.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import browser from '../util/browser';

import shaders from '../shaders';
import {prelude} from '../shaders';
import assert from 'assert';
import ProgramConfiguration from '../data/program_configuration';
import VertexArrayObject from './vertex_array_object';
Expand Down Expand Up @@ -43,8 +43,8 @@ class Program<Us: UniformBindings> {
defines.push('#define OVERDRAW_INSPECTOR;');
}

const fragmentSource = defines.concat(shaders.prelude.fragmentSource, source.fragmentSource).join('\n');
const vertexSource = defines.concat(shaders.prelude.vertexSource, source.vertexSource).join('\n');
const fragmentSource = defines.concat(prelude.fragmentSource, source.fragmentSource).join('\n');
const vertexSource = defines.concat(prelude.vertexSource, source.vertexSource).join('\n');
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
Expand Down
217 changes: 10 additions & 207 deletions src/shaders/index.js
Original file line number Diff line number Diff line change
@@ -1,213 +1,16 @@
// @flow

// We use brfs, a browserify transform, to inline shader sources during bundling. As a result:
// - readFileSync calls must be written out long-form
// - this module must use CommonJS rather than ES2015 syntax
/* eslint-disable prefer-template, no-path-concat, import/unambiguous, import/no-commonjs */
// Shaders entry point for Node (tests and GL Native)
/* eslint-disable import/unambiguous, import/no-commonjs, flowtype/require-valid-file-annotation, no-global-assign */

const fs = require('fs');

const shaders: {[string]: {fragmentSource: string, vertexSource: string}} = {
prelude: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/_prelude.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/_prelude.vertex.glsl', 'utf8')
},
background: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/background.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/background.vertex.glsl', 'utf8')
},
backgroundPattern: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/background_pattern.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/background_pattern.vertex.glsl', 'utf8')
},
circle: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/circle.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/circle.vertex.glsl', 'utf8')
},
clippingMask: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/clipping_mask.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/clipping_mask.vertex.glsl', 'utf8')
},
heatmap: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/heatmap.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/heatmap.vertex.glsl', 'utf8')
},
heatmapTexture: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/heatmap_texture.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/heatmap_texture.vertex.glsl', 'utf8')
},
collisionBox: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/collision_box.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/collision_box.vertex.glsl', 'utf8')
},
collisionCircle: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/collision_circle.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/collision_circle.vertex.glsl', 'utf8')
},
debug: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/debug.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/debug.vertex.glsl', 'utf8')
},
fill: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/fill.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/fill.vertex.glsl', 'utf8')
},
fillOutline: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/fill_outline.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/fill_outline.vertex.glsl', 'utf8')
},
fillOutlinePattern: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/fill_outline_pattern.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/fill_outline_pattern.vertex.glsl', 'utf8')
},
fillPattern: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/fill_pattern.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/fill_pattern.vertex.glsl', 'utf8')
},
fillExtrusion: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/fill_extrusion.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/fill_extrusion.vertex.glsl', 'utf8')
},
fillExtrusionPattern: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/fill_extrusion_pattern.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/fill_extrusion_pattern.vertex.glsl', 'utf8')
},
extrusionTexture: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/extrusion_texture.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/extrusion_texture.vertex.glsl', 'utf8')
},
hillshadePrepare: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/hillshade_prepare.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/hillshade_prepare.vertex.glsl', 'utf8')
},
hillshade: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/hillshade.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/hillshade.vertex.glsl', 'utf8')
},
line: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/line.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/line.vertex.glsl', 'utf8')
},
lineGradient: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/line_gradient.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/line_gradient.vertex.glsl', 'utf8')
},
linePattern: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/line_pattern.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/line_pattern.vertex.glsl', 'utf8')
},
lineSDF: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/line_sdf.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/line_sdf.vertex.glsl', 'utf8')
},
raster: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/raster.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/raster.vertex.glsl', 'utf8')
},
symbolIcon: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/symbol_icon.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/symbol_icon.vertex.glsl', 'utf8')
},
symbolSDF: {
fragmentSource: fs.readFileSync(__dirname + '/../shaders/symbol_sdf.fragment.glsl', 'utf8'),
vertexSource: fs.readFileSync(__dirname + '/../shaders/symbol_sdf.vertex.glsl', 'utf8')
}
};

// Expand #pragmas to #ifdefs.

const re = /#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g;

for (const programName in shaders) {
const program = shaders[programName];
const fragmentPragmas: {[string]: boolean} = {};
// enable ES Modules in Node
require = require("esm")(module);

program.fragmentSource = program.fragmentSource.replace(re, (match: string, operation: string, precision: string, type: string, name: string) => {
fragmentPragmas[name] = true;
if (operation === 'define') {
return `
#ifndef HAS_UNIFORM_u_${name}
varying ${precision} ${type} ${name};
#else
uniform ${precision} ${type} u_${name};
#endif
`;
} else /* if (operation === 'initialize') */ {
return `
#ifdef HAS_UNIFORM_u_${name}
${precision} ${type} ${name} = u_${name};
#endif
`;
}
});

program.vertexSource = program.vertexSource.replace(re, (match: string, operation: string, precision: string, type: string, name: string) => {
const attrType = type === 'float' ? 'vec2' : 'vec4';
const unpackType = name.match(/color/) ? 'color' : attrType;

if (fragmentPragmas[name]) {
if (operation === 'define') {
return `
#ifndef HAS_UNIFORM_u_${name}
uniform lowp float a_${name}_t;
attribute ${precision} ${attrType} a_${name};
varying ${precision} ${type} ${name};
#else
uniform ${precision} ${type} u_${name};
#endif
`;
} else /* if (operation === 'initialize') */ {
if (unpackType === 'vec4') {
// vec4 attributes are only used for cross-faded properties, and are not packed
return `
#ifndef HAS_UNIFORM_u_${name}
${name} = a_${name};
#else
${precision} ${type} ${name} = u_${name};
#endif
`;
} else {
return `
#ifndef HAS_UNIFORM_u_${name}
${name} = unpack_mix_${unpackType}(a_${name}, a_${name}_t);
#else
${precision} ${type} ${name} = u_${name};
#endif
`;
}
}
} else {
if (operation === 'define') {
return `
#ifndef HAS_UNIFORM_u_${name}
uniform lowp float a_${name}_t;
attribute ${precision} ${attrType} a_${name};
#else
uniform ${precision} ${type} u_${name};
#endif
`;
} else /* if (operation === 'initialize') */ {
if (unpackType === 'vec4') {
// vec4 attributes are only used for cross-faded properties, and are not packed
return `
#ifndef HAS_UNIFORM_u_${name}
${precision} ${type} ${name} = a_${name};
#else
${precision} ${type} ${name} = u_${name};
#endif
`;
} else /* */{
return `
#ifndef HAS_UNIFORM_u_${name}
${precision} ${type} ${name} = unpack_mix_${unpackType}(a_${name}, a_${name}_t);
#else
${precision} ${type} ${name} = u_${name};
#endif
`;
}
}
}
});
}
// enable requiring GLSL in Node
require.extensions['.glsl'] = function (module, filename) {
const content = fs.readFileSync(filename, 'utf8');
module._compile(`module.exports = \`${content}\``, filename);
};

module.exports = shaders;
module.exports = require("./shaders.js");
Loading

0 comments on commit dc53cd3

Please sign in to comment.