Skip to content

Commit

Permalink
Optimize extrusion rendering by rendering all textures before any oth…
Browse files Browse the repository at this point in the history
…er layers + preserve depth buffer between extrusion layers
  • Loading branch information
Lauren Budorick committed Aug 28, 2017
1 parent ff7adb3 commit 4642796
Show file tree
Hide file tree
Showing 15 changed files with 402 additions and 135 deletions.
4 changes: 2 additions & 2 deletions src/render/draw_background.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ function drawBackground(painter: Painter, sourceCache: SourceCache, layer: Style
const image = layer.paint['background-pattern'];
const opacity = layer.paint['background-opacity'];

const isOpaque = !image && color[3] === 1 && opacity === 1;
if (painter.isOpaquePass !== isOpaque) return;
const pass = (!image && color[3] === 1 && opacity === 1) ? 'opaque' : 'translucent';
if (painter.renderPass !== pass) return;

gl.disable(gl.STENCIL_TEST);

Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type TileCoord from '../source/tile_coord';
module.exports = drawCircles;

function drawCircles(painter: Painter, sourceCache: SourceCache, layer: CircleStyleLayer, coords: Array<TileCoord>) {
if (painter.isOpaquePass) return;
if (painter.renderPass !== 'translucent') return;

const gl = painter.gl;

Expand Down
9 changes: 4 additions & 5 deletions src/render/draw_fill.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,22 @@ function drawFill(painter: Painter, sourceCache: SourceCache, layer: FillStyleLa
const gl = painter.gl;
gl.enable(gl.STENCIL_TEST);

const isOpaque =
!layer.paint['fill-pattern'] &&
const pass = (!layer.paint['fill-pattern'] &&
layer.isPaintValueFeatureConstant('fill-color') &&
layer.isPaintValueFeatureConstant('fill-opacity') &&
layer.paint['fill-color'][3] === 1 &&
layer.paint['fill-opacity'] === 1;
layer.paint['fill-opacity'] === 1) ? 'opaque' : 'translucent';

// Draw fill
if (painter.isOpaquePass === isOpaque) {
if (painter.renderPass === pass) {
// Once we switch to earcut drawing we can pull most of the WebGL setup
// outside of this coords loop.
painter.setDepthSublayer(1);
drawFillTiles(painter, sourceCache, layer, coords, drawFillTile);
}

// Draw stroke
if (!painter.isOpaquePass && layer.paint['fill-antialias']) {
if (painter.renderPass === 'translucent' && layer.paint['fill-antialias']) {
painter.lineWidth(2);
painter.depthMask(false);

Expand Down
95 changes: 24 additions & 71 deletions src/render/draw_fill_extrusion.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// @flow

const glMatrix = require('@mapbox/gl-matrix');
const VertexBuffer = require('../gl/vertex_buffer');
const VertexArrayObject = require('./vertex_array_object');
const PosArray = require('../data/pos_array');
const pattern = require('./pattern');
const mat3 = glMatrix.mat3;
const mat4 = glMatrix.mat4;
Expand All @@ -18,95 +15,51 @@ import type TileCoord from '../source/tile_coord';
module.exports = draw;

function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLayer, coords: Array<TileCoord>) {
if (painter.isOpaquePass) return;
if (layer.paint['fill-extrusion-opacity'] === 0) return;
if (painter.renderPass === '3d') {
const gl = painter.gl;

const gl = painter.gl;
gl.disable(gl.STENCIL_TEST);
gl.enable(gl.DEPTH_TEST);
painter.depthMask(true);

// Create a new texture to which to render the extrusion layer. This approach
// allows us to adjust opacity on a per-layer basis (eliminating the interior
// walls per-feature opacity problem)
const texture = renderToTexture(gl, painter);
gl.disable(gl.STENCIL_TEST);
gl.enable(gl.DEPTH_TEST);

gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
painter.clearColor();
painter.depthMask(true);

for (let i = 0; i < coords.length; i++) {
drawExtrusion(painter, source, layer, coords[i]);
for (let i = 0; i < coords.length; i++) {
drawExtrusion(painter, source, layer, coords[i]);
}
} else if (painter.renderPass === 'translucent') {
drawExtrusionTexture(painter, layer);
}

// Unbind the framebuffer as a render target and render it to the map
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
renderTextureToMap(gl, painter, layer, texture);
}

function renderToTexture(gl, painter) {
gl.activeTexture(gl.TEXTURE1);

let texture = painter.viewportTexture;
if (!texture) {
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, 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, painter.width, painter.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
painter.viewportTexture = texture;
} else {
gl.bindTexture(gl.TEXTURE_2D, texture);
}
function drawExtrusionTexture(painter, layer) {
const renderedTexture = painter.prerenderedFrames[layer.id];
if (!renderedTexture) return;

let fbo = painter.viewportFbo;
if (!fbo) {
fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
const depthRenderBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRenderBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, painter.width, painter.height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRenderBuffer);
painter.viewportFbo = fbo;
} else {
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
}

gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

return texture;
}

function renderTextureToMap(gl, painter, layer, texture) {
const gl = painter.gl;
const program = painter.useProgram('extrusionTexture');

gl.disable(gl.STENCIL_TEST);
gl.disable(gl.DEPTH_TEST);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindTexture(gl.TEXTURE_2D, renderedTexture.texture);

gl.uniform1f(program.uniforms.u_opacity, layer.paint['fill-extrusion-opacity']);
gl.uniform1i(program.uniforms.u_image, 1);
gl.uniform1i(program.uniforms.u_image, 0);

const matrix = mat4.create();
mat4.ortho(matrix, 0, painter.width, painter.height, 0, 0, 1);
gl.uniformMatrix4fv(program.uniforms.u_matrix, false, matrix);

gl.disable(gl.DEPTH_TEST);

gl.uniform2f(program.uniforms.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight);

const array = new PosArray();
array.emplaceBack(0, 0);
array.emplaceBack(1, 0);
array.emplaceBack(0, 1);
array.emplaceBack(1, 1);
const buffer = new VertexBuffer(gl, array);

const vao = new VertexArrayObject();
vao.bind(gl, program, buffer);
renderedTexture.vao.bind(gl, program, renderedTexture.buffer);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

gl.enable(gl.DEPTH_TEST);
// Since this texture has been rendered, make it available for reuse in the next frame.
painter.viewportFrames.push(renderedTexture);
delete painter.prerenderedFrames[layer.id];
}

function drawExtrusion(painter, source, layer, coord) {
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_line.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type LineBucket from '../data/bucket/line_bucket';
import type TileCoord from '../source/tile_coord';

module.exports = function drawLine(painter: Painter, sourceCache: SourceCache, layer: LineStyleLayer, coords: Array<TileCoord>) {
if (painter.isOpaquePass) return;
if (painter.renderPass !== 'translucent') return;
painter.setDepthSublayer(0);
painter.depthMask(false);

Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_raster.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type TileCoord from '../source/tile_coord';
module.exports = drawRaster;

function drawRaster(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array<TileCoord>) {
if (painter.isOpaquePass) return;
if (painter.renderPass !== 'translucent') return;

const gl = painter.gl;
const source = sourceCache.getSource();
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type TileCoord from '../source/tile_coord';
module.exports = drawSymbols;

function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolStyleLayer, coords: Array<TileCoord>) {
if (painter.isOpaquePass) return;
if (painter.renderPass !== 'translucent') return;

const drawAcrossEdges =
!layer.layout['text-allow-overlap'] &&
Expand Down
Loading

0 comments on commit 4642796

Please sign in to comment.