From 3252ad1114761ddc87ea28044fd61dc08862d7d2 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 24 Oct 2016 08:42:18 -0700 Subject: [PATCH 1/2] Push ProgramConfiguration to {Array,Buffer}Group --- js/data/array_group.js | 40 +++++++++++++++---------- js/data/bucket.js | 20 +++---------- js/data/bucket/circle_bucket.js | 2 +- js/data/bucket/fill_bucket.js | 2 +- js/data/bucket/fill_extrusion_bucket.js | 2 +- js/data/bucket/line_bucket.js | 2 +- js/data/buffer_group.js | 36 ++++++++++++---------- js/data/program_configuration.js | 4 +-- js/render/draw_circle.js | 5 ++-- js/render/draw_extrusion.js | 5 ++-- js/render/draw_fill.js | 12 ++++---- js/render/draw_line.js | 5 ++-- test/js/data/bucket.test.js | 16 +++++----- test/js/data/fill_bucket.test.js | 6 ++-- 14 files changed, 82 insertions(+), 75 deletions(-) diff --git a/js/data/array_group.js b/js/data/array_group.js index e0adab1b334..f8faeb86293 100644 --- a/js/data/array_group.js +++ b/js/data/array_group.js @@ -1,6 +1,7 @@ 'use strict'; const util = require('../util/util'); +const ProgramConfiguration = require('./program_configuration'); class Segment { constructor(vertexOffset, primitiveOffset) { @@ -31,7 +32,9 @@ class Segment { * @private */ class ArrayGroup { - constructor(programInterface, programConfigurations) { + constructor(programInterface, layers, zoom) { + this.globalProperties = {zoom}; + const LayoutVertexArrayType = programInterface.layoutVertexArrayType; this.layoutVertexArray = new LayoutVertexArrayType(); @@ -41,12 +44,17 @@ class ArrayGroup { const ElementArrayType2 = programInterface.elementArrayType2; if (ElementArrayType2) this.elementArray2 = new ElementArrayType2(); - this.paintVertexArrays = util.mapObject(programConfigurations, (programConfiguration) => { + this.layerData = {}; + for (const layer of layers) { + const programConfiguration = ProgramConfiguration.createDynamic( + programInterface.paintAttributes || [], layer, zoom); const PaintVertexArrayType = programConfiguration.paintVertexArrayType(); - const paintVertexArray = new PaintVertexArrayType(); - paintVertexArray.programConfiguration = programConfiguration; - return paintVertexArray; - }); + this.layerData[layer.id] = { + layer: layer, + programConfiguration: programConfiguration, + paintVertexArray: new PaintVertexArrayType() + }; + } this.segments = []; this.segments2 = []; @@ -70,14 +78,14 @@ class ArrayGroup { return segment; } - populatePaintArrays(layers, globalProperties, featureProperties) { - for (const layer of layers) { - const paintArray = this.paintVertexArrays[layer.id]; - paintArray.programConfiguration.populatePaintArray( - layer, - paintArray, + populatePaintArrays(featureProperties) { + for (const key in this.layerData) { + const layerData = this.layerData[key]; + layerData.programConfiguration.populatePaintArray( + layerData.layer, + layerData.paintVertexArray, this.layoutVertexArray.length, - globalProperties, + this.globalProperties, featureProperties); } } @@ -91,10 +99,10 @@ class ArrayGroup { layoutVertexArray: this.layoutVertexArray.serialize(transferables), elementArray: this.elementArray && this.elementArray.serialize(transferables), elementArray2: this.elementArray2 && this.elementArray2.serialize(transferables), - paintVertexArrays: util.mapObject(this.paintVertexArrays, (array) => { + paintVertexArrays: util.mapObject(this.layerData, (layerData) => { return { - array: array.serialize(transferables), - type: array.constructor.serialize() + array: layerData.paintVertexArray.serialize(transferables), + type: layerData.paintVertexArray.constructor.serialize() }; }), segments: this.segments, diff --git a/js/data/bucket.js b/js/data/bucket.js index edcf49b0f56..c58730d7ba4 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -2,7 +2,6 @@ const ArrayGroup = require('./array_group'); const BufferGroup = require('./buffer_group'); -const ProgramConfiguration = require('./program_configuration'); const util = require('../util/util'); const FAKE_ZOOM_HISTORY = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; @@ -32,17 +31,9 @@ class Bucket { this.layers = options.layers; this.index = options.index; - this.programConfigurations = util.mapObject(this.programInterfaces, (programInterface) => { - const result = {}; - for (const layer of this.layers) { - result[layer.id] = ProgramConfiguration.createDynamic(programInterface.paintAttributes || [], layer, options); - } - return result; - }); - if (options.arrays) { this.bufferGroups = util.mapObject(options.arrays, (arrayGroup, programName) => { - return new BufferGroup(arrayGroup, this.programInterfaces[programName]); + return new BufferGroup(this.programInterfaces[programName], options.layers, options.zoom, arrayGroup); }); } } @@ -60,12 +51,9 @@ class Bucket { } createArrays() { - this.arrays = {}; - for (const programName in this.programInterfaces) { - this.arrays[programName] = new ArrayGroup( - this.programInterfaces[programName], - this.programConfigurations[programName]); - } + this.arrays = util.mapObject(this.programInterfaces, (programInterface) => { + return new ArrayGroup(programInterface, this.layers, this.zoom); + }); } destroy() { diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index eede026f9d8..721957728b7 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -113,7 +113,7 @@ class CircleBucket extends Bucket { } } - arrays.populatePaintArrays(this.layers, {zoom: this.zoom}, feature.properties); + arrays.populatePaintArrays(feature.properties); } } diff --git a/js/data/bucket/fill_bucket.js b/js/data/bucket/fill_bucket.js index bdd659af94e..e5c61be0019 100644 --- a/js/data/bucket/fill_bucket.js +++ b/js/data/bucket/fill_bucket.js @@ -112,7 +112,7 @@ class FillBucket extends Bucket { triangleSegment.primitiveLength += indices.length / 3; } - arrays.populatePaintArrays(this.layers, {zoom: this.zoom}, feature.properties); + arrays.populatePaintArrays(feature.properties); } } diff --git a/js/data/bucket/fill_extrusion_bucket.js b/js/data/bucket/fill_extrusion_bucket.js index a625fdbdbfb..c30deccb77d 100644 --- a/js/data/bucket/fill_extrusion_bucket.js +++ b/js/data/bucket/fill_extrusion_bucket.js @@ -157,7 +157,7 @@ class FillExtrusionBucket extends Bucket { segment.primitiveLength += triangleIndices.length / 3; } - arrays.populatePaintArrays(this.layers, {zoom: this.zoom}, feature.properties); + arrays.populatePaintArrays(feature.properties); } } diff --git a/js/data/bucket/line_bucket.js b/js/data/bucket/line_bucket.js index 6110be955a7..17d48ec8f63 100644 --- a/js/data/bucket/line_bucket.js +++ b/js/data/bucket/line_bucket.js @@ -356,7 +356,7 @@ class LineBucket extends Bucket { startOfLine = false; } - arrays.populatePaintArrays(this.layers, {zoom: this.zoom}, featureProperties); + arrays.populatePaintArrays(featureProperties); } /** diff --git a/js/data/buffer_group.js b/js/data/buffer_group.js index b8cb2a67b94..d1ba1e12508 100644 --- a/js/data/buffer_group.js +++ b/js/data/buffer_group.js @@ -2,34 +2,40 @@ const util = require('../util/util'); const Buffer = require('./buffer'); +const ProgramConfiguration = require('./program_configuration'); const VertexArrayObject = require('../render/vertex_array_object'); class BufferGroup { - - constructor(arrayGroup, programInterface) { - this.layoutVertexBuffer = new Buffer(arrayGroup.layoutVertexArray, + constructor(programInterface, layers, zoom, arrays) { + this.layoutVertexBuffer = new Buffer(arrays.layoutVertexArray, programInterface.layoutVertexArrayType.serialize(), Buffer.BufferType.VERTEX); - if (arrayGroup.elementArray) { - this.elementBuffer = new Buffer(arrayGroup.elementArray, + if (arrays.elementArray) { + this.elementBuffer = new Buffer(arrays.elementArray, programInterface.elementArrayType.serialize(), Buffer.BufferType.ELEMENT); } - if (arrayGroup.elementArray2) { - this.elementBuffer2 = new Buffer(arrayGroup.elementArray2, + if (arrays.elementArray2) { + this.elementBuffer2 = new Buffer(arrays.elementArray2, programInterface.elementArrayType2.serialize(), Buffer.BufferType.ELEMENT); } - this.paintVertexBuffers = util.mapObject(arrayGroup.paintVertexArrays, (array) => { - return new Buffer(array.array, array.type, Buffer.BufferType.VERTEX); - }); + this.layerData = {}; + for (const layer of layers) { + const array = arrays.paintVertexArrays[layer.id]; + this.layerData[layer.id] = { + programConfiguration: ProgramConfiguration.createDynamic( + programInterface.paintAttributes || [], layer, zoom), + paintVertexBuffer: new Buffer(array.array, array.type, Buffer.BufferType.VERTEX) + }; + } - this.segments = arrayGroup.segments; - this.segments2 = arrayGroup.segments2; + this.segments = arrays.segments; + this.segments2 = arrays.segments2; for (const segments of [this.segments, this.segments2]) { for (const segment of segments || []) { - segment.vaos = util.mapObject(arrayGroup.paintVertexArrays, () => { + segment.vaos = util.mapObject(arrays.paintVertexArrays, () => { return new VertexArrayObject(); }); } @@ -44,8 +50,8 @@ class BufferGroup { if (this.elementBuffer2) { this.elementBuffer2.destroy(); } - for (const n in this.paintVertexBuffers) { - this.paintVertexBuffers[n].destroy(); + for (const n in this.layerData) { + this.layerData[n].paintVertexBuffer.destroy(); } for (const segments of [this.segments, this.segments2]) { for (const segment of segments || []) { diff --git a/js/data/program_configuration.js b/js/data/program_configuration.js index 01fc77e95ba..53caf292968 100644 --- a/js/data/program_configuration.js +++ b/js/data/program_configuration.js @@ -22,7 +22,7 @@ const assert = require('assert'); * @private */ class ProgramConfiguration { - static createDynamic(attributes, layer, options) { + static createDynamic(attributes, layer, zoom) { const self = new ProgramConfiguration(); self.attributes = []; @@ -61,7 +61,7 @@ class ProgramConfiguration { // Find the four closest stops, ideally with two on each side of the zoom level. let numStops = 0; const zoomLevels = layer.getPaintValueStopZoomLevels(attribute.paintProperty); - while (numStops < zoomLevels.length && zoomLevels[numStops] < options.zoom) numStops++; + while (numStops < zoomLevels.length && zoomLevels[numStops] < zoom) numStops++; const stopOffset = Math.max(0, Math.min(zoomLevels.length - 4, numStops - 2)); const fourZoomLevels = []; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 10857014d42..1e3de33f94a 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -24,7 +24,8 @@ function drawCircles(painter, sourceCache, layer, coords) { if (!bucket) continue; const buffers = bucket.bufferGroups.circle; - const programConfiguration = bucket.programConfigurations.circle[layer.id]; + const layerData = buffers.layerData[layer.id]; + const programConfiguration = layerData.programConfiguration; const program = painter.useProgram('circle', programConfiguration); programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom}); @@ -48,7 +49,7 @@ function drawCircles(painter, sourceCache, layer, coords) { )); for (const segment of buffers.segments) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, buffers.paintVertexBuffers[layer.id], segment.vertexOffset); + segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, layerData.paintVertexBuffer, segment.vertexOffset); gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); } } diff --git a/js/render/draw_extrusion.js b/js/render/draw_extrusion.js index f7bc7da196f..a04a630da9c 100644 --- a/js/render/draw_extrusion.js +++ b/js/render/draw_extrusion.js @@ -153,7 +153,8 @@ function drawExtrusion(painter, source, layer, coord) { const image = layer.paint['fill-pattern']; - const programConfiguration = bucket.programConfigurations.fillextrusion[layer.id]; + const layerData = buffers.layerData[layer.id]; + const programConfiguration = layerData.programConfiguration; const program = painter.useProgram(image ? 'fillExtrudePattern' : 'fillExtrude', programConfiguration); programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom}); @@ -165,7 +166,7 @@ function drawExtrusion(painter, source, layer, coord) { setLight(program, painter); for (const segment of buffers.segments) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, buffers.paintVertexBuffers[layer.id], segment.vertexOffset); + segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, layerData.paintVertexBuffer, segment.vertexOffset); gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); } } diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index 9cc4288c2c5..0fbf32daccf 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -55,11 +55,12 @@ function drawFill(painter, sourceCache, layer, coord) { const gl = painter.gl; const image = layer.paint['fill-pattern']; + const layerData = buffers.layerData[layer.id]; + let program; if (!image) { - - const programConfiguration = bucket.programConfigurations.fill[layer.id]; + const programConfiguration = layerData.programConfiguration; program = painter.useProgram('fill', programConfiguration); programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom}); @@ -83,7 +84,7 @@ function drawFill(painter, sourceCache, layer, coord) { painter.enableTileClippingMask(coord); for (const segment of buffers.segments) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, buffers.paintVertexBuffers[layer.id], segment.vertexOffset); + segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, layerData.paintVertexBuffer, segment.vertexOffset); gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); } } @@ -94,6 +95,7 @@ function drawStroke(painter, sourceCache, layer, coord) { if (!bucket) return; const buffers = bucket.bufferGroups.fill; + const layerData = buffers.layerData[layer.id]; const gl = painter.gl; const image = layer.paint['fill-pattern']; @@ -105,7 +107,7 @@ function drawStroke(painter, sourceCache, layer, coord) { gl.uniform2f(program.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight); } else { - const programConfiguration = bucket.programConfigurations.fill[layer.id]; + const programConfiguration = layerData.programConfiguration; program = painter.useProgram('fillOutline', programConfiguration); programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom}); gl.uniform2f(program.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight); @@ -127,7 +129,7 @@ function drawStroke(painter, sourceCache, layer, coord) { painter.enableTileClippingMask(coord); for (const segment of buffers.segments2) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer2, buffers.paintVertexBuffers[layer.id], segment.vertexOffset); + segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer2, layerData.paintVertexBuffer, segment.vertexOffset); gl.drawElements(gl.LINES, segment.primitiveLength * 2, gl.UNSIGNED_SHORT, segment.primitiveOffset * 2 * 2); } } diff --git a/js/render/draw_line.js b/js/render/draw_line.js index f8df4c23bc2..e6bec76d380 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -35,12 +35,13 @@ function drawLineTile(painter, sourceCache, layer, coord) { if (!bucket) return; const buffers = bucket.bufferGroups.line; + const layerData = buffers.layerData[layer.id]; const gl = painter.gl; const dasharray = layer.paint['line-dasharray']; const image = layer.paint['line-pattern']; - const programConfiguration = bucket.programConfigurations.line[layer.id]; + const programConfiguration = layerData.programConfiguration; const program = painter.useProgram(dasharray ? 'lineSDF' : image ? 'linePattern' : 'line', programConfiguration); programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom}); @@ -121,7 +122,7 @@ function drawLineTile(painter, sourceCache, layer, coord) { gl.uniform1f(program.u_ratio, 1 / pixelsToTileUnits(tile, 1, painter.transform.zoom)); for (const segment of buffers.segments) { - segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, buffers.paintVertexBuffers[layer.id], segment.vertexOffset); + segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, layerData.paintVertexBuffer, segment.vertexOffset); gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2); } } diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index 4325b6d08a7..55c911fce40 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -63,7 +63,7 @@ test('Bucket', (t) => { arrays.layoutVertexArray.emplaceBack(point.x * 2, point.y * 2); arrays.elementArray.emplaceBack(1, 2, 3); arrays.elementArray2.emplaceBack(point.x, point.y); - arrays.populatePaintArrays(this.layers, {}, feature.properties); + arrays.populatePaintArrays(feature.properties); } } @@ -96,7 +96,7 @@ test('Bucket', (t) => { const v0 = testVertex.get(0); t.equal(v0.a_box0, 34); t.equal(v0.a_box1, 84); - const paintVertex = bucket.arrays.test.paintVertexArrays.layerid; + const paintVertex = bucket.arrays.test.layerData.layerid.paintVertexArray; t.equal(paintVertex.length, 1); const p0 = paintVertex.get(0); t.equal(p0.a_map, 17); @@ -126,8 +126,8 @@ test('Bucket', (t) => { bucket.populate([createFeature(17, 42)], createOptions()); const v0 = bucket.arrays.test.layoutVertexArray.get(0); - const a0 = bucket.arrays.test.paintVertexArrays.one.get(0); - const b0 = bucket.arrays.test.paintVertexArrays.two.get(0); + const a0 = bucket.arrays.test.layerData.one.paintVertexArray.get(0); + const b0 = bucket.arrays.test.layerData.two.paintVertexArray.get(0); t.equal(a0.a_map, 17); t.equal(b0.a_map, 17); t.equal(v0.a_box0, 34); @@ -154,7 +154,7 @@ test('Bucket', (t) => { t.equal(bucket.arrays.test.layoutVertexArray.bytesPerElement, 0); t.deepEqual( - bucket.programConfigurations.test.one.uniforms[0].getValue.call(bucket), + bucket.arrays.test.layerData.one.programConfiguration.uniforms[0].getValue.call(bucket), [5] ); @@ -214,7 +214,7 @@ test('Bucket', (t) => { t.equal(bucket.arrays.test.layoutVertexArray.arrayBuffer, transferables[0]); t.equal(bucket.arrays.test.elementArray.arrayBuffer, transferables[1]); t.equal(bucket.arrays.test.elementArray2.arrayBuffer, transferables[2]); - t.equal(bucket.arrays.test.paintVertexArrays.layerid.arrayBuffer, transferables[3]); + t.equal(bucket.arrays.test.layerData.layerid.paintVertexArray.arrayBuffer, transferables[3]); t.end(); }); @@ -231,7 +231,7 @@ test('Bucket', (t) => { const v0 = testVertex.get(0); t.equal(v0.a_box0, 34); t.equal(v0.a_box1, 84); - const testPaintVertex = bucket.arrays.test.paintVertexArrays.layerid; + const testPaintVertex = bucket.arrays.test.layerData.layerid.paintVertexArray; t.equal(testPaintVertex.length, 1); const p0 = testPaintVertex.get(0); t.equal(p0.a_map, 17); @@ -268,7 +268,7 @@ test('Bucket', (t) => { const v0 = testVertex.get(0); t.equal(v0.a_box0, 34); t.equal(v0.a_box1, 84); - const testPaintVertex = bucket.arrays.test.paintVertexArrays.layerid; + const testPaintVertex = bucket.arrays.test.layerData.layerid.paintVertexArray; t.equal(testPaintVertex.length, 1); const p0 = testPaintVertex.get(0); t.equal(p0.a_map, 17); diff --git a/test/js/data/fill_bucket.test.js b/test/js/data/fill_bucket.test.js index c417541e7ef..6f51947a222 100644 --- a/test/js/data/fill_bucket.test.js +++ b/test/js/data/fill_bucket.test.js @@ -103,9 +103,9 @@ test('FillBucket segmentation', (t) => { primitiveLength: 126 }); - t.equal(arrays.paintVertexArrays.test.length, 266); - for (let i = 0; i < arrays.paintVertexArrays.test.length; i++) { - const vertex = arrays.paintVertexArrays.test.get(i); + t.equal(arrays.layerData.test.paintVertexArray.length, 266); + for (let i = 0; i < arrays.layerData.test.paintVertexArray.length; i++) { + const vertex = arrays.layerData.test.paintVertexArray.get(i); t.deepEqual([ vertex['a_color0'], vertex['a_color1'], From 46a73c4efcf119d287810e0f7ec305eb9d4102a6 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 27 Oct 2016 09:31:45 -0700 Subject: [PATCH 2/2] Encapsulate SymbolBucket's unique requirements SymbolBucket is in several ways the "odd bucket out": * It's the only bucket type that works with multiple program interfaces -- one for glyphs, one for icons, and one for collision boxes. * It's the only bucket type whose instances are reused to build buffers multiple times. Previously, the Bucket base class was generalized such that it could support multiple program interfaces for any Bucket subtype, and re-create buffers at any time. However, this added complexity to all subtypes other than Symbol. Now, SymbolBucket encapsulates its differences: * It uses ArrayGroup/BufferGroup directly, creating one for each of its three interfaces. In turn, the other bucket types, their corresponding layer rendering functions, and the Bucket base class, can all assume a single program interface. * It recreates these arrays when needed, while the Bucket base class creates them once, in the constructor. --- js/data/bucket.js | 40 ++----- js/data/bucket/circle_bucket.js | 105 ++++++++-------- js/data/bucket/fill_bucket.js | 82 +++++++------ js/data/bucket/fill_extrusion_bucket.js | 100 ++++++++-------- js/data/bucket/line_bucket.js | 55 ++++----- js/data/bucket/symbol_bucket.js | 96 +++++++++------ js/render/draw_circle.js | 2 +- js/render/draw_collision_debug.js | 2 +- js/render/draw_extrusion.js | 2 +- js/render/draw_fill.js | 4 +- js/render/draw_line.js | 2 +- js/render/draw_symbol.js | 2 +- js/source/worker_tile.js | 23 +++- test/js/data/bucket.test.js | 151 ++++++------------------ test/js/data/fill_bucket.test.js | 4 +- test/js/data/line_bucket.test.js | 1 - test/js/data/symbol_bucket.test.js | 12 ++ test/js/source/worker_tile.test.js | 6 +- 18 files changed, 310 insertions(+), 379 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index c58730d7ba4..30b06d66a96 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -4,8 +4,6 @@ const ArrayGroup = require('./array_group'); const BufferGroup = require('./buffer_group'); const util = require('../util/util'); -const FAKE_ZOOM_HISTORY = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; - /** * The `Bucket` class is the single point of knowledge about turning vector * tiles into WebGL buffers. @@ -25,23 +23,20 @@ class Bucket { * built for this tile. This object facilitates sharing of `Buffer`s be between `Bucket`s. */ - constructor (options) { + constructor (options, programInterface) { this.zoom = options.zoom; this.overscaling = options.overscaling; this.layers = options.layers; this.index = options.index; if (options.arrays) { - this.bufferGroups = util.mapObject(options.arrays, (arrayGroup, programName) => { - return new BufferGroup(this.programInterfaces[programName], options.layers, options.zoom, arrayGroup); - }); + this.buffers = new BufferGroup(programInterface, options.layers, options.zoom, options.arrays); + } else { + this.arrays = new ArrayGroup(programInterface, options.layers, options.zoom); } } populate(features, options) { - this.createArrays(); - this.recalculateStyleLayers(); - for (const feature of features) { if (this.layers[0].filter(feature)) { this.addFeature(feature); @@ -50,39 +45,20 @@ class Bucket { } } - createArrays() { - this.arrays = util.mapObject(this.programInterfaces, (programInterface) => { - return new ArrayGroup(programInterface, this.layers, this.zoom); - }); - } - - destroy() { - for (const programName in this.bufferGroups) { - this.bufferGroups[programName].destroy(); - } - } - isEmpty() { - for (const programName in this.arrays) { - if (!this.arrays[programName].isEmpty()) { - return false; - } - } - return true; + return this.arrays.isEmpty(); } serialize(transferables) { return { zoom: this.zoom, layerIds: this.layers.map((l) => l.id), - arrays: util.mapObject(this.arrays, (a) => a.serialize(transferables)) + arrays: this.arrays.serialize(transferables) }; } - recalculateStyleLayers() { - for (const layer of this.layers) { - layer.recalculate(this.zoom, FAKE_ZOOM_HISTORY); - } + destroy() { + this.buffers.destroy(); } } diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index 721957728b7..cac764ca5d4 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -6,56 +6,54 @@ const ElementArrayType = require('../element_array_type'); const loadGeometry = require('../load_geometry'); const EXTENT = require('../extent'); -const circleInterfaces = { - circle: { - layoutVertexArrayType: new VertexArrayType([{ - name: 'a_pos', - components: 2, - type: 'Int16' - }]), - elementArrayType: new ElementArrayType(), - - paintAttributes: [{ - name: 'a_color', - components: 4, - type: 'Uint8', - getValue: (layer, globalProperties, featureProperties) => { - return layer.getPaintValue("circle-color", globalProperties, featureProperties); - }, - multiplier: 255, - paintProperty: 'circle-color' - }, { - name: 'a_radius', - components: 1, - type: 'Uint16', - isLayerConstant: false, - getValue: (layer, globalProperties, featureProperties) => { - return [layer.getPaintValue("circle-radius", globalProperties, featureProperties)]; - }, - multiplier: 10, - paintProperty: 'circle-radius' - }, { - name: 'a_blur', - components: 1, - type: 'Uint16', - isLayerConstant: false, - getValue: (layer, globalProperties, featureProperties) => { - return [layer.getPaintValue("circle-blur", globalProperties, featureProperties)]; - }, - multiplier: 10, - paintProperty: 'circle-blur' - }, { - name: 'a_opacity', - components: 1, - type: 'Uint16', - isLayerConstant: false, - getValue: (layer, globalProperties, featureProperties) => { - return [layer.getPaintValue("circle-opacity", globalProperties, featureProperties)]; - }, - multiplier: 255, - paintProperty: 'circle-opacity' - }] - } +const circleInterface = { + layoutVertexArrayType: new VertexArrayType([{ + name: 'a_pos', + components: 2, + type: 'Int16' + }]), + elementArrayType: new ElementArrayType(), + + paintAttributes: [{ + name: 'a_color', + components: 4, + type: 'Uint8', + getValue: (layer, globalProperties, featureProperties) => { + return layer.getPaintValue("circle-color", globalProperties, featureProperties); + }, + multiplier: 255, + paintProperty: 'circle-color' + }, { + name: 'a_radius', + components: 1, + type: 'Uint16', + isLayerConstant: false, + getValue: (layer, globalProperties, featureProperties) => { + return [layer.getPaintValue("circle-radius", globalProperties, featureProperties)]; + }, + multiplier: 10, + paintProperty: 'circle-radius' + }, { + name: 'a_blur', + components: 1, + type: 'Uint16', + isLayerConstant: false, + getValue: (layer, globalProperties, featureProperties) => { + return [layer.getPaintValue("circle-blur", globalProperties, featureProperties)]; + }, + multiplier: 10, + paintProperty: 'circle-blur' + }, { + name: 'a_opacity', + components: 1, + type: 'Uint16', + isLayerConstant: false, + getValue: (layer, globalProperties, featureProperties) => { + return [layer.getPaintValue("circle-opacity", globalProperties, featureProperties)]; + }, + multiplier: 255, + paintProperty: 'circle-opacity' + }] }; function addCircleVertex(layoutVertexArray, x, y, extrudeX, extrudeY) { @@ -72,13 +70,12 @@ function addCircleVertex(layoutVertexArray, x, y, extrudeX, extrudeY) { * @private */ class CircleBucket extends Bucket { - - get programInterfaces() { - return circleInterfaces; + constructor(options) { + super(options, circleInterface); } addFeature(feature) { - const arrays = this.arrays.circle; + const arrays = this.arrays; for (const ring of loadGeometry(feature)) { for (const point of ring) { diff --git a/js/data/bucket/fill_bucket.js b/js/data/bucket/fill_bucket.js index e5c61be0019..e31c9555947 100644 --- a/js/data/bucket/fill_bucket.js +++ b/js/data/bucket/fill_bucket.js @@ -9,54 +9,52 @@ const classifyRings = require('../../util/classify_rings'); const assert = require('assert'); const EARCUT_MAX_RINGS = 500; -const fillInterfaces = { - fill: { - layoutVertexArrayType: new VertexArrayType([{ - name: 'a_pos', - components: 2, - type: 'Int16' - }]), - elementArrayType: new ElementArrayType(3), - elementArrayType2: new ElementArrayType(2), - - paintAttributes: [{ - name: 'a_color', - components: 4, - type: 'Uint8', - getValue: (layer, globalProperties, featureProperties) => { - return layer.getPaintValue("fill-color", globalProperties, featureProperties); - }, - multiplier: 255, - paintProperty: 'fill-color' - }, { - name: 'a_outline_color', - components: 4, - type: 'Uint8', - getValue: (layer, globalProperties, featureProperties) => { - return layer.getPaintValue("fill-outline-color", globalProperties, featureProperties); - }, - multiplier: 255, - paintProperty: 'fill-outline-color' - }, { - name: 'a_opacity', - components: 1, - type: 'Uint8', - getValue: (layer, globalProperties, featureProperties) => { - return [layer.getPaintValue("fill-opacity", globalProperties, featureProperties)]; - }, - multiplier: 255, - paintProperty: 'fill-opacity' - }] - } +const fillInterface = { + layoutVertexArrayType: new VertexArrayType([{ + name: 'a_pos', + components: 2, + type: 'Int16' + }]), + elementArrayType: new ElementArrayType(3), + elementArrayType2: new ElementArrayType(2), + + paintAttributes: [{ + name: 'a_color', + components: 4, + type: 'Uint8', + getValue: (layer, globalProperties, featureProperties) => { + return layer.getPaintValue("fill-color", globalProperties, featureProperties); + }, + multiplier: 255, + paintProperty: 'fill-color' + }, { + name: 'a_outline_color', + components: 4, + type: 'Uint8', + getValue: (layer, globalProperties, featureProperties) => { + return layer.getPaintValue("fill-outline-color", globalProperties, featureProperties); + }, + multiplier: 255, + paintProperty: 'fill-outline-color' + }, { + name: 'a_opacity', + components: 1, + type: 'Uint8', + getValue: (layer, globalProperties, featureProperties) => { + return [layer.getPaintValue("fill-opacity", globalProperties, featureProperties)]; + }, + multiplier: 255, + paintProperty: 'fill-opacity' + }] }; class FillBucket extends Bucket { - get programInterfaces() { - return fillInterfaces; + constructor(options) { + super(options, fillInterface); } addFeature(feature) { - const arrays = this.arrays.fill; + const arrays = this.arrays; for (const polygon of classifyRings(loadGeometry(feature), EARCUT_MAX_RINGS)) { let numVertices = 0; diff --git a/js/data/bucket/fill_extrusion_bucket.js b/js/data/bucket/fill_extrusion_bucket.js index c30deccb77d..dd5e533e2fe 100644 --- a/js/data/bucket/fill_extrusion_bucket.js +++ b/js/data/bucket/fill_extrusion_bucket.js @@ -10,54 +10,52 @@ const classifyRings = require('../../util/classify_rings'); const assert = require('assert'); const EARCUT_MAX_RINGS = 500; -const fillExtrusionInterfaces = { - fillextrusion: { - layoutVertexArrayType: new VertexArrayType([{ - name: 'a_pos', - components: 2, - type: 'Int16' - }, { - name: 'a_normal', - components: 3, - type: 'Int16' - }, { - name: 'a_edgedistance', - components: 1, - type: 'Int16' - }]), - elementArrayType: new ElementArrayType(3), - - paintAttributes: [{ - name: 'a_minH', - components: 1, - type: 'Uint16', - getValue: (layer, globalProperties, featureProperties) => { - return [Math.max(layer.getPaintValue("fill-extrude-base", globalProperties, featureProperties), 0)]; - }, - multiplier: 1, - paintProperty: 'fill-extrude-base' - }, { - name: 'a_maxH', - components: 1, - type: 'Uint16', - getValue: (layer, globalProperties, featureProperties) => { - return [Math.max(layer.getPaintValue("fill-extrude-height", globalProperties, featureProperties), 0)]; - }, - multiplier: 1, - paintProperty: 'fill-extrude-height' - }, { - name: 'a_color', - components: 4, - type: 'Uint8', - getValue: (layer, globalProperties, featureProperties) => { - const color = layer.getPaintValue("fill-color", globalProperties, featureProperties); - color[3] = 1.0; - return color; - }, - multiplier: 255, - paintProperty: 'fill-color' - }] - } +const fillExtrusionInterface = { + layoutVertexArrayType: new VertexArrayType([{ + name: 'a_pos', + components: 2, + type: 'Int16' + }, { + name: 'a_normal', + components: 3, + type: 'Int16' + }, { + name: 'a_edgedistance', + components: 1, + type: 'Int16' + }]), + elementArrayType: new ElementArrayType(3), + + paintAttributes: [{ + name: 'a_minH', + components: 1, + type: 'Uint16', + getValue: (layer, globalProperties, featureProperties) => { + return [Math.max(layer.getPaintValue("fill-extrude-base", globalProperties, featureProperties), 0)]; + }, + multiplier: 1, + paintProperty: 'fill-extrude-base' + }, { + name: 'a_maxH', + components: 1, + type: 'Uint16', + getValue: (layer, globalProperties, featureProperties) => { + return [Math.max(layer.getPaintValue("fill-extrude-height", globalProperties, featureProperties), 0)]; + }, + multiplier: 1, + paintProperty: 'fill-extrude-height' + }, { + name: 'a_color', + components: 4, + type: 'Uint8', + getValue: (layer, globalProperties, featureProperties) => { + const color = layer.getPaintValue("fill-color", globalProperties, featureProperties); + color[3] = 1.0; + return color; + }, + multiplier: 255, + paintProperty: 'fill-color' + }] }; const FACTOR = Math.pow(2, 13); @@ -78,12 +76,12 @@ function addVertex(vertexArray, x, y, nx, ny, nz, t, e) { } class FillExtrusionBucket extends Bucket { - get programInterfaces() { - return fillExtrusionInterfaces; + constructor(options) { + super(options, fillExtrusionInterface); } addFeature(feature) { - const arrays = this.arrays.fillextrusion; + const arrays = this.arrays; for (const polygon of classifyRings(loadGeometry(feature), EARCUT_MAX_RINGS)) { let numVertices = 0; diff --git a/js/data/bucket/line_bucket.js b/js/data/bucket/line_bucket.js index 17d48ec8f63..bad53c3d70f 100644 --- a/js/data/bucket/line_bucket.js +++ b/js/data/bucket/line_bucket.js @@ -39,29 +39,27 @@ const LINE_DISTANCE_SCALE = 1 / 2; // The maximum line distance, in tile units, that fits in the buffer. const MAX_LINE_DISTANCE = Math.pow(2, LINE_DISTANCE_BUFFER_BITS - 1) / LINE_DISTANCE_SCALE; -const lineInterfaces = { - line: { - layoutVertexArrayType: new VertexArrayType([{ - name: 'a_pos', - components: 2, - type: 'Int16' - }, { - name: 'a_data', - components: 4, - type: 'Uint8' - }]), - paintAttributes: [{ - name: 'a_color', - components: 4, - type: 'Uint8', - getValue: (layer, globalProperties, featureProperties) => { - return layer.getPaintValue("line-color", globalProperties, featureProperties); - }, - multiplier: 255, - paintProperty: 'line-color' - }], - elementArrayType: new ElementArrayType() - } +const lineInterface = { + layoutVertexArrayType: new VertexArrayType([{ + name: 'a_pos', + components: 2, + type: 'Int16' + }, { + name: 'a_data', + components: 4, + type: 'Uint8' + }]), + paintAttributes: [{ + name: 'a_color', + components: 4, + type: 'Uint8', + getValue: (layer, globalProperties, featureProperties) => { + return layer.getPaintValue("line-color", globalProperties, featureProperties); + }, + multiplier: 255, + paintProperty: 'line-color' + }], + elementArrayType: new ElementArrayType() }; function addLineVertex(layoutVertexBuffer, point, extrude, tx, ty, dir, linesofar) { @@ -86,9 +84,8 @@ function addLineVertex(layoutVertexBuffer, point, extrude, tx, ty, dir, linesofa * @private */ class LineBucket extends Bucket { - - get programInterfaces() { - return lineInterfaces; + constructor(options) { + super(options, lineInterface); } addFeature(feature) { @@ -122,7 +119,7 @@ class LineBucket extends Bucket { lastVertex = vertices[len - 1], closed = firstVertex.equals(lastVertex); - const arrays = this.arrays.line; + const arrays = this.arrays; // we could be more precise, but it would only save a negligible amount of space const segment = arrays.prepareSegment('line', len * 10); @@ -372,7 +369,7 @@ class LineBucket extends Bucket { addCurrentVertex(currentVertex, distance, normal, endLeft, endRight, round, segment) { const tx = round ? 1 : 0; let extrude; - const arrays = this.arrays.line; + const arrays = this.arrays; const layoutVertexArray = arrays.layoutVertexArray; const elementArray = arrays.elementArray; @@ -421,7 +418,7 @@ class LineBucket extends Bucket { addPieSliceVertex(currentVertex, distance, extrude, lineTurnsLeft, segment) { const ty = lineTurnsLeft ? 1 : 0; extrude = extrude.mult(lineTurnsLeft ? -1 : 1); - const arrays = this.arrays.line; + const arrays = this.arrays; const layoutVertexArray = arrays.layoutVertexArray; const elementArray = arrays.elementArray; diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index e2d985b09cd..3c99c2987ff 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -1,8 +1,8 @@ 'use strict'; const Point = require('point-geometry'); - -const Bucket = require('../bucket'); +const ArrayGroup = require('../array_group'); +const BufferGroup = require('../buffer_group'); const VertexArrayType = require('../vertex_array_type'); const ElementArrayType = require('../element_array_type'); const EXTENT = require('../extent'); @@ -105,40 +105,30 @@ function addCollisionBoxVertex(layoutVertexArray, point, extrude, maxZoom, place placementZoom * 10); } -class SymbolBucket extends Bucket { +class SymbolBucket { constructor(options) { - super(options); - this.collisionBoxArray = options.collisionBoxArray; this.symbolQuadsArray = options.symbolQuadsArray; this.symbolInstancesArray = options.symbolInstancesArray; + this.zoom = options.zoom; + this.overscaling = options.overscaling; + this.layers = options.layers; this.sdfIcons = options.sdfIcons; this.iconsNeedLinear = options.iconsNeedLinear; this.adjustedTextSize = options.adjustedTextSize; this.adjustedIconSize = options.adjustedIconSize; this.fontstack = options.fontstack; - this.layer = this.layers[0]; - } - - get programInterfaces() { - return symbolInterfaces; - } - serialize() { - const serialized = Bucket.prototype.serialize.apply(this); - serialized.sdfIcons = this.sdfIcons; - serialized.iconsNeedLinear = this.iconsNeedLinear; - serialized.adjustedTextSize = this.adjustedTextSize; - serialized.adjustedIconSize = this.adjustedIconSize; - serialized.fontstack = this.fontstack; - return serialized; + if (options.arrays) { + this.buffers = util.mapObject(options.arrays, (arrays, key) => { + return new BufferGroup(symbolInterfaces[key], options.layers, options.zoom, options.arrays[key]); + }); + } } populate(features, options) { - this.recalculateStyleLayers(); - - const layout = this.layer.layout; + const layout = this.layers[0].layout; const textField = layout['text-field']; const textFont = layout['text-font']; const iconImage = layout['icon-image']; @@ -157,7 +147,7 @@ class SymbolBucket extends Bucket { const stack = stacks[textFont] = stacks[textFont] || {}; for (const feature of features) { - if (!this.layer.filter(feature)) { + if (!this.layers[0].filter(feature)) { continue; } @@ -202,16 +192,49 @@ class SymbolBucket extends Bucket { } } + isEmpty() { + return this.arrays.icon.isEmpty() && + this.arrays.glyph.isEmpty() && + this.arrays.collisionBox.isEmpty(); + } + + serialize(transferables) { + return { + zoom: this.zoom, + layerIds: this.layers.map((l) => l.id), + sdfIcons: this.sdfIcons, + iconsNeedLinear: this.iconsNeedLinear, + adjustedTextSize: this.adjustedTextSize, + adjustedIconSize: this.adjustedIconSize, + fontstack: this.fontstack, + arrays: util.mapObject(this.arrays, (a) => a.serialize(transferables)) + }; + } + + destroy() { + this.buffers.icon.destroy(); + this.buffers.glyph.destroy(); + this.buffers.collisionBox.destroy(); + } + + createArrays() { + this.arrays = util.mapObject(symbolInterfaces, (programInterface) => { + return new ArrayGroup(programInterface, this.layers, this.zoom); + }); + } + prepare(stacks, icons) { + this.createArrays(); + // To reduce the number of labels that jump around when zooming we need // to use a text-size value that is the same for all zoom levels. // This calculates text-size at a high zoom level so that all tiles can // use the same value when calculating anchor positions. const zoomHistory = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; - this.adjustedTextMaxSize = this.layer.getLayoutValue('text-size', {zoom: 18, zoomHistory: zoomHistory}); - this.adjustedTextSize = this.layer.getLayoutValue('text-size', {zoom: this.zoom + 1, zoomHistory: zoomHistory}); - this.adjustedIconMaxSize = this.layer.getLayoutValue('icon-size', {zoom: 18, zoomHistory: zoomHistory}); - this.adjustedIconSize = this.layer.getLayoutValue('icon-size', {zoom: this.zoom + 1, zoomHistory: zoomHistory}); + this.adjustedTextMaxSize = this.layers[0].getLayoutValue('text-size', {zoom: 18, zoomHistory: zoomHistory}); + this.adjustedTextSize = this.layers[0].getLayoutValue('text-size', {zoom: this.zoom + 1, zoomHistory: zoomHistory}); + this.adjustedIconMaxSize = this.layers[0].getLayoutValue('icon-size', {zoom: 18, zoomHistory: zoomHistory}); + this.adjustedIconSize = this.layers[0].getLayoutValue('icon-size', {zoom: this.zoom + 1, zoomHistory: zoomHistory}); const tileSize = 512 * this.overscaling; this.tilePixelRatio = EXTENT / tileSize; @@ -219,7 +242,7 @@ class SymbolBucket extends Bucket { this.iconsNeedLinear = false; this.symbolInstancesStartIndex = this.symbolInstancesArray.length; - const layout = this.layer.layout; + const layout = this.layers[0].layout; let horizontalAlign = 0.5, verticalAlign = 0.5; @@ -281,7 +304,7 @@ class SymbolBucket extends Bucket { } if (image.pixelRatio !== 1) { this.iconsNeedLinear = true; - } else if (layout['icon-rotate'] !== 0 || !this.layer.isLayoutValueFeatureConstant('icon-rotate')) { + } else if (layout['icon-rotate'] !== 0 || !this.layers[0].isLayoutValueFeatureConstant('icon-rotate')) { this.iconsNeedLinear = true; } } @@ -296,7 +319,7 @@ class SymbolBucket extends Bucket { addFeature(feature, shapedText, shapedIcon) { const lines = feature.geometry; - const layout = this.layer.layout; + const layout = this.layers[0].layout; const glyphSize = 24; @@ -378,7 +401,7 @@ class SymbolBucket extends Bucket { // be drawn across tile boundaries. Instead they need to be included in // the buffers for both tiles and clipped to tile boundaries at draw time. const addToBuffers = inside || mayOverlap; - this.addSymbolInstance(anchor, line, shapedText, shapedIcon, this.layer, + this.addSymbolInstance(anchor, line, shapedText, shapedIcon, this.layers[0], addToBuffers, this.symbolInstancesArray.length, this.collisionBoxArray, feature.index, feature.sourceLayerIndex, this.index, textBoxScale, textPadding, textAlongLine, iconBoxScale, iconPadding, iconAlongLine, {zoom: this.zoom}, feature.properties); @@ -422,14 +445,12 @@ class SymbolBucket extends Bucket { } place(collisionTile, showCollisionBoxes) { - this.recalculateStyleLayers(); - // Calculate which labels can be shown and when they can be shown and // create the bufers used for rendering. this.createArrays(); - const layout = this.layer.layout; + const layout = this.layers[0].layout; const maxScale = collisionTile.maxScale; @@ -507,14 +528,14 @@ class SymbolBucket extends Bucket { if (hasText) { collisionTile.insertCollisionFeature(textCollisionFeature, glyphScale, layout['text-ignore-placement']); if (glyphScale <= maxScale) { - this.addSymbols('glyph', symbolInstance.glyphQuadStartIndex, symbolInstance.glyphQuadEndIndex, glyphScale, layout['text-keep-upright'], textAlongLine, collisionTile.angle); + this.addSymbols(this.arrays.glyph, symbolInstance.glyphQuadStartIndex, symbolInstance.glyphQuadEndIndex, glyphScale, layout['text-keep-upright'], textAlongLine, collisionTile.angle); } } if (hasIcon) { collisionTile.insertCollisionFeature(iconCollisionFeature, iconScale, layout['icon-ignore-placement']); if (iconScale <= maxScale) { - this.addSymbols('icon', symbolInstance.iconQuadStartIndex, symbolInstance.iconQuadEndIndex, iconScale, layout['icon-keep-upright'], iconAlongLine, collisionTile.angle); + this.addSymbols(this.arrays.icon, symbolInstance.iconQuadStartIndex, symbolInstance.iconQuadEndIndex, iconScale, layout['icon-keep-upright'], iconAlongLine, collisionTile.angle); } } @@ -523,8 +544,7 @@ class SymbolBucket extends Bucket { if (showCollisionBoxes) this.addToDebugBuffers(collisionTile); } - addSymbols(programName, quadsStart, quadsEnd, scale, keepUpright, alongLine, placementAngle) { - const arrays = this.arrays[programName]; + addSymbols(arrays, quadsStart, quadsEnd, scale, keepUpright, alongLine, placementAngle) { const elementArray = arrays.elementArray; const layoutVertexArray = arrays.layoutVertexArray; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 1e3de33f94a..e3960376ccb 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -23,7 +23,7 @@ function drawCircles(painter, sourceCache, layer, coords) { const bucket = tile.getBucket(layer); if (!bucket) continue; - const buffers = bucket.bufferGroups.circle; + const buffers = bucket.buffers; const layerData = buffers.layerData[layer.id]; const programConfiguration = layerData.programConfiguration; const program = painter.useProgram('circle', programConfiguration); diff --git a/js/render/draw_collision_debug.js b/js/render/draw_collision_debug.js index 1cd363d6939..cb5b174ab43 100644 --- a/js/render/draw_collision_debug.js +++ b/js/render/draw_collision_debug.js @@ -22,7 +22,7 @@ function drawCollisionDebug(painter, sourceCache, layer, coords) { gl.uniform1f(program.u_zoom, painter.transform.zoom * 10); gl.uniform1f(program.u_maxzoom, (tile.coord.z + 1) * 10); - const buffers = bucket.bufferGroups.collisionBox; + const buffers = bucket.buffers.collisionBox; for (const segment of buffers.segments) { segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, null, segment.vertexOffset); gl.drawElements(gl.LINES, segment.primitiveLength * 2, gl.UNSIGNED_SHORT, segment.primitiveOffset * 2 * 2); diff --git a/js/render/draw_extrusion.js b/js/render/draw_extrusion.js index a04a630da9c..3617642e9d4 100644 --- a/js/render/draw_extrusion.js +++ b/js/render/draw_extrusion.js @@ -148,7 +148,7 @@ function drawExtrusion(painter, source, layer, coord) { const bucket = tile.getBucket(layer); if (!bucket) return; - const buffers = bucket.bufferGroups.fillextrusion; + const buffers = bucket.buffers; const gl = painter.gl; const image = layer.paint['fill-pattern']; diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index 0fbf32daccf..640c95626b5 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -51,7 +51,7 @@ function drawFill(painter, sourceCache, layer, coord) { const bucket = tile.getBucket(layer); if (!bucket) return; - const buffers = bucket.bufferGroups.fill; + const buffers = bucket.buffers; const gl = painter.gl; const image = layer.paint['fill-pattern']; @@ -94,7 +94,7 @@ function drawStroke(painter, sourceCache, layer, coord) { const bucket = tile.getBucket(layer); if (!bucket) return; - const buffers = bucket.bufferGroups.fill; + const buffers = bucket.buffers; const layerData = buffers.layerData[layer.id]; const gl = painter.gl; diff --git a/js/render/draw_line.js b/js/render/draw_line.js index e6bec76d380..7ece8c88f2d 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -34,7 +34,7 @@ function drawLineTile(painter, sourceCache, layer, coord) { const bucket = tile.getBucket(layer); if (!bucket) return; - const buffers = bucket.bufferGroups.line; + const buffers = bucket.buffers; const layerData = buffers.layerData[layer.id]; const gl = painter.gl; diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 14c15cfd9db..580637d03e4 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -84,7 +84,7 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, const tile = sourceCache.getTile(coords[j]); const bucket = tile.getBucket(layer); if (!bucket) continue; - const buffers = isText ? bucket.bufferGroups.glyph : bucket.bufferGroups.icon; + const buffers = isText ? bucket.buffers.glyph : bucket.buffers.icon; if (!buffers.segments.length) continue; painter.enableTileClippingMask(coords[j]); diff --git a/js/source/worker_tile.js b/js/source/worker_tile.js index 7fbe4bbfea3..13e405fc620 100644 --- a/js/source/worker_tile.js +++ b/js/source/worker_tile.js @@ -9,6 +9,8 @@ const SymbolInstancesArray = require('../symbol/symbol_instances'); const SymbolQuadsArray = require('../symbol/symbol_quads'); const assert = require('assert'); +const FAKE_ZOOM_HISTORY = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; + class WorkerTile { constructor(params) { this.coord = params.coord; @@ -82,8 +84,12 @@ class WorkerTile { if (layer.maxzoom && this.zoom >= layer.maxzoom) continue; if (layer.layout && layer.layout.visibility === 'none') continue; + for (const layer of family) { + layer.recalculate(this.zoom, FAKE_ZOOM_HISTORY); + } + const bucket = buckets[layer.id] = layer.createBucket({ - index: bucketIndex++, + index: bucketIndex, layers: family, zoom: this.zoom, overscaling: this.overscaling, @@ -93,7 +99,9 @@ class WorkerTile { }); bucket.populate(features, options); - featureIndex.bucketLayerIDs[bucket.index] = family.map((l) => l.id); + featureIndex.bucketLayerIDs[bucketIndex] = family.map((l) => l.id); + + bucketIndex++; } } @@ -139,6 +147,11 @@ class WorkerTile { deps++; if (deps === 2) { for (const bucket of this.symbolBuckets) { + // Layers are shared and may have been used by a WorkerTile with a different zoom. + for (const layer of bucket.layers) { + layer.recalculate(this.zoom, FAKE_ZOOM_HISTORY); + } + bucket.prepare(stacks, icons); bucket.place(collisionTile, this.showCollisionBoxes); } @@ -173,7 +186,13 @@ class WorkerTile { } const collisionTile = new CollisionTile(angle, pitch, this.collisionBoxArray); + for (const bucket of this.symbolBuckets) { + // Layers are shared and may have been used by a WorkerTile with a different zoom. + for (const layer of bucket.layers) { + layer.recalculate(this.zoom, FAKE_ZOOM_HISTORY); + } + bucket.place(collisionTile, showCollisionBoxes); } diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index 55c911fce40..08776bd177f 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -33,32 +33,32 @@ test('Bucket', (t) => { function create(options) { options = options || {}; + const programInterface = { + layoutVertexArrayType: new VertexArrayType(options.layoutAttributes || [{ + name: 'a_box', + components: 2, + type: 'Int16' + }]), + elementArrayType: new ElementArrayType(), + elementArrayType2: new ElementArrayType(2), + + paintAttributes: options.paintAttributes || [{ + name: 'a_map', + type: 'Int16', + getValue: function(layer, globalProperties, featureProperties) { + return [featureProperties.x]; + }, + paintProperty: 'circle-color' + }] + }; + class Class extends Bucket { - get programInterfaces() { - return { - test: { - layoutVertexArrayType: new VertexArrayType(options.layoutAttributes || [{ - name: 'a_box', - components: 2, - type: 'Int16' - }]), - elementArrayType: new ElementArrayType(), - elementArrayType2: new ElementArrayType(2), - - paintAttributes: options.paintAttributes || [{ - name: 'a_map', - type: 'Int16', - getValue: function(layer, globalProperties, featureProperties) { - return [featureProperties.x]; - }, - paintProperty: 'circle-color' - }] - } - }; + constructor(options) { + super(options, programInterface); } addFeature(feature) { - const arrays = this.arrays.test; + const arrays = this.arrays; const point = feature.loadGeometry()[0][0]; arrays.layoutVertexArray.emplaceBack(point.x * 2, point.y * 2); arrays.elementArray.emplaceBack(1, 2, 3); @@ -91,24 +91,24 @@ test('Bucket', (t) => { bucket.populate([createFeature(17, 42)], createOptions()); - const testVertex = bucket.arrays.test.layoutVertexArray; + const testVertex = bucket.arrays.layoutVertexArray; t.equal(testVertex.length, 1); const v0 = testVertex.get(0); t.equal(v0.a_box0, 34); t.equal(v0.a_box1, 84); - const paintVertex = bucket.arrays.test.layerData.layerid.paintVertexArray; + const paintVertex = bucket.arrays.layerData.layerid.paintVertexArray; t.equal(paintVertex.length, 1); const p0 = paintVertex.get(0); t.equal(p0.a_map, 17); - const testElement = bucket.arrays.test.elementArray; + const testElement = bucket.arrays.elementArray; t.equal(testElement.length, 1); const e1 = testElement.get(0); t.equal(e1.vertices0, 1); t.equal(e1.vertices1, 2); t.equal(e1.vertices2, 3); - const testElement2 = bucket.arrays.test.elementArray2; + const testElement2 = bucket.arrays.elementArray2; t.equal(testElement2.length, 1); const e2 = testElement2.get(0); t.equal(e2.vertices0, 17); @@ -125,9 +125,9 @@ test('Bucket', (t) => { bucket.populate([createFeature(17, 42)], createOptions()); - const v0 = bucket.arrays.test.layoutVertexArray.get(0); - const a0 = bucket.arrays.test.layerData.one.paintVertexArray.get(0); - const b0 = bucket.arrays.test.layerData.two.paintVertexArray.get(0); + const v0 = bucket.arrays.layoutVertexArray.get(0); + const a0 = bucket.arrays.layerData.one.paintVertexArray.get(0); + const b0 = bucket.arrays.layerData.two.paintVertexArray.get(0); t.equal(a0.a_map, 17); t.equal(b0.a_map, 17); t.equal(v0.a_box0, 34); @@ -152,9 +152,9 @@ test('Bucket', (t) => { bucket.populate([createFeature(17, 42)], createOptions()); - t.equal(bucket.arrays.test.layoutVertexArray.bytesPerElement, 0); + t.equal(bucket.arrays.layoutVertexArray.bytesPerElement, 0); t.deepEqual( - bucket.arrays.test.layerData.one.programConfiguration.uniforms[0].getValue.call(bucket), + bucket.arrays.layerData.one.programConfiguration.uniforms[0].getValue.call(bucket), [5] ); @@ -172,31 +172,16 @@ test('Bucket', (t) => { bucket.populate([createFeature(17, 42)], createOptions()); - const v0 = bucket.arrays.test.layoutVertexArray.get(0); + const v0 = bucket.arrays.layoutVertexArray.get(0); t.equal(v0.a_map, 34); t.end(); }); - t.test('reset buffers', (t) => { - const bucket = create(); - - bucket.populate([createFeature(17, 42)], createOptions()); - - t.notEqual(bucket.arrays.test.layoutVertexArray.length, 0); - bucket.createArrays(); - t.equal(bucket.arrays.test.layoutVertexArray.length, 0); - - t.end(); - }); - t.test('isEmpty', (t) => { const bucket = create(); t.ok(bucket.isEmpty()); - bucket.createArrays(); - t.ok(bucket.isEmpty()); - bucket.populate([createFeature(17, 42)], createOptions()); t.ok(!bucket.isEmpty()); @@ -211,43 +196,10 @@ test('Bucket', (t) => { bucket.serialize(transferables); t.equal(4, transferables.length); - t.equal(bucket.arrays.test.layoutVertexArray.arrayBuffer, transferables[0]); - t.equal(bucket.arrays.test.elementArray.arrayBuffer, transferables[1]); - t.equal(bucket.arrays.test.elementArray2.arrayBuffer, transferables[2]); - t.equal(bucket.arrays.test.layerData.layerid.paintVertexArray.arrayBuffer, transferables[3]); - - t.end(); - }); - - t.test('add features after resetting buffers', (t) => { - const bucket = create(); - - bucket.populate([createFeature(1, 5)], createOptions()); - bucket.createArrays(); - bucket.populate([createFeature(17, 42)], createOptions()); - - const testVertex = bucket.arrays.test.layoutVertexArray; - t.equal(testVertex.length, 1); - const v0 = testVertex.get(0); - t.equal(v0.a_box0, 34); - t.equal(v0.a_box1, 84); - const testPaintVertex = bucket.arrays.test.layerData.layerid.paintVertexArray; - t.equal(testPaintVertex.length, 1); - const p0 = testPaintVertex.get(0); - t.equal(p0.a_map, 17); - - const testElement = bucket.arrays.test.elementArray; - t.equal(testElement.length, 1); - const e1 = testElement.get(0); - t.equal(e1.vertices0, 1); - t.equal(e1.vertices1, 2); - t.equal(e1.vertices2, 3); - - const testElement2 = bucket.arrays.test.elementArray2; - t.equal(testElement2.length, 1); - const e2 = testElement2.get(0); - t.equal(e2.vertices0, 17); - t.equal(e2.vertices1, 42); + t.equal(bucket.arrays.layoutVertexArray.arrayBuffer, transferables[0]); + t.equal(bucket.arrays.elementArray.arrayBuffer, transferables[1]); + t.equal(bucket.arrays.elementArray2.arrayBuffer, transferables[2]); + t.equal(bucket.arrays.layerData.layerid.paintVertexArray.arrayBuffer, transferables[3]); t.end(); }); @@ -258,36 +210,5 @@ test('Bucket', (t) => { t.end(); }); - t.test('add features', (t) => { - const bucket = create(); - - bucket.populate([createFeature(17, 42)], createOptions()); - - const testVertex = bucket.arrays.test.layoutVertexArray; - t.equal(testVertex.length, 1); - const v0 = testVertex.get(0); - t.equal(v0.a_box0, 34); - t.equal(v0.a_box1, 84); - const testPaintVertex = bucket.arrays.test.layerData.layerid.paintVertexArray; - t.equal(testPaintVertex.length, 1); - const p0 = testPaintVertex.get(0); - t.equal(p0.a_map, 17); - - const testElement = bucket.arrays.test.elementArray; - t.equal(testElement.length, 1); - const e1 = testElement.get(0); - t.equal(e1.vertices0, 1); - t.equal(e1.vertices1, 2); - t.equal(e1.vertices2, 3); - - const testElement2 = bucket.arrays.test.elementArray2; - t.equal(testElement2.length, 1); - const e2 = testElement2.get(0); - t.equal(e2.vertices0, 17); - t.equal(e2.vertices1, 42); - - t.end(); - }); - t.end(); }); diff --git a/test/js/data/fill_bucket.test.js b/test/js/data/fill_bucket.test.js index 6f51947a222..6860cc86d10 100644 --- a/test/js/data/fill_bucket.test.js +++ b/test/js/data/fill_bucket.test.js @@ -33,7 +33,6 @@ function createPolygon(numPoints) { test('FillBucket', (t) => { const layer = new StyleLayer({ id: 'test', type: 'fill', layout: {} }); const bucket = new FillBucket({ layers: [layer] }); - bucket.createArrays(); bucket.addFeature(createFeature([[ new Point(0, 0), @@ -71,7 +70,6 @@ test('FillBucket segmentation', (t) => { layer.updatePaintTransition('fill-color', [], {}); const bucket = new FillBucket({ layers: [layer] }); - bucket.createArrays(); // first add an initial, small feature to make sure the next one starts at // a non-zero offset @@ -83,7 +81,7 @@ test('FillBucket segmentation', (t) => { createPolygon(128) ])); - const arrays = bucket.arrays.fill; + const arrays = bucket.arrays; // Each polygon must fit entirely within a segment, so we expect the // first segment to include the first feature and the first polygon diff --git a/test/js/data/line_bucket.test.js b/test/js/data/line_bucket.test.js index 5881d8bfff2..b580749efd4 100644 --- a/test/js/data/line_bucket.test.js +++ b/test/js/data/line_bucket.test.js @@ -16,7 +16,6 @@ const feature = vt.layers.road.feature(0); test('LineBucket', (t) => { const layer = new StyleLayer({ id: 'test', type: 'line', layout: {} }); const bucket = new LineBucket({ layers: [layer] }); - bucket.createArrays(); const pointWithScale = new Point(0, 0); pointWithScale.scale = 10; diff --git a/test/js/data/symbol_bucket.test.js b/test/js/data/symbol_bucket.test.js index 865a3f1e68b..2678102e3cf 100644 --- a/test/js/data/symbol_bucket.test.js +++ b/test/js/data/symbol_bucket.test.js @@ -92,3 +92,15 @@ test('SymbolBucket integer overflow', (t) => { t.ok(util.warnOnce.getCall(1).calledWithMatch(/Too many (symbols|glyphs) being rendered in a tile./)); t.end(); }); + +test('SymbolBucket redo placement', (t) => { + const bucket = bucketSetup(); + const options = {iconDependencies: {}, glyphDependencies: {}}; + + bucket.populate([feature], options); + bucket.prepare(stacks, {}); + bucket.place(collision); + bucket.place(collision); + + t.end(); +}); diff --git a/test/js/source/worker_tile.test.js b/test/js/source/worker_tile.test.js index 90a42d0944e..5949872f5e8 100644 --- a/test/js/source/worker_tile.test.js +++ b/test/js/source/worker_tile.test.js @@ -44,10 +44,6 @@ test('WorkerTile#parse', (t) => { test('WorkerTile#parse skips hidden layers', (t) => { const layerIndex = new StyleLayerIndex([{ - id: 'test', - source: 'source', - type: 'circle' - }, { id: 'test-hidden', source: 'source', type: 'fill', @@ -57,7 +53,7 @@ test('WorkerTile#parse skips hidden layers', (t) => { const tile = createWorkerTile(); tile.parse(createWrapper(), layerIndex, {}, (err, result) => { t.ifError(err); - t.equal(Object.keys(result.buckets[0].arrays).length, 1); + t.equal(result.buckets.length, 0); t.end(); }); });