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

Globe - clipping fix #4146

Merged
merged 14 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
2 changes: 1 addition & 1 deletion src/data/bucket/circle_bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class CircleBucket<Layer extends CircleStyleLayer | HeatmapStyleLayer> im
for (let x = 0; x < verticesPerAxis - 1; x++) {
const lowerIndex = index + y * verticesPerAxis + x;
const upperIndex = index + (y + 1) * verticesPerAxis + x;
this.indexArray.emplaceBack(lowerIndex, lowerIndex + 1, upperIndex + 1);
this.indexArray.emplaceBack(lowerIndex, upperIndex + 1, lowerIndex + 1);
this.indexArray.emplaceBack(lowerIndex, upperIndex, upperIndex + 1);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/data/bucket/line_bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ export class LineBucket implements Bucket {

const e = segment.vertexLength++;
if (this.e1 >= 0 && this.e2 >= 0) {
this.indexArray.emplaceBack(this.e1, this.e2, e);
this.indexArray.emplaceBack(this.e1, e, this.e2);
segment.primitiveLength++;
}
if (up) {
Expand Down
4 changes: 2 additions & 2 deletions src/data/bucket/symbol_bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ export class SymbolBucket implements Bucket {

addDynamicAttributes(arrays.dynamicLayoutVertexArray, labelAnchor, angle);

indexArray.emplaceBack(index, index + 1, index + 2);
indexArray.emplaceBack(index, index + 2, index + 1);
indexArray.emplaceBack(index + 1, index + 2, index + 3);

segment.vertexLength += 4;
Expand Down Expand Up @@ -859,7 +859,7 @@ export class SymbolBucket implements Bucket {

const endIndex = placedSymbol.vertexStartIndex + placedSymbol.numGlyphs * 4;
for (let vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
iconOrText.indexArray.emplaceBack(vertexIndex, vertexIndex + 1, vertexIndex + 2);
iconOrText.indexArray.emplaceBack(vertexIndex, vertexIndex + 2, vertexIndex + 1);
iconOrText.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/geo/projection/globe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,11 +474,11 @@ export class GlobeProjection implements Projection {
return `${granularity.toString(36)}_${border ? 'b' : ''}${north ? 'n' : ''}${south ? 's' : ''}`;
}

public getMeshFromTileID(context: Context, canonical: CanonicalTileID, hasBorder: boolean): Mesh {
public getMeshFromTileID(context: Context, canonical: CanonicalTileID, hasBorder: boolean, allowPoles: boolean): Mesh {
// Stencil granularity must match fill granularity
const granularity = granularitySettingsGlobe.fill.getGranularityForZoomLevel(canonical.z);
const north = (canonical.y === 0);
const south = (canonical.y === (1 << canonical.z) - 1);
const north = (canonical.y === 0) && allowPoles;
const south = (canonical.y === (1 << canonical.z) - 1) && allowPoles;
return this._getMesh(context, granularity, hasBorder, north, south);
}

Expand Down
2 changes: 1 addition & 1 deletion src/geo/projection/mercator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export class MercatorProjection implements Projection {
return translatePosition(transform, tile, translate, translateAnchor);
}

public getMeshFromTileID(context: Context, _tileID: CanonicalTileID, _hasBorder: boolean): Mesh {
public getMeshFromTileID(context: Context, _tileID: CanonicalTileID, _hasBorder: boolean, _allowPoles: boolean): Mesh {
if (this._cachedMesh) {
return this._cachedMesh;
}
Expand Down
3 changes: 2 additions & 1 deletion src/geo/projection/projection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,9 @@ export interface Projection {
* @param context - WebGL context.
* @param tileID - The tile coordinates for which to return a mesh. Meshes for tiles that border the top/bottom mercator edge might include extra geometry for the north/south pole.
* @param hasBorder - When true, the mesh will also include a small border beyond the 0..EXTENT range.
* @param allowPoles - When true, the mesh will also include geometry to cover the north (south) pole, if the given tileID borders the mercator range's top (bottom) edge.
*/
getMeshFromTileID(context: Context, tileID: CanonicalTileID, hasBorder: boolean): Mesh;
getMeshFromTileID(context: Context, tileID: CanonicalTileID, hasBorder: boolean, allowPoles: boolean): Mesh;

/**
* @internal
Expand Down
7 changes: 7 additions & 0 deletions src/gl/cull_face_mode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {CullFaceModeType, FrontFaceType} from './types';

const FRONT = 0x0404;
const BACK = 0x0405;
const CCW = 0x0901;

Expand All @@ -21,7 +22,13 @@ export class CullFaceMode {
* Use for 3D geometry such as terrain.
*/
static backCCW: Readonly<CullFaceMode>;

/**
* Opposite of {@link backCCW}. Culls front-facing triangles when counterclockwise vertex order is used.
*/
static frontCCW: Readonly<CullFaceMode>;
}

CullFaceMode.disabled = new CullFaceMode(false, BACK, CCW);
CullFaceMode.backCCW = new CullFaceMode(true, BACK, CCW);
CullFaceMode.frontCCW = new CullFaceMode(true, FRONT, CCW);
HarelM marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 2 additions & 3 deletions src/render/draw_background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,8 @@ export function drawBackground(painter: Painter, sourceCache: SourceCache, layer
// and also enable stencil clipping. Make sure to render a proper tile clipping mask into stencil
// first though, as that doesn't seem to happen for background layers as of writing this.

const useMeshWithBorders = false;
const mesh = projection.getMeshFromTileID(context, tileID.canonical, useMeshWithBorders);
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
const mesh = projection.getMeshFromTileID(context, tileID.canonical, false, true);
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW,
uniformValues, terrainData, projectionData, layer.id,
mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
}
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export function drawCircles(painter: Painter, sourceCache: SourceCache, layer: C
const {programConfiguration, program, layoutVertexBuffer, indexBuffer, uniformValues, terrainData, projectionData} = segmentsState.state;
const segments = segmentsState.segments;

program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW,
uniformValues, terrainData, projectionData, layer.id,
layoutVertexBuffer, indexBuffer, segments,
layer.paint, painter.transform.zoom, programConfiguration);
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function drawFillTiles(
const stencil = (painter.renderPass === 'translucent') ? painter.stencilModeForClipping(coord) : StencilMode.disabled;

program.draw(painter.context, drawMode, depthMode,
stencil, colorMode, CullFaceMode.disabled, uniformValues, terrainData, projectionData,
stencil, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData,
layer.id, bucket.layoutVertexBuffer, indexBuffer, segments,
layer.paint, painter.transform.zoom, programConfiguration);
}
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_heatmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function drawHeatmap(painter: Painter, sourceCache: SourceCache, layer: H

const radiusCorrectionFactor = projection.getCircleRadiusCorrection(transform);

program.draw(context, gl.TRIANGLES, DepthMode.disabled, stencilMode, colorMode, CullFaceMode.disabled,
program.draw(context, gl.TRIANGLES, DepthMode.disabled, stencilMode, colorMode, CullFaceMode.backCCW,
heatmapUniformValues(tile, transform.zoom, layer.paint.get('heatmap-intensity'), radiusCorrectionFactor),
null, projectionData,
layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer,
Expand Down
4 changes: 2 additions & 2 deletions src/render/draw_hillshade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function renderHillshade(
if (!fbo) {
continue;
}
const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder);
const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true);

const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord);

Expand All @@ -75,7 +75,7 @@ function renderHillshade(
const posMatrix = terrainData ? coord.posMatrix : painter.transform.calculatePosMatrix(tile.tileID.toUnwrapped(), align);
const projectionData = painter.style.map.projection.getProjectionData(coord.canonical, posMatrix);

program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.disabled,
program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.backCCW,
hillshadeUniformValues(painter, tile, layer), terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
}
}
Expand Down
18 changes: 10 additions & 8 deletions src/render/draw_raster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: Ra

// Stencil mask and two-pass is not used for ImageSource sources regardless of projection.
if (source instanceof ImageSource) {
// Image source - not stencil is used
drawTiles(painter, sourceCache, layer, tileIDs, null, false, source.tileCoords);
// Image source - no stencil is used
drawTiles(painter, sourceCache, layer, tileIDs, null, false, false, source.tileCoords, source.flippedWindingOrder);
} else if (useSubdivision) {
// Two-pass rendering
const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs);
drawTiles(painter, sourceCache, layer, coords, stencilBorderless, false, cornerCoords); // draw without borders
drawTiles(painter, sourceCache, layer, coords, stencilBorders, true, cornerCoords); // draw with borders
drawTiles(painter, sourceCache, layer, coords, stencilBorderless, false, true, cornerCoords); // draw without borders
drawTiles(painter, sourceCache, layer, coords, stencilBorders, true, true, cornerCoords); // draw with borders
} else {
// Simple rendering
const [stencil, coords] = painter.stencilConfigForOverlap(tileIDs);
drawTiles(painter, sourceCache, layer, coords, stencil, false, cornerCoords);
drawTiles(painter, sourceCache, layer, coords, stencil, false, true, cornerCoords);
}
}

Expand All @@ -63,7 +63,9 @@ function drawTiles(
coords: Array<OverscaledTileID>,
stencilModes: {[_: number]: Readonly<StencilMode>} | null,
useBorder: boolean,
corners: Array<Point>) {
allowPoles: boolean,
corners: Array<Point>,
flipCullfaceMode: boolean = false) {
const minTileZ = coords[coords.length - 1].overscaledZ;

const context = painter.context;
Expand Down Expand Up @@ -116,11 +118,11 @@ function drawTiles(
const projectionData = projection.getProjectionData(coord.canonical, posMatrix);
const uniformValues = rasterUniformValues(parentTL || [0, 0], parentScaleBy || 1, fade, layer, corners);

const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder);
const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, allowPoles);

const stencilMode = stencilModes ? stencilModes[coord.overscaledZ] : StencilMode.disabled;

program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, flipCullfaceMode ? CullFaceMode.frontCCW : CullFaceMode.backCCW,
uniformValues, terrainData, projectionData, layer.id, mesh.vertexBuffer,
mesh.indexBuffer, mesh.segments);
}
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ function drawSymbolElements(
terrainData: TerrainData) {
const context = painter.context;
const gl = context.gl;
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW,
uniformValues, terrainData, projectionData, layer.id, buffers.layoutVertexBuffer,
buffers.indexBuffer, segments, layer.paint,
painter.transform.zoom, buffers.programConfigurations.get(layer.id),
Expand Down
2 changes: 1 addition & 1 deletion src/render/painter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ export class Painter {
const stencilRef = tileStencilRefs[tileID.key];
const terrainData = this.style.map.terrain && this.style.map.terrain.getTerrainData(tileID);

const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, useBorders);
const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, useBorders, true);

const projectionData = projection.getProjectionData(tileID.canonical, tileID.posMatrix);

Expand Down
4 changes: 2 additions & 2 deletions src/render/terrain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export class Terrain {
indexArray.emplaceBack(x + y, meshSize + x + y + 1, meshSize + x + y + 2);
indexArray.emplaceBack(x + y, meshSize + x + y + 2, x + y + 1);
}
// add an extra frame around the mesh to avoid stiching on tile boundaries with different zoomlevels
// add an extra frame around the mesh to avoid stitching on tile boundaries with different zoomlevels
// first code-block is for top-bottom frame and second for left-right frame
const offsetTop = vertexArray.length, offsetBottom = offsetTop + (meshSize + 1) * 2;
for (const y of [0, 1]) for (let x = 0; x <= meshSize; x++) for (const z of [0, 1])
Expand Down Expand Up @@ -413,7 +413,7 @@ export class Terrain {
}

/**
* Calculates a height of the frame around the terrain-mesh to avoid stiching between
* Calculates a height of the frame around the terrain-mesh to avoid stitching between
* tile boundaries in different zoomlevels.
* @param zoom - current zoomlevel
* @returns the elevation delta in meters
Expand Down
12 changes: 12 additions & 0 deletions src/shaders/circle.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ void main() {

fragColor = v_visibility * opacity_t * mix(color * opacity, stroke_color * stroke_opacity, color_t);

const float epsilon = 0.5 / 255.0;
if (fragColor.r < epsilon && fragColor.g < epsilon && fragColor.b < epsilon && fragColor.a < epsilon) {
// If this pixel wouldn't affect the framebuffer contents in any way, discard it for performance.
// This disables early-Z test, but that is likely irrelevant for circles, performance wise.
// But many circles might put a lot of load on the blending and framebuffer output hardware due to using a lot of pixels,
// and this discard will help in that case.
// Also, each circle will at most use ~3/4 of its rasterized pixels, due to being a circle approximated with a square,
// this will discard the unused 1/4.
// Also note that this discard happens even if overdraw inspection is enabled - because discarded pixels never contribute to overdraw.
discard;
}

#ifdef OVERDRAW_INSPECTOR
fragColor = vec4(1.0);
#endif
Expand Down
9 changes: 9 additions & 0 deletions src/shaders/circle.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ void main(void) {
} else {
gl_Position = projectTileWithElevation(circle_center, ele);

if (gl_Position.z / gl_Position.w > 1.0) {
// Same as in fill_outline.fragment.glsl and line.fragment.glsl, we need to account for some hardware
// doing glFragDepth and clipping in the wrong order by doing clipping manually in the shader.
// For screenspace (not u_pitch_with_map) circles, it is enough to detect whether the anchor
// point should be clipped here in the vertex shader, and clip it by moving in beyond the
// renderable range in X and Y.
gl_Position.xy = vec2(10000.0);
HarelM marked this conversation as resolved.
Show resolved Hide resolved
}

if (u_scale_with_map) {
gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * u_camera_to_center_distance;
} else {
Expand Down
12 changes: 12 additions & 0 deletions src/shaders/fill_outline.fragment.glsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
in vec2 v_pos;
#ifdef GLOBE
in float v_depth;
#endif

#pragma mapbox: define highp vec4 outline_color
#pragma mapbox: define lowp float opacity
Expand All @@ -11,6 +14,15 @@ void main() {
float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
fragColor = outline_color * (alpha * opacity);

#ifdef GLOBE
if (v_depth > 1.0) {
// Hides polygon outlines that are visible on the backfacing side of the globe.
// This is needed, because some hardware seems to apply glDepthRange first and then apply clipping, which is the wrong order.
// Other layers fix this by using backface culling, but that is unavailable for line primitives, so we clip the lines in software here.
discard;
}
#endif

#ifdef OVERDRAW_INSPECTOR
fragColor = vec4(1.0);
#endif
Expand Down
6 changes: 6 additions & 0 deletions src/shaders/fill_outline.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ uniform vec2 u_fill_translate;
in vec2 a_pos;

out vec2 v_pos;
#ifdef GLOBE
out float v_depth;
#endif

#pragma mapbox: define highp vec4 outline_color
#pragma mapbox: define lowp float opacity
Expand All @@ -15,4 +18,7 @@ void main() {
gl_Position = projectTile(a_pos + u_fill_translate);

v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;
#ifdef GLOBE
v_depth = gl_Position.z / gl_Position.w;
#endif
}
11 changes: 10 additions & 1 deletion src/shaders/fill_outline_pattern.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ uniform float u_fade;
in vec2 v_pos_a;
in vec2 v_pos_b;
in vec2 v_pos;
#ifdef GLOBE
in float v_depth;
#endif

#pragma mapbox: define lowp float opacity
#pragma mapbox: define lowp vec4 pattern_from
Expand Down Expand Up @@ -34,9 +37,15 @@ void main() {
float dist = length(v_pos - gl_FragCoord.xy);
float alpha = 1.0 - smoothstep(0.0, 1.0, dist);


fragColor = mix(color1, color2, u_fade) * alpha * opacity;

#ifdef GLOBE
if (v_depth > 1.0) {
// See comment in fill_outline.fragment.glsl
discard;
}
#endif

#ifdef OVERDRAW_INSPECTOR
fragColor = vec4(1.0);
#endif
Expand Down
6 changes: 6 additions & 0 deletions src/shaders/fill_outline_pattern.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ in vec2 a_pos;
out vec2 v_pos_a;
out vec2 v_pos_b;
out vec2 v_pos;
#ifdef GLOBE
out float v_depth;
#endif

#pragma mapbox: define lowp float opacity
#pragma mapbox: define lowp vec4 pattern_from
Expand Down Expand Up @@ -41,4 +44,7 @@ void main() {
v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, toScale * display_size_b, tileRatio, a_pos);

v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;
#ifdef GLOBE
v_depth = gl_Position.z / gl_Position.w;
#endif
}
14 changes: 14 additions & 0 deletions src/shaders/line.fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ uniform lowp float u_device_pixel_ratio;
in vec2 v_width2;
in vec2 v_normal;
in float v_gamma_scale;
#ifdef GLOBE
in float v_depth;
#endif

#pragma mapbox: define highp vec4 color
#pragma mapbox: define lowp float blur
Expand All @@ -24,6 +27,17 @@ void main() {

fragColor = color * (alpha * opacity);

#ifdef GLOBE
if (v_depth > 1.0) {
// Hides lines that are visible on the backfacing side of the globe.
// This is needed, because some hardware seems to apply glDepthRange first and then apply clipping, which is the wrong order.
// Other layers fix this by using backface culling, but the line layer's geometry (actually drawn as polygons) is complex and partly resolved in the shader,
// so we can't easily ensure that all triangles have the proper winding order in the vertex buffer creation step.
// Thus we render line geometry without face culling, and clip the lines manually here.
discard;
}
#endif

#ifdef OVERDRAW_INSPECTOR
fragColor = vec4(1.0);
#endif
Expand Down
Loading