Skip to content

Commit

Permalink
Merge pull request #564 from mapbox/texturerendermerge
Browse files Browse the repository at this point in the history
raster prerendering
  • Loading branch information
ansis committed Jul 22, 2014
2 parents 07325fb + 68fdbea commit a632597
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 33 deletions.
4 changes: 3 additions & 1 deletion js/geometry/createbucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = createBucket;
var LineBucket = require('./linebucket.js');
var FillBucket = require('./fillbucket.js');
var SymbolBucket = require('./symbolbucket.js');
var RasterBucket = require('./rasterbucket.js');
var RenderProperties = require('../style/renderproperties.js');

function createBucket(layer, buffers, collision, indices) {
Expand All @@ -19,7 +20,8 @@ function createBucket(layer, buffers, collision, indices) {
var BucketClass =
layer.type === 'line' ? LineBucket :
layer.type === 'fill' ? FillBucket :
layer.type === 'symbol' ? SymbolBucket : null;
layer.type === 'symbol' ? SymbolBucket :
layer.type === 'raster' ? RasterBucket : null;

var bucket = new BucketClass(info, buffers, collision, indices);
bucket.type = layer.type;
Expand Down
7 changes: 7 additions & 0 deletions js/geometry/rasterbucket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = RasterBucket;

function RasterBucket(info) {
this.info = info;
}
2 changes: 1 addition & 1 deletion js/render/drawbackground.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function drawFill(gl, painter, bucket, layerStyle, posMatrix, params, imageSprit
} else {
// Draw filling rectangle.
shader = painter.fillShader;
gl.switchShader(shader, posMatrix);
gl.switchShader(shader, params.padded || posMatrix);
gl.uniform4fv(shader.u_color, color || [1.0, 0, 0, 1]);
}

Expand Down
63 changes: 56 additions & 7 deletions js/render/drawraster.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,51 @@
'use strict';

var Tile = require('../ui/tile.js');
var PrerenderedTexture = require('./prerendered.js');
var mat4 = require('../lib/glmatrix.js').mat4;

module.exports = drawRaster;

function drawRaster(gl, painter, bucket, layerStyle) {
function drawRaster(gl, painter, bucket, layerStyle, params, style, layer, tile) {

if (layer && layer.layers) {

if (!bucket.prerendered) {
bucket.prerendered = new PrerenderedTexture(gl, bucket.info);
bucket.prerendered.bindFramebuffer();

gl.clearStencil(0x80);
gl.stencilMask(0xFF);
gl.clear(gl.STENCIL_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.stencilMask(0x00);

gl.viewport(0, 0, bucket.prerendered.size, bucket.prerendered.size);

var buffer = bucket.prerendered.buffer * 4096;

var matrix = mat4.create();
mat4.ortho(matrix, -buffer, 4096 + buffer, -4096 - buffer, buffer, 0, 1);
mat4.translate(matrix, matrix, [0, -4096, 0]);

params.padded = mat4.create();
mat4.ortho(params.padded, 0, 4096, -4096, 0, 0, 1);
mat4.translate(params.padded, params.padded, [0, -4096, 0]);

painter.draw(tile, style, layer.layers, params, matrix);

var tile = bucket.tile;
delete params.padded;

if (bucket.info['raster-blur'] > 0) {
bucket.prerendered.blur(painter, bucket.info['raster-blur']);
}

bucket.prerendered.unbindFramebuffer();
gl.viewport(0, 0, painter.width, painter.height);
}

}

var texture = bucket.tile ? bucket.tile : bucket.prerendered;

gl.disable(gl.STENCIL_TEST);

Expand All @@ -20,34 +60,43 @@ function drawRaster(gl, painter, bucket, layerStyle) {
gl.uniform3fv(shader.u_spin_weights, spinWeights(layerStyle['raster-spin']));


var parentTile = findParent(tile);
var opacities = getOpacities(tile, parentTile);
var parentTile, opacities;
if (layer && layer.layers) {
parentTile = null;
opacities = [layerStyle['raster-opacity'], 0];
} else {
parentTile = findParent(texture);
opacities = getOpacities(texture, parentTile);
}
var parentScaleBy, parentTL;

gl.activeTexture(gl.TEXTURE0);
tile.bind(gl);
texture.bind(gl);

if (parentTile) {
gl.activeTexture(gl.TEXTURE1);
parentTile.bind(gl);

var tilePos = Tile.fromID(tile.id);
var tilePos = Tile.fromID(texture.id);
var parentPos = parentTile && Tile.fromID(parentTile.id);
parentScaleBy = Math.pow(2, parentPos.z - tilePos.z);
parentTL = [tilePos.x * parentScaleBy % 1, tilePos.y * parentScaleBy % 1];
} else {
opacities[1] = 0;
}

var bufferScale = bucket.prerendered ? (4096 * (1 + 2 * bucket.prerendered.buffer)) / 4096 : 1;

// cross-fade parameters
gl.uniform2fv(shader.u_tl_parent, parentTL || [0, 0]);
gl.uniform1f(shader.u_scale_parent, parentScaleBy || 1);
gl.uniform1f(shader.u_buffer_scale, bufferScale);
gl.uniform1f(shader.u_opacity0, opacities[0]);
gl.uniform1f(shader.u_opacity1, opacities[1]);
gl.uniform1i(shader.u_image0, 0);
gl.uniform1i(shader.u_image1, 1);

gl.bindBuffer(gl.ARRAY_BUFFER, tile.boundsBuffer || painter.tileExtentBuffer);
gl.bindBuffer(gl.ARRAY_BUFFER, texture.boundsBuffer || painter.tileExtentBuffer);

gl.vertexAttribPointer(
shader.a_pos,
Expand Down
22 changes: 15 additions & 7 deletions js/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ GLPainter.prototype.setup = function() {
['a_pos'],
['u_posmatrix', 'u_opacity']);

this.gaussianShader = gl.initializeShader('gaussian',
['a_pos'],
['u_posmatrix', 'u_opacity', 'u_image', 'u_offset']);

this.rasterShader = gl.initializeShader('raster',
['a_pos', 'a_texture_pos'],
['u_posmatrix', 'u_brightness_low', 'u_brightness_high', 'u_saturation_factor', 'u_spin_weights', 'u_contrast_factor', 'u_opacity0', 'u_opacity1', 'u_image0', 'u_image1', 'u_tl_parent', 'u_scale_parent']);
['u_posmatrix', 'u_brightness_low', 'u_brightness_high', 'u_saturation_factor', 'u_spin_weights', 'u_contrast_factor', 'u_opacity0', 'u_opacity1', 'u_image0', 'u_image1', 'u_tl_parent', 'u_scale_parent', 'u_buffer_scale']);

this.lineShader = gl.initializeShader('line',
['a_pos', 'a_extrude', 'a_linesofar'],
Expand Down Expand Up @@ -274,11 +278,11 @@ GLPainter.prototype.freeRenderTexture = function(name) {
* Draw a new tile to the context, assuming that the viewport is
* already correctly set.
*/
GLPainter.prototype.draw = function glPainterDraw(tile, style, layers, params) {
GLPainter.prototype.draw = function glPainterDraw(tile, style, layers, params, matrix) {
this.tile = tile;

// false when drawing a group of composited layers
if (tile) {
if (tile && !matrix) {
// Draw the root clipping mask.
this.drawClippingMask();
}
Expand All @@ -290,22 +294,25 @@ GLPainter.prototype.draw = function glPainterDraw(tile, style, layers, params) {
// Draw layers front-to-back.
// Layers are already in reverse order from style.restructure()
for (var i = 0, len = layers.length; i < len; i++) {
this.applyStyle(layers[i], style, tile && tile.buckets, params);
this.applyStyle(layers[i], style, tile && tile.buckets, params, tile, matrix);
}

if (params.debug) {
drawDebug(this.gl, this, tile, params);
}
};

GLPainter.prototype.applyStyle = function(layer, style, buckets, params) {
GLPainter.prototype.applyStyle = function(layer, style, buckets, params, tile, matrix) {
var gl = this.gl;

var layerStyle = style.computed[layer.id];
if (!layerStyle || layerStyle.hidden) return;

if (layer.layers) {
drawComposited(gl, this, buckets, layerStyle, params, style, layer);
if (layer.type === 'composite') drawComposited(gl, this, buckets, layerStyle, params, style, layer);
else if (layer.type === 'raster') {
drawRaster(gl, this, buckets[layer.bucket], layerStyle, params, style, layer, tile);
}
} else if (params.background) {
drawBackground(gl, this, undefined, layerStyle, this.identityMatrix, params, style.sprite);
} else {
Expand All @@ -325,7 +332,8 @@ GLPainter.prototype.applyStyle = function(layer, style, buckets, params) {
type === 'raster' ? drawRaster : null;

if (draw) {
draw(gl, this, bucket, layerStyle, this.tile.posMatrix, params, style.sprite);
var useMatrix = matrix || this.tile.posMatrix;
draw(gl, this, bucket, layerStyle, useMatrix, params, style.sprite);
} else {
console.warn('No bucket type specified');
}
Expand Down
101 changes: 101 additions & 0 deletions js/render/prerendered.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use strict';

var glmatrix = require('../lib/glmatrix.js');
var mat4 = glmatrix.mat4;

module.exports = PrerenderedTexture;

function PrerenderedTexture(gl, bucket) {
this.gl = gl;
this.buffer = bucket['raster-buffer'] || (1/32);
this.size = (bucket['raster-size'] || 512) * (1 + 2 * this.buffer);

this.texture = null;
this.fbo = null;
this.fboPrevious = null;
}

PrerenderedTexture.prototype.bindFramebuffer = function() {
var gl = this.gl;
// TODO get previous fbo

if (!this.texture) {
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.size, this.size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
}

if (!this.fbo) {
var stencil = this.stencilBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, stencil);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, this.size, this.size);

this.fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.stencilBuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);

}
};

PrerenderedTexture.prototype.unbindFramebuffer = function() {
var gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fboPrevious);
gl.deleteFramebuffer(this.fbo);
};

PrerenderedTexture.prototype.bind = function() {
if (!this.texture) throw('pre-rendered texture does not exist');
var gl = this.gl;
gl.bindTexture(gl.TEXTURE_2D, this.texture);
};

PrerenderedTexture.prototype.blur = function(painter, passes) {
var gl = this.gl;
var originalTexture = this.texture;
var secondaryTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, secondaryTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.size, this.size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.bindTexture(gl.TEXTURE_2D, null);

var matrix = mat4.create();
mat4.ortho(matrix, 0, 4096, -4096, 0, 0, 1);
mat4.translate(matrix, matrix, [0, -4096, 0]);

gl.switchShader(painter.gaussianShader, matrix);
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(painter.gaussianShader.u_image, 0);
gl.uniform1f(painter.gaussianShader.u_opacity, 1);

for (var i = 0; i < passes; i++) {

// Render horizontal
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, secondaryTexture, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform2fv(painter.gaussianShader.u_offset, [1 / this.size, 0]);
gl.bindTexture(gl.TEXTURE_2D, originalTexture);
gl.bindBuffer(gl.ARRAY_BUFFER, painter.tileExtentBuffer);
gl.vertexAttribPointer(painter.gaussianShader.a_pos, 2, gl.SHORT, false, 8, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);


// Render vertical
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, originalTexture, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform2fv(painter.gaussianShader.u_offset, [0, 1 / this.size]);
gl.bindTexture(gl.TEXTURE_2D, secondaryTexture);
gl.bindBuffer(gl.ARRAY_BUFFER, painter.tileExtentBuffer);
gl.vertexAttribPointer(painter.gaussianShader.a_pos, 2, gl.SHORT, false, 8, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

gl.deleteTexture(secondaryTexture);
};
1 change: 1 addition & 0 deletions js/render/shaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
"debug": glify('../../shaders/debug.*.glsl'),
"dot": glify('../../shaders/dot.*.glsl'),
"fill": glify('../../shaders/fill.*.glsl'),
"gaussian": glify('../../shaders/gaussian.*.glsl'),
"line": glify('../../shaders/line.*.glsl'),
"linepattern": glify('../../shaders/linepattern.*.glsl'),
"outline": glify('../../shaders/outline.*.glsl'),
Expand Down
7 changes: 4 additions & 3 deletions js/style/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Style.prototype.recalculate = function(z) {
var simple = {};
simple.id = layer.id;
if (bucket) simple.bucket = bucket.id;
if (layer.type) simple.type = layer.type;
if (layer.layers) simple.layers = layer.layers.map(simpleLayer);
return simple;
}
Expand Down Expand Up @@ -137,7 +138,7 @@ Style.prototype.recalculate = function(z) {
var style = layerValues[layer.id];
if (!style || style.hidden) continue;

if (layer.layers) {
if (layer.layers && layer.type == 'composite') {
// TODO if composited layer is opaque just inline the layers
group.dependencies[layer.id] = groupLayers(layer.layers);
} else {
Expand Down Expand Up @@ -182,8 +183,8 @@ Style.prototype.cascade = function(options) {
var layer = layers[a];
if (layer.layers) {
buckets = getbuckets(buckets, ordered, layer.layers);
continue;
} else if (!layer.source || !layer.type) {
}
if (!layer.source || !layer.type) {
continue;
}
var bucket = { id: layer.id };
Expand Down
3 changes: 1 addition & 2 deletions js/worker/workertile.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ WorkerTile.prototype.parse = function(data, actor, callback) {

if (!skip) {
var now = Date.now();
bucket.addFeatures();
if (bucket.type !== 'raster') bucket.addFeatures();
var time = Date.now() - now;
if (bucket.interactive) {
for (var i = 0; i < bucket.features.length; i++) {
Expand Down Expand Up @@ -265,4 +265,3 @@ function getGeometry(feature) {
function getType(feature) {
return vt.VectorTileFeature.types[feature.type];
}

13 changes: 13 additions & 0 deletions shaders/gaussian.fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
uniform sampler2D u_image;
uniform float u_opacity;

varying vec2 v_pos;
varying vec2 v_coords[3];

void main() {
vec4 sum = vec4(0.0);
sum += texture2D(u_image, v_coords[0]) * 0.40261994689424746;
sum += texture2D(u_image, v_coords[1]) * 0.2986900265528763;
sum += texture2D(u_image, v_coords[2]) * 0.2986900265528763;
gl_FragColor = sum;
}
15 changes: 15 additions & 0 deletions shaders/gaussian.vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
attribute vec2 a_pos;

uniform mat4 u_posmatrix;
uniform vec2 u_offset;
varying vec2 v_pos;
varying vec2 v_coords[3];

void main() {
gl_Position = u_posmatrix * vec4(a_pos, 0, 1);
vec2 tex = gl_Position.xy / 2.0 + 0.5;

v_coords[0] = tex;
v_coords[1] = tex + u_offset * 1.1824255238063563;
v_coords[2] = tex - u_offset * 1.1824255238063563;
}
Loading

0 comments on commit a632597

Please sign in to comment.