Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use stencil test instead of tile mask approach #9012

Merged
merged 2 commits into from
Nov 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 10 additions & 15 deletions src/render/draw_hillshade.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ function drawHillshade(painter: Painter, sourceCache: SourceCache, layer: Hillsh
const sourceMaxZoom = sourceCache.getSource().maxzoom;

const depthMode = painter.depthModeForSublayer(0, DepthMode.ReadOnly);
const stencilMode = StencilMode.disabled;
const colorMode = painter.colorModeForRenderPass();

for (const tileID of tileIDs) {
const tile = sourceCache.getTile(tileID);
const [stencilModes, coords] = painter.renderPass === 'translucent' ?
painter.stencilConfigForOverlap(tileIDs) : [{}, tileIDs];

for (const coord of coords) {
const tile = sourceCache.getTile(coord);
if (tile.needsHillshadePrepare && painter.renderPass === 'offscreen') {
prepareHillshade(painter, tile, layer, sourceMaxZoom, depthMode, stencilMode, colorMode);
continue;
prepareHillshade(painter, tile, layer, sourceMaxZoom, depthMode, StencilMode.disabled, colorMode);
} else if (painter.renderPass === 'translucent') {
renderHillshade(painter, tile, layer, depthMode, stencilMode, colorMode);
renderHillshade(painter, tile, layer, depthMode, stencilModes[coord.overscaledZ], colorMode);
}
}

Expand All @@ -52,15 +53,9 @@ function renderHillshade(painter, tile, layer, depthMode, stencilMode, colorMode

const uniformValues = hillshadeUniformValues(painter, tile, layer);

if (tile.maskedBoundsBuffer && tile.maskedIndexBuffer && tile.segments) {
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
uniformValues, layer.id, tile.maskedBoundsBuffer,
tile.maskedIndexBuffer, tile.segments);
} else {
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
uniformValues, layer.id, painter.rasterBoundsBuffer,
painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments);
}
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
uniformValues, layer.id, painter.rasterBoundsBuffer,
painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments);
}

// hillshade rendering is done in two steps. the prepare step first calculates the slope of the terrain in the x and y
Expand Down
20 changes: 10 additions & 10 deletions src/render/draw_raster.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@ import type {OverscaledTileID} from '../source/tile_id';

export default drawRaster;

function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, coords: Array<OverscaledTileID>) {
function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array<OverscaledTileID>) {
if (painter.renderPass !== 'translucent') return;
if (layer.paint.get('raster-opacity') === 0) return;
if (!tileIDs.length) return;

const context = painter.context;
const gl = context.gl;
const source = sourceCache.getSource();
const program = painter.useProgram('raster');

const stencilMode = StencilMode.disabled;
const colorMode = painter.colorModeForRenderPass();
const minTileZ = coords.length && coords[0].overscaledZ;

const [stencilModes, coords] = source instanceof ImageSource ? [{}, tileIDs] :
painter.stencilConfigForOverlap(tileIDs);

const minTileZ = coords[coords.length - 1].overscaledZ;

const align = !painter.options.moving;
for (const coord of coords) {
// Set the lower zoom level to sublayer 0, and higher zoom levels to higher sublayers
Expand Down Expand Up @@ -64,16 +69,11 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
const uniformValues = rasterUniformValues(posMatrix, parentTL || [0, 0], parentScaleBy || 1, fade, layer);

if (source instanceof ImageSource) {
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled,
uniformValues, layer.id, source.boundsBuffer,
painter.quadTriangleIndexBuffer, source.boundsSegments);
} else if (tile.maskedBoundsBuffer && tile.maskedIndexBuffer && tile.segments) {
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
uniformValues, layer.id, tile.maskedBoundsBuffer,
tile.maskedIndexBuffer, tile.segments, layer.paint,
painter.transform.zoom);
} else {
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.disabled,
uniformValues, layer.id, painter.rasterBoundsBuffer,
painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments);
}
Expand Down
40 changes: 30 additions & 10 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import StencilMode from '../gl/stencil_mode';
import ColorMode from '../gl/color_mode';
import CullFaceMode from '../gl/cull_face_mode';
import Texture from './texture';
import updateTileMasks from './tile_mask';
import {clippingMaskUniformValues} from './program/clipping_mask_program';
import Color from '../style-spec/util/color';
import symbol from './draw_symbol';
Expand Down Expand Up @@ -296,6 +295,36 @@ class Painter {
return new StencilMode({func: gl.EQUAL, mask: 0xFF}, this._tileClippingMaskIDs[tileID.key], 0x00, gl.KEEP, gl.KEEP, gl.REPLACE);
}

/*
* Sort coordinates by Z as drawing tiles is done in Z-descending order.
* All children with the same Z write the same stencil value. Children
* stencil values are greater than parent's. This is used only for raster
* and raster-dem tiles, which are already clipped to tile boundaries, to
* mask area of tile overlapped by children tiles.
* Stencil ref values continue range used in _tileClippingMaskIDs.
*
* Returns [StencilMode for tile overscaleZ map, sortedCoords].
*/
stencilConfigForOverlap(tileIDs: Array<OverscaledTileID>): [{[number]: $ReadOnly<StencilMode>}, Array<OverscaledTileID>] {
const gl = this.context.gl;
const coords = tileIDs.sort((a, b) => b.overscaledZ - a.overscaledZ);
const minTileZ = coords[coords.length - 1].overscaledZ;
const stencilValues = coords[0].overscaledZ - minTileZ + 1;
astojilj marked this conversation as resolved.
Show resolved Hide resolved
if (stencilValues > 1) {
this.currentStencilSource = undefined;
if (this.nextStencilID + stencilValues > 256) {
this.clearStencil();
}
const zToStencilMode = {};
for (let i = 0; i < stencilValues; i++) {
zToStencilMode[i + minTileZ] = new StencilMode({func: gl.GEQUAL, mask: 0xFF}, i + this.nextStencilID, 0xFF, gl.KEEP, gl.KEEP, gl.REPLACE);
}
this.nextStencilID += stencilValues;
return [zToStencilMode, coords];
}
return [{[minTileZ]: StencilMode.disabled}, coords];
}

colorModeForRenderPass(): $ReadOnly<ColorMode> {
const gl = this.context.gl;
if (this._showOverdrawInspector) {
Expand Down Expand Up @@ -360,15 +389,6 @@ class Painter {
coordsDescendingSymbol[id] = sourceCache.getVisibleCoordinates(true).reverse();
}

for (const id in sourceCaches) {
const sourceCache = sourceCaches[id];
const source = sourceCache.getSource();
if (source.type !== 'raster' && source.type !== 'raster-dem') continue;
const visibleTiles = [];
for (const coord of coordsAscending[id]) visibleTiles.push(sourceCache.getTile(coord));
updateTileMasks(visibleTiles, this.context);
}

this.opaquePassCutoff = Infinity;
for (let i = 0; i < layerIds.length; i++) {
const layerId = layerIds[i];
Expand Down
104 changes: 0 additions & 104 deletions src/render/tile_mask.js

This file was deleted.

1 change: 0 additions & 1 deletion src/source/geojson_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ class GeoJSONSource extends Evented implements Source {

unloadTile(tile: Tile) {
tile.unloadVectorData();
tile.clearMask();
this.actor.send('removeTile', {uid: tile.uid, type: this.type, source: this.id});
}

Expand Down
1 change: 0 additions & 1 deletion src/source/raster_dem_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ class RasterDEMTileSource extends RasterTileSource implements Source {
}
if (tile.dem) delete tile.dem;
delete tile.neighboringTiles;
tile.clearMask();

tile.state = 'unloaded';
if (tile.actor) {
Expand Down
1 change: 0 additions & 1 deletion src/source/raster_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ class RasterTileSource extends Evented implements Source {

unloadTile(tile: Tile, callback: Callback<void>) {
if (tile.texture) this.map.painter.saveTileTexture(tile.texture);
tile.clearMask();
callback();
}

Expand Down
82 changes: 2 additions & 80 deletions src/source/tile.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
// @flow

import {uniqueId, deepEqual, parseCacheControl} from '../util/util';
import {uniqueId, parseCacheControl} from '../util/util';
import {deserialize as deserializeBucket} from '../data/bucket';
import FeatureIndex from '../data/feature_index';
import GeoJSONFeature from '../util/vectortile_to_geojson';
import featureFilter from '../style-spec/feature_filter';
import SymbolBucket from '../data/bucket/symbol_bucket';
import {RasterBoundsArray, CollisionBoxArray} from '../data/array_types';
import rasterBoundsAttributes from '../data/raster_bounds_attributes';
import EXTENT from '../data/extent';
import Point from '@mapbox/point-geometry';
import {CollisionBoxArray} from '../data/array_types';
import Texture from '../render/texture';
import SegmentVector from '../data/segment';
import {TriangleIndexArray} from '../data/index_array_type';
import browser from '../util/browser';
import EvaluationParameters from '../style/evaluation_parameters';
import SourceFeatureState from '../source/source_state';
Expand All @@ -27,10 +22,7 @@ import type DEMData from '../data/dem_data';
import type {AlphaImage} from '../util/image';
import type ImageAtlas from '../render/image_atlas';
import type ImageManager from '../render/image_manager';
import type {Mask} from '../render/tile_mask';
import type Context from '../gl/context';
import type IndexBuffer from '../gl/index_buffer';
import type VertexBuffer from '../gl/vertex_buffer';
import type {OverscaledTileID} from './tile_id';
import type Framebuffer from '../gl/framebuffer';
import type Transform from '../geo/transform';
Expand Down Expand Up @@ -76,14 +68,10 @@ class Tile {
placementSource: any;
actor: ?Actor;
vtLayers: {[string]: VectorTileLayer};
mask: Mask;

neighboringTiles: ?Object;
dem: ?DEMData;
aborted: ?boolean;
maskedBoundsBuffer: ?VertexBuffer;
maskedIndexBuffer: ?IndexBuffer;
segments: ?SegmentVector;
needsHillshadePrepare: ?boolean;
request: ?Cancelable;
texture: any;
Expand Down Expand Up @@ -322,72 +310,6 @@ class Tile {
}
}

clearMask() {
if (this.segments) {
this.segments.destroy();
delete this.segments;
}
if (this.maskedBoundsBuffer) {
this.maskedBoundsBuffer.destroy();
delete this.maskedBoundsBuffer;
}
if (this.maskedIndexBuffer) {
this.maskedIndexBuffer.destroy();
delete this.maskedIndexBuffer;
}

delete this.mask;
}

setMask(mask: Mask, context: Context) {

// don't redo buffer work if the mask is the same;
if (deepEqual(this.mask, mask)) return;

this.clearMask();
this.mask = mask;

// We want to render the full tile, and keeping the segments/vertices/indices empty means
// using the global shared buffers for covering the entire tile.
if (deepEqual(mask, {'0': true})) return;

const maskedBoundsArray = new RasterBoundsArray();
const indexArray = new TriangleIndexArray();

this.segments = new SegmentVector();
// Create a new segment so that we will upload (empty) buffers even when there is nothing to
// draw for this tile.
this.segments.prepareSegment(0, maskedBoundsArray, indexArray);

const maskArray = Object.keys(mask);
for (let i = 0; i < maskArray.length; i++) {
const maskCoord = mask[+maskArray[i]];
const vertexExtent = EXTENT >> maskCoord.z;
const tlVertex = new Point(maskCoord.x * vertexExtent, maskCoord.y * vertexExtent);
const brVertex = new Point(tlVertex.x + vertexExtent, tlVertex.y + vertexExtent);

// not sure why flow is complaining here because it doesn't complain at L401
const segment = (this.segments: any).prepareSegment(4, maskedBoundsArray, indexArray);

maskedBoundsArray.emplaceBack(tlVertex.x, tlVertex.y, tlVertex.x, tlVertex.y);
maskedBoundsArray.emplaceBack(brVertex.x, tlVertex.y, brVertex.x, tlVertex.y);
maskedBoundsArray.emplaceBack(tlVertex.x, brVertex.y, tlVertex.x, brVertex.y);
maskedBoundsArray.emplaceBack(brVertex.x, brVertex.y, brVertex.x, brVertex.y);

const offset = segment.vertexLength;
// 0, 1, 2
// 1, 2, 3
indexArray.emplaceBack(offset, offset + 1, offset + 2);
indexArray.emplaceBack(offset + 1, offset + 2, offset + 3);

segment.vertexLength += 4;
segment.primitiveLength += 2;
}

this.maskedBoundsBuffer = context.createVertexBuffer(maskedBoundsArray, rasterBoundsAttributes.members);
this.maskedIndexBuffer = context.createIndexBuffer(indexArray);
}

hasData() {
return this.state === 'loaded' || this.state === 'reloading' || this.state === 'expired';
}
Expand Down
Loading