From 64d66b1fbe875982b56d572f9393c5c474a96afc Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Fri, 28 Oct 2016 20:20:03 +0300 Subject: [PATCH 1/2] optimize line drawing by avoiding per-tile work --- js/render/draw_line.js | 108 +++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 7ece8c88f2d..0383421ac5a 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -24,72 +24,84 @@ module.exports = function drawLine(painter, sourceCache, layer, coords) { // don't draw zero-width lines if (layer.paint['line-width'] <= 0) return; + const programId = + layer.paint['line-dasharray'] ? 'lineSDF' : + layer.paint['line-pattern'] ? 'linePattern' : 'line'; + for (let k = 0; k < coords.length; k++) { - drawLineTile(painter, sourceCache, layer, coords[k]); + const tile = sourceCache.getTile(coords[k]); + const bucket = tile.getBucket(layer); + if (!bucket) continue; + + const layerData = bucket.buffers.layerData[layer.id]; + const prevProgram = painter.currentProgram; + const program = painter.useProgram(programId, layerData.programConfiguration); + const programChanged = k === 0 || program !== prevProgram; + + if (programChanged) { + layerData.programConfiguration.setUniforms(painter.gl, program, layer, {zoom: painter.transform.zoom}); + } + drawLineTile(program, painter, tile, bucket.buffers, layer, coords[k], layerData, programChanged); } }; -function drawLineTile(painter, sourceCache, layer, coord) { - const tile = sourceCache.getTile(coord); - const bucket = tile.getBucket(layer); - if (!bucket) return; - - const buffers = bucket.buffers; - const layerData = buffers.layerData[layer.id]; +function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, programChanged) { const gl = painter.gl; - const dasharray = layer.paint['line-dasharray']; const image = layer.paint['line-pattern']; - const programConfiguration = layerData.programConfiguration; - const program = painter.useProgram(dasharray ? 'lineSDF' : image ? 'linePattern' : 'line', programConfiguration); - programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom}); - - if (!image) { - gl.uniform4fv(program.u_color, layer.paint['line-color']); - } - let posA, posB, imagePosA, imagePosB; + if (dasharray) { posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round'); posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round'); - gl.uniform1i(program.u_image, 0); - gl.activeTexture(gl.TEXTURE0); - painter.lineAtlas.bind(gl); - - gl.uniform1f(program.u_tex_y_a, posA.y); - gl.uniform1f(program.u_tex_y_b, posB.y); - gl.uniform1f(program.u_mix, dasharray.t); - } else if (image) { imagePosA = painter.spriteAtlas.getPosition(image.from, true); imagePosB = painter.spriteAtlas.getPosition(image.to, true); - if (!imagePosA || !imagePosB) return; - - gl.uniform1i(program.u_image, 0); - gl.activeTexture(gl.TEXTURE0); - painter.spriteAtlas.bind(gl, true); - - gl.uniform2fv(program.u_pattern_tl_a, imagePosA.tl); - gl.uniform2fv(program.u_pattern_br_a, imagePosA.br); - gl.uniform2fv(program.u_pattern_tl_b, imagePosB.tl); - gl.uniform2fv(program.u_pattern_br_b, imagePosB.br); - gl.uniform1f(program.u_fade, image.t); } - // the distance over which the line edge fades out. - // Retina devices need a smaller distance to avoid aliasing. - const antialiasing = 1 / browser.devicePixelRatio; - - gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2); - gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2); - gl.uniform1f(program.u_antialiasing, antialiasing / 2); - gl.uniform1f(program.u_blur, layer.paint['line-blur'] + antialiasing); - gl.uniform1f(program.u_opacity, layer.paint['line-opacity']); - gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, painter.transform.lineAntialiasingMatrix); - gl.uniform1f(program.u_offset, -layer.paint['line-offset']); - gl.uniform1f(program.u_extra, painter.transform.lineStretch); + if (programChanged) { + if (!image) { + gl.uniform4fv(program.u_color, layer.paint['line-color']); + } + + if (dasharray) { + gl.uniform1i(program.u_image, 0); + gl.activeTexture(gl.TEXTURE0); + painter.lineAtlas.bind(gl); + + gl.uniform1f(program.u_tex_y_a, posA.y); + gl.uniform1f(program.u_tex_y_b, posB.y); + gl.uniform1f(program.u_mix, dasharray.t); + + } else if (image) { + if (!imagePosA || !imagePosB) return; + + gl.uniform1i(program.u_image, 0); + gl.activeTexture(gl.TEXTURE0); + painter.spriteAtlas.bind(gl, true); + + gl.uniform2fv(program.u_pattern_tl_a, imagePosA.tl); + gl.uniform2fv(program.u_pattern_br_a, imagePosA.br); + gl.uniform2fv(program.u_pattern_tl_b, imagePosB.tl); + gl.uniform2fv(program.u_pattern_br_b, imagePosB.br); + gl.uniform1f(program.u_fade, image.t); + } + + // the distance over which the line edge fades out. + // Retina devices need a smaller distance to avoid aliasing. + const antialiasing = 1 / browser.devicePixelRatio; + + gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2); + gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2); + gl.uniform1f(program.u_antialiasing, antialiasing / 2); + gl.uniform1f(program.u_blur, layer.paint['line-blur'] + antialiasing); + gl.uniform1f(program.u_opacity, layer.paint['line-opacity']); + gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, painter.transform.lineAntialiasingMatrix); + gl.uniform1f(program.u_offset, -layer.paint['line-offset']); + gl.uniform1f(program.u_extra, painter.transform.lineStretch); + } painter.enableTileClippingMask(coord); From 5148a77371e4ba73946a9e6527ac4aed0e77e5fe Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Sat, 29 Oct 2016 00:49:36 +0300 Subject: [PATCH 2/2] avoid redundant dasharray/pattern state change in draw_line --- js/render/draw_line.js | 59 +++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 0383421ac5a..7ec90b128c3 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -28,6 +28,7 @@ module.exports = function drawLine(painter, sourceCache, layer, coords) { layer.paint['line-dasharray'] ? 'lineSDF' : layer.paint['line-pattern'] ? 'linePattern' : 'line'; + let prevTileZoom; for (let k = 0; k < coords.length; k++) { const tile = sourceCache.getTile(coords[k]); const bucket = tile.getBucket(layer); @@ -37,28 +38,45 @@ module.exports = function drawLine(painter, sourceCache, layer, coords) { const prevProgram = painter.currentProgram; const program = painter.useProgram(programId, layerData.programConfiguration); const programChanged = k === 0 || program !== prevProgram; + const tileRatioChanged = prevTileZoom !== tile.coord.z; if (programChanged) { layerData.programConfiguration.setUniforms(painter.gl, program, layer, {zoom: painter.transform.zoom}); } - drawLineTile(program, painter, tile, bucket.buffers, layer, coords[k], layerData, programChanged); + drawLineTile(program, painter, tile, bucket.buffers, layer, coords[k], layerData, programChanged, tileRatioChanged); + prevTileZoom = tile.coord.z; } }; -function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, programChanged) { +function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, programChanged, tileRatioChanged) { const gl = painter.gl; const dasharray = layer.paint['line-dasharray']; const image = layer.paint['line-pattern']; let posA, posB, imagePosA, imagePosB; - if (dasharray) { - posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round'); - posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round'); + if (programChanged || tileRatioChanged) { + const tileRatio = 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom); - } else if (image) { - imagePosA = painter.spriteAtlas.getPosition(image.from, true); - imagePosB = painter.spriteAtlas.getPosition(image.to, true); + if (dasharray) { + posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round'); + posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round'); + + const widthA = posA.width * dasharray.fromScale; + const widthB = posB.width * dasharray.toScale; + + gl.uniform2f(program.u_patternscale_a, tileRatio / widthA, -posA.height / 2); + gl.uniform2f(program.u_patternscale_b, tileRatio / widthB, -posB.height / 2); + gl.uniform1f(program.u_sdfgamma, painter.lineAtlas.width / (Math.min(widthA, widthB) * 256 * browser.devicePixelRatio) / 2); + + } else if (image) { + imagePosA = painter.spriteAtlas.getPosition(image.from, true); + imagePosB = painter.spriteAtlas.getPosition(image.to, true); + if (!imagePosA || !imagePosB) return; + + gl.uniform2f(program.u_pattern_size_a, imagePosA.size[0] * image.fromScale / tileRatio, imagePosB.size[1]); + gl.uniform2f(program.u_pattern_size_b, imagePosB.size[0] * image.toScale / tileRatio, imagePosB.size[1]); + } } if (programChanged) { @@ -76,8 +94,6 @@ function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, gl.uniform1f(program.u_mix, dasharray.t); } else if (image) { - if (!imagePosA || !imagePosB) return; - gl.uniform1i(program.u_image, 0); gl.activeTexture(gl.TEXTURE0); painter.spriteAtlas.bind(gl, true); @@ -105,32 +121,9 @@ function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, painter.enableTileClippingMask(coord); - // set uniforms that are different for each tile const posMatrix = painter.translatePosMatrix(coord.posMatrix, tile, layer.paint['line-translate'], layer.paint['line-translate-anchor']); gl.uniformMatrix4fv(program.u_matrix, false, posMatrix); - if (dasharray) { - const widthA = posA.width * dasharray.fromScale; - const widthB = posB.width * dasharray.toScale; - const scaleA = [1 / pixelsToTileUnits(tile, widthA, painter.transform.tileZoom), -posA.height / 2]; - const scaleB = [1 / pixelsToTileUnits(tile, widthB, painter.transform.tileZoom), -posB.height / 2]; - const gamma = painter.lineAtlas.width / (Math.min(widthA, widthB) * 256 * browser.devicePixelRatio) / 2; - - gl.uniform2fv(program.u_patternscale_a, scaleA); - gl.uniform2fv(program.u_patternscale_b, scaleB); - gl.uniform1f(program.u_sdfgamma, gamma); - - } else if (image) { - gl.uniform2fv(program.u_pattern_size_a, [ - pixelsToTileUnits(tile, imagePosA.size[0] * image.fromScale, painter.transform.tileZoom), - imagePosB.size[1] - ]); - gl.uniform2fv(program.u_pattern_size_b, [ - pixelsToTileUnits(tile, imagePosB.size[0] * image.toScale, painter.transform.tileZoom), - imagePosB.size[1] - ]); - } - gl.uniform1f(program.u_ratio, 1 / pixelsToTileUnits(tile, 1, painter.transform.zoom)); for (const segment of buffers.segments) {