Skip to content

Commit

Permalink
Increase precision of line progress when needed
Browse files Browse the repository at this point in the history
  • Loading branch information
karimnaaji committed May 27, 2020
1 parent 588ad5a commit 2c7ad46
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 29 deletions.
9 changes: 9 additions & 0 deletions src/data/bucket/line_attributes_ext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @flow
import {createLayout} from '../../util/struct_array';

const lineLayoutAttributesExt = createLayout([
{name: 'a_line_progress', components: 1, type: 'Float32'}
], 4);

export default lineLayoutAttributesExt;
export const {members, size, alignment} = lineLayoutAttributesExt;
87 changes: 60 additions & 27 deletions src/data/bucket/line_bucket.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// @flow

import {LineLayoutArray} from '../array_types';
import {LineLayoutArray, StructArrayLayout1f4} from '../array_types';

import {members as layoutAttributes} from './line_attributes';
import {members as layoutAttributesExt} from './line_attributes_ext';
import SegmentVector from '../segment';
import {ProgramConfigurationSet} from '../program_configuration';
import {TriangleIndexArray} from '../index_array_type';
Expand Down Expand Up @@ -67,6 +68,11 @@ 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;

type LineClips = {
clipStart: number;
clipEnd: number;
}

/**
* @private
*/
Expand All @@ -75,8 +81,7 @@ class LineBucket implements Bucket {
totalDistance: number;
maxLineLength: number;
scaledDistance: number;
clipStart: number;
clipEnd: number;
lineClips: ?LineClips;

e1: number;
e2: number;
Expand All @@ -92,11 +97,14 @@ class LineBucket implements Bucket {

layoutVertexArray: LineLayoutArray;
layoutVertexBuffer: VertexBuffer;
layoutVertexArray2: StructArrayLayout1f4;
layoutVertexBuffer2: VertexBuffer;

indexArray: TriangleIndexArray;
indexBuffer: IndexBuffer;

hasPattern: boolean;
requiresHighPrecisionLineProgress: boolean;
programConfigurations: ProgramConfigurationSet<LineStyleLayer>;
segments: SegmentVector;
uploaded: boolean;
Expand All @@ -108,9 +116,11 @@ class LineBucket implements Bucket {
this.layerIds = this.layers.map(layer => layer.id);
this.index = options.index;
this.hasPattern = false;
this.requiresHighPrecisionLineProgress = false;
this.patternFeatures = [];

this.layoutVertexArray = new LineLayoutArray();
this.layoutVertexArray2 = new StructArrayLayout1f4();
this.indexArray = new TriangleIndexArray();
this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom);
this.segments = new SegmentVector();
Expand Down Expand Up @@ -160,6 +170,7 @@ class LineBucket implements Bucket {
});
}

this.requiresHighPrecisionLineProgress = this._requiresHighPrecisionLineProgress(bucketFeatures);
for (const bucketFeature of bucketFeatures) {
const {geometry, index, sourceLayerIndex} = bucketFeature;

Expand Down Expand Up @@ -198,6 +209,9 @@ class LineBucket implements Bucket {

upload(context: Context) {
if (!this.uploaded) {
if (this.layoutVertexArray2.length !== 0) {
this.layoutVertexBuffer2 = context.createVertexBuffer(this.layoutVertexArray2, layoutAttributesExt);
}
this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, layoutAttributes);
this.indexBuffer = context.createIndexBuffer(this.indexArray);
}
Expand All @@ -213,12 +227,40 @@ class LineBucket implements Bucket {
this.segments.destroy();
}

lineFeatureClips(feature: BucketFeature): ?LineClips {
if (!!feature.properties && feature.properties.hasOwnProperty('mapbox_clip_start') && feature.properties.hasOwnProperty('mapbox_clip_end')) {
const clipStart = +feature.properties['mapbox_clip_start'];
const clipEnd = +feature.properties['mapbox_clip_end'];
return {clipStart, clipEnd};
}
}

_requiresHighPrecisionLineProgress(features: Array<BucketFeature>) {
for (const bucketFeature of features) {
const lineClips = this.lineFeatureClips(bucketFeature);
if (!lineClips) continue;

let totalFeatureDistance = 0;
for (const line of bucketFeature.geometry) {
for (let i = 0; i < line.length - 1; i++) {
totalFeatureDistance += line[i].dist(line[i + 1]);
}
}
const clipDiff = lineClips.clipEnd - lineClips.clipStart;
if (totalFeatureDistance / clipDiff > MAX_LINE_DISTANCE) {
return true;
}
}
return false;
}

addFeature(feature: BucketFeature, geometry: Array<Array<Point>>, index: number, canonical: CanonicalTileID, imagePositions: {[_: string]: ImagePosition}) {
const layout = this.layers[0].layout;
const join = layout.get('line-join').evaluate(feature, {});
const cap = layout.get('line-cap');
const miterLimit = layout.get('line-miter-limit');
const roundLimit = layout.get('line-round-limit');
this.lineClips = this.lineFeatureClips(feature);

for (const line of geometry) {
this.addLine(line, feature, join, cap, miterLimit, roundLimit);
Expand All @@ -232,19 +274,14 @@ class LineBucket implements Bucket {
this.scaledDistance = 0;
this.totalDistance = 0;

if (!!feature.properties &&
feature.properties.hasOwnProperty('mapbox_clip_start') &&
feature.properties.hasOwnProperty('mapbox_clip_end')) {

this.clipStart = +feature.properties['mapbox_clip_start'];
this.clipEnd = +feature.properties['mapbox_clip_end'];

if (this.lineClips) {
// Calculate the total distance, in tile units, of this tiled line feature
for (let i = 0; i < vertices.length - 1; i++) {
this.totalDistance += vertices[i].dist(vertices[i + 1]);
}
this.updateScaledDistance();
this.maxLineLength = Math.max(this.maxLineLength, this.totalDistance / (this.clipEnd - this.clipStart));
//$FlowFixMe
this.maxLineLength = Math.max(this.maxLineLength, this.totalDistance / (this.lineClips.clipEnd - this.lineClips.clipStart));
}

const isPolygon = vectorTileFeatureTypes[feature.type] === 'Polygon';
Expand Down Expand Up @@ -495,20 +532,11 @@ class LineBucket implements Bucket {

this.addHalfVertex(p, leftX, leftY, round, false, endLeft, segment);
this.addHalfVertex(p, rightX, rightY, round, true, -endRight, segment);

// There is a maximum "distance along the line" that we can store in the buffers.
// When we get close to the distance, reset it to zero and add the vertex again with
// a distance of zero. The max distance is determined by the number of bits we allocate
// to `linesofar`.
if (this.distance > MAX_LINE_DISTANCE / 2 && this.totalDistance === 0) {
this.distance = 0;
this.addCurrentVertex(p, normal, endLeft, endRight, segment, round);
}
}

addHalfVertex({x, y}: Point, extrudeX: number, extrudeY: number, round: boolean, up: boolean, dir: number, segment: Segment) {
// scale down so that we can store longer distances while sacrificing precision.
const linesofar = this.scaledDistance * LINE_DISTANCE_SCALE;
const linesofarScaled = (this.totalDistance > 0 ? this.scaledDistance * (MAX_LINE_DISTANCE - 1) : this.scaledDistance) * LINE_DISTANCE_SCALE;

this.layoutVertexArray.emplaceBack(
// a_pos_normal
Expand All @@ -520,11 +548,16 @@ class LineBucket implements Bucket {
Math.round(EXTRUDE_SCALE * extrudeX) + 128,
Math.round(EXTRUDE_SCALE * extrudeY) + 128,
// Encode the -1/0/1 direction value into the first two bits of .z of a_data.
// Combine it with the lower 6 bits of `linesofar` (shifted by 2 bites to make
// room for the direction value). The upper 8 bits of `linesofar` are placed in
// Combine it with the lower 6 bits of `linesofarScaled` (shifted by 2 bits to make
// room for the direction value). The upper 8 bits of `linesofarScaled` are placed in
// the `w` component.
((dir === 0 ? 0 : (dir < 0 ? -1 : 1)) + 1) | ((linesofar & 0x3F) << 2),
linesofar >> 6);
((dir === 0 ? 0 : (dir < 0 ? -1 : 1)) + 1) | ((linesofarScaled & 0x3F) << 2),
linesofarScaled >> 6);

// Constructs a second vertex buffer with higher precision line progress
if (this.requiresHighPrecisionLineProgress) {
this.layoutVertexArray2.emplaceBack(this.scaledDistance);
}

const e = segment.vertexLength++;
if (this.e1 >= 0 && this.e2 >= 0) {
Expand All @@ -543,8 +576,8 @@ class LineBucket implements Bucket {
// as the total distance (in tile units) of this tiled feature, and the distance
// (in tile units) of the current vertex, we can determine the relative distance
// of this vertex along the full linestring feature and scale it to [0, 2^15)
this.scaledDistance = this.totalDistance > 0 ?
(this.clipStart + (this.clipEnd - this.clipStart) * this.distance / this.totalDistance) * (MAX_LINE_DISTANCE - 1) :
this.scaledDistance = this.lineClips ?
this.lineClips.clipStart + (this.lineClips.clipEnd - this.lineClips.clipStart) * this.distance / this.totalDistance :
this.distance;
}

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 @@ -95,7 +95,7 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay
program.draw(context, gl.TRIANGLES, depthMode,
painter.stencilModeForClipping(coord), colorMode, CullFaceMode.disabled, uniformValues,
layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments,
layer.paint, painter.transform.zoom, programConfiguration);
layer.paint, painter.transform.zoom, programConfiguration, bucket.layoutVertexBuffer2);

firstTile = false;
// once refactored so that bound texture state is managed, we'll also be able to remove this firstTile/programChanged logic
Expand Down
3 changes: 2 additions & 1 deletion src/shaders/line_gradient.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

attribute vec2 a_pos_normal;
attribute vec4 a_data;
attribute float a_line_progress;

uniform mat4 u_matrix;
uniform mediump float u_ratio;
Expand Down Expand Up @@ -43,7 +44,7 @@ void main() {
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;

v_lineprogress = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0 / MAX_LINE_DISTANCE;
v_lineprogress = a_line_progress != 0.0 ? a_line_progress : (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0 / MAX_LINE_DISTANCE;

vec2 pos = floor(a_pos_normal * 0.5);

Expand Down

0 comments on commit 2c7ad46

Please sign in to comment.