Skip to content
Open
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
32 changes: 27 additions & 5 deletions src/render/draw_fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function drawFill(painter: Painter, tileManager: TileManager, layer: Fill
return;
}

const {isRenderingToTexture} = renderOptions;
const {isRenderingToTexture, isRenderingGlobe} = renderOptions;
const colorMode = painter.colorModeForRenderPass();
const pattern = layer.paint.get('fill-pattern');
const pass = painter.opaquePassEnabledForLayer() &&
Expand All @@ -37,7 +37,13 @@ export function drawFill(painter: Painter, tileManager: TileManager, layer: Fill
if (painter.renderPass === pass) {
const depthMode = painter.getDepthModeForSublayer(
1, painter.renderPass === 'opaque' ? DepthMode.ReadWrite : DepthMode.ReadOnly);
drawFillTiles(painter, tileManager, layer, coords, depthMode, colorMode, false, isRenderingToTexture);
drawFillTiles(painter, tileManager, layer, coords, depthMode, colorMode, false, isRenderingToTexture, isRenderingGlobe);

// For globe projection, the fill pass uses overdraw prevention on antimeridian tiles,
// which corrupts their stencil values. Restore them before the outline pass.
if (isRenderingGlobe) {
painter.restoreAntimeridianClippingMasks(coords, isRenderingToTexture);
}
}

// Draw stroke
Expand All @@ -53,7 +59,13 @@ export function drawFill(painter: Painter, tileManager: TileManager, layer: Fill
// the (non-antialiased) fill.
const depthMode = painter.getDepthModeForSublayer(
layer.getPaintProperty('fill-outline-color') ? 2 : 0, DepthMode.ReadOnly);
drawFillTiles(painter, tileManager, layer, coords, depthMode, colorMode, true, isRenderingToTexture);

drawFillTiles(painter, tileManager, layer, coords, depthMode, colorMode, true, isRenderingToTexture, isRenderingGlobe);

// Restore masks after outline pass for subsequent layers
if (isRenderingGlobe) {
painter.restoreAntimeridianClippingMasks(coords, isRenderingToTexture);
}
}
}

Expand All @@ -65,7 +77,8 @@ function drawFillTiles(
depthMode: Readonly<DepthMode>,
colorMode: Readonly<ColorMode>,
isOutline: boolean,
isRenderingToTexture: boolean) {
isRenderingToTexture: boolean,
isRenderingGlobe: boolean) {
const gl = painter.context.gl;
const fillPropertyName = 'fill-pattern';
const patternProperty = layer.paint.get(fillPropertyName);
Expand Down Expand Up @@ -128,7 +141,16 @@ function drawFillTiles(
fillOutlineUniformValues(drawingBufferSize, translateForUniforms);
}

const stencil = painter.stencilModeForClipping(coord);
// For globe projection, tiles at the antimeridian (x=0 or x=2^z-1) share the same
// clipping mask ID to allow their overlapping geometry to be drawn. For fills,
// we need to prevent double-drawing at the seam using overdraw prevention.
const isAtAntimeridian = isRenderingGlobe && (
coord.canonical.x === 0 ||
coord.canonical.x === (1 << coord.canonical.z) - 1
);
const stencil = isAtAntimeridian
? painter.stencilModeForClippingWithOverdrawPrevention(coord)
: painter.stencilModeForClipping(coord);

program.draw(painter.context, drawMode, depthMode,
stencil, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData,
Expand Down
31 changes: 31 additions & 0 deletions src/render/painter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,37 @@ export class Painter {
return new StencilMode({func: gl.EQUAL, mask: 0xFF}, this._tileClippingMaskIDs[tileID.key], 0x00, gl.KEEP, gl.KEEP, gl.REPLACE);
}

/**
* Returns a stencil mode for clipping that also prevents overdraw.
*/
stencilModeForClippingWithOverdrawPrevention(tileID: OverscaledTileID): StencilMode {
const gl = this.context.gl;
const clipId = this._tileClippingMaskIDs[tileID.key];
// Test EQUAL to respect clipping mask, but write 0 to prevent subsequent draws
// at the same pixel (since 0 != clipId, subsequent EQUAL tests will fail)
return new StencilMode({func: gl.EQUAL, mask: 0xFF}, clipId, 0xFF, gl.KEEP, gl.KEEP, gl.ZERO);
}

/**
* Re-renders clipping masks for antimeridian tiles (x=0 or x=2^z-1) to restore
* their stencil values after they were modified by overdraw prevention.
*/
restoreAntimeridianClippingMasks(tileIDs: Array<OverscaledTileID>, renderToTexture: boolean) {
const antimeridianTiles = tileIDs.filter(tileID =>
tileID.canonical.x === 0 ||
tileID.canonical.x === (1 << tileID.canonical.z) - 1
);

if (antimeridianTiles.length === 0) return;

const context = this.context;
context.setColorMode(ColorMode.disabled);
context.setDepthMode(DepthMode.disabled);

this._renderTileMasks(this._tileClippingMaskIDs, antimeridianTiles, renderToTexture, true);
this._renderTileMasks(this._tileClippingMaskIDs, antimeridianTiles, renderToTexture, false);
}

/*
* 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
Expand Down
Loading