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

expose context antialiasing and draw 3d things into main framebuffer #7821

Merged
merged 2 commits into from
Feb 25, 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
1 change: 1 addition & 0 deletions debug/threejs.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

var map = window.map = new mapboxgl.Map({
container: 'map',
antialias: true,
zoom: 16.5,
center: [-79.390307, 43.658956],
bearing: 20,
Expand Down
35 changes: 11 additions & 24 deletions src/render/draw_custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export default drawCustom;

import DepthMode from '../gl/depth_mode';
import StencilMode from '../gl/stencil_mode';
import {prepareOffscreenFramebuffer, drawOffscreenTexture} from './offscreen';

import type Painter from './painter';
import type SourceCache from '../source/source_cache';
Expand All @@ -27,35 +26,23 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty
painter.setBaseState();
}

if (implementation.renderingMode === '3d') {
painter.setCustomLayerDefaults();

prepareOffscreenFramebuffer(painter, layer);
implementation.render(context.gl, painter.transform.customLayerMatrix());

context.setDirty();
painter.setBaseState();
}

} else if (painter.renderPass === 'translucent') {

if (implementation.renderingMode === '3d') {
drawOffscreenTexture(painter, layer, 1);
painter.setCustomLayerDefaults();

} else {
painter.setCustomLayerDefaults();
context.setColorMode(painter.colorModeForRenderPass());
context.setStencilMode(StencilMode.disabled);

context.setColorMode(painter.colorModeForRenderPass());
context.setStencilMode(StencilMode.disabled);
const depthMode = implementation.renderingMode === '3d' ?
new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D) :
painter.depthModeForSublayer(0, DepthMode.ReadOnly);

const depthMode = painter.depthModeForSublayer(0, DepthMode.ReadOnly);
context.setDepthMode(depthMode);
context.setDepthMode(depthMode);

implementation.render(context.gl, painter.transform.customLayerMatrix());
implementation.render(context.gl, painter.transform.customLayerMatrix());

context.setDirty();
painter.setBaseState();
context.bindFramebuffer.set(null);
}
context.setDirty();
painter.setBaseState();
context.bindFramebuffer.set(null);
}
}
3 changes: 2 additions & 1 deletion src/render/draw_fill.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ function drawFill(painter: Painter, sourceCache: SourceCache, layer: FillStyleLa
const colorMode = painter.colorModeForRenderPass();

const pattern = layer.paint.get('fill-pattern');
const pass = (!pattern.constantOr((1: any)) &&
const pass = painter.opaquePassEnabledForLayer() &&
(!pattern.constantOr((1: any)) &&
color.constantOr(Color.transparent).a === 1 &&
opacity.constantOr(0) === 1) ? 'opaque' : 'translucent';

Expand Down
42 changes: 27 additions & 15 deletions src/render/draw_fill_extrusion.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import DepthMode from '../gl/depth_mode';
import StencilMode from '../gl/stencil_mode';
import ColorMode from '../gl/color_mode';
import CullFaceMode from '../gl/cull_face_mode';
import {
fillExtrusionUniformValues,
fillExtrusionPatternUniformValues,
} from './program/fill_extrusion_program';
import {prepareOffscreenFramebuffer, drawOffscreenTexture} from './offscreen';

import type Painter from './painter';
import type SourceCache from '../source/source_cache';
Expand All @@ -18,21 +18,32 @@ import type {OverscaledTileID} from '../source/tile_id';
export default draw;

function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLayer, coords: Array<OverscaledTileID>) {
if (layer.paint.get('fill-extrusion-opacity') === 0) {
const opacity = layer.paint.get('fill-extrusion-opacity');
if (opacity === 0) {
return;
}

if (painter.renderPass === 'offscreen') {
prepareOffscreenFramebuffer(painter, layer);

const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, [0, 1]),
stencilMode = StencilMode.disabled,
colorMode = painter.colorModeForRenderPass();

drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMode, colorMode);

} else if (painter.renderPass === 'translucent') {
drawOffscreenTexture(painter, layer, layer.paint.get('fill-extrusion-opacity'));
if (painter.renderPass === 'translucent') {
const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D);

if (opacity === 1 && !layer.paint.get('fill-extrusion-pattern').constantOr((1: any))) {
const colorMode = painter.colorModeForRenderPass();
drawExtrusionTiles(painter, source, layer, coords, depthMode, StencilMode.disabled, colorMode);

} else {
// Draw transparent buildings in two passes so that only the closest surface is drawn.
// First draw all the extrusions into only the depth buffer. No colors are drawn.
drawExtrusionTiles(painter, source, layer, coords, depthMode,
StencilMode.disabled,
ColorMode.disabled);

// Then draw all the extrusions a second type, only coloring fragments if they have the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "a second time"

// same depth value as the closest fragment in the previous pass. Use the stencil buffer
// to prevent the second draw in cases where we have coincident polygons.
drawExtrusionTiles(painter, source, layer, coords, depthMode,
painter.stencilModeFor3D(),
painter.colorModeForRenderPass());
}
}
}

Expand All @@ -42,6 +53,7 @@ function drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMo
const patternProperty = layer.paint.get('fill-extrusion-pattern');
const image = patternProperty.constantOr((1: any));
const crossfade = layer.getCrossfadeParameters();
const opacity = layer.paint.get('fill-extrusion-opacity');

for (const coord of coords) {
const tile = source.getTile(coord);
Expand Down Expand Up @@ -72,8 +84,8 @@ function drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMo

const shouldUseVerticalGradient = layer.paint.get('fill-extrusion-vertical-gradient');
const uniformValues = image ?
fillExtrusionPatternUniformValues(matrix, painter, shouldUseVerticalGradient, coord, crossfade, tile) :
fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient);
fillExtrusionPatternUniformValues(matrix, painter, shouldUseVerticalGradient, opacity, coord, crossfade, tile) :
fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient, opacity);


program.draw(context, context.gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW,
Expand Down
64 changes: 0 additions & 64 deletions src/render/offscreen.js

This file was deleted.

81 changes: 56 additions & 25 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import type ImageManager from './image_manager';
import type GlyphManager from './glyph_manager';
import type VertexBuffer from '../gl/vertex_buffer';
import type IndexBuffer from '../gl/index_buffer';
import type {DepthMaskType, DepthFuncType} from '../gl/types';
import type {DepthRangeType, DepthMaskType, DepthFuncType} from '../gl/types';

export type RenderPass = 'offscreen' | 'opaque' | 'translucent';

Expand Down Expand Up @@ -108,9 +108,12 @@ class Painter {
lineAtlas: LineAtlas;
imageManager: ImageManager;
glyphManager: GlyphManager;
depthRange: number;
depthRangeFor3D: DepthRangeType;
opaquePassCutoff: number;
renderPass: RenderPass;
currentLayer: number;
currentStencilSource: ?string;
nextStencilID: number;
id: string;
_showOverdrawInspector: boolean;
cache: { [string]: Program<*> };
Expand Down Expand Up @@ -219,6 +222,9 @@ class Painter {
const context = this.context;
const gl = context.gl;

this.nextStencilID = 1;
this.currentStencilSource = undefined;

// As a temporary workaround for https://github.com/mapbox/mapbox-gl-js/issues/5490,
// pending an upstream fix, we draw a fullscreen stencil=0 clipping mask here,
// effectively clearing the stencil buffer: once an upstream patch lands, remove
Expand All @@ -235,20 +241,28 @@ class Painter {
this.quadTriangleIndexBuffer, this.viewportSegments);
}

_renderTileClippingMasks(tileIDs: Array<OverscaledTileID>) {
_renderTileClippingMasks(layer: StyleLayer, tileIDs: Array<OverscaledTileID>) {
if (this.currentStencilSource === layer.source || !layer.isTileClipped() || !tileIDs || !tileIDs.length) return;

this.currentStencilSource = layer.source;

const context = this.context;
const gl = context.gl;

if (this.nextStencilID + tileIDs.length > 256) {
// we'll run out of fresh IDs so we need to clear and start from scratch
this.clearStencil();
}

context.setColorMode(ColorMode.disabled);
context.setDepthMode(DepthMode.disabled);

const program = this.useProgram('clippingMask');

let idNext = 1;
this._tileClippingMaskIDs = {};

for (const tileID of tileIDs) {
const id = this._tileClippingMaskIDs[tileID.key] = idNext++;
const id = this._tileClippingMaskIDs[tileID.key] = this.nextStencilID++;

program.draw(context, gl.TRIANGLES, DepthMode.disabled,
// Tests will always pass, and ref value will be written to stencil buffer.
Expand All @@ -259,6 +273,16 @@ class Painter {
}
}

stencilModeFor3D(): StencilMode {
if (this.nextStencilID + 1 > 256) {
this.clearStencil();
}

const id = this.nextStencilID++;
const gl = this.context.gl;
return new StencilMode({ func: gl.NOTEQUAL, mask: 0xFF }, id, 0xFF, gl.KEEP, gl.KEEP, gl.REPLACE);
}

stencilModeForClipping(tileID: OverscaledTileID): StencilMode {
const gl = this.context.gl;
return new StencilMode({ func: gl.EQUAL, mask: 0xFF }, this._tileClippingMaskIDs[tileID.key], 0x00, gl.KEEP, gl.KEEP, gl.REPLACE);
Expand All @@ -278,11 +302,23 @@ class Painter {
}
}

depthModeForSublayer(n: number, mask: DepthMaskType, func: ?DepthFuncType): DepthMode {
depthModeForSublayer(n: number, mask: DepthMaskType, func: ?DepthFuncType): $ReadOnly<DepthMode> {
if (!this.opaquePassEnabledForLayer()) return DepthMode.disabled;
const depth = 1 - ((1 + this.currentLayer) * this.numSublayers + n) * this.depthEpsilon;
return new DepthMode(func || this.context.gl.LEQUAL, mask, [depth, depth]);
}

/*
* The opaque pass and 3D layers both use the depth buffer.
* Layers drawn above 3D layers need to be drawn using the
* painter's algorithm so that they appear above 3D features.
* This returns true for layers that can be drawn using the
* opaque pass.
*/
opaquePassEnabledForLayer() {
return this.currentLayer < this.opaquePassCutoff;
}

render(style: Style, options: PainterOptions) {
this.style = style;
this.options = options;
Expand Down Expand Up @@ -323,6 +359,15 @@ class Painter {
updateTileMasks(visibleTiles, this.context);
}

this.opaquePassCutoff = Infinity;
for (let i = 0; i < layerIds.length; i++) {
const layerId = layerIds[i];
if (this.style._layers[layerId].is3D()) {
this.opaquePassCutoff = i;
break;
}
}

// Offscreen pass ===============================================
// We first do all rendering that requires rendering to a separate
// framebuffer, and then save those for rendering back to the map
Expand All @@ -345,36 +390,29 @@ class Painter {

// Clear buffers in preparation for drawing to the main framebuffer
this.context.clear({ color: options.showOverdrawInspector ? Color.black : Color.transparent, depth: 1 });
this.clearStencil();

this._showOverdrawInspector = options.showOverdrawInspector;
this.depthRange = (style._order.length + 2) * this.numSublayers * this.depthEpsilon;
this.depthRangeFor3D = [0, 1 - ((style._order.length + 2) * this.numSublayers * this.depthEpsilon)];

// Opaque pass ===============================================
// Draw opaque layers top-to-bottom first.
this.renderPass = 'opaque';
let prevSourceId;

for (this.currentLayer = layerIds.length - 1; this.currentLayer >= 0; this.currentLayer--) {
const layer = this.style._layers[layerIds[this.currentLayer]];
const sourceCache = sourceCaches[layer.source];
const coords = coordsAscending[layer.source];

if (layer.source !== prevSourceId && sourceCache) {
this.clearStencil();
if (sourceCache.getSource().isTileClipped) {
this._renderTileClippingMasks(coords);
}
}

this._renderTileClippingMasks(layer, coords);
this.renderLayer(this, sourceCache, layer, coords);
prevSourceId = layer.source;
}

// Translucent pass ===============================================
// Draw all other layers bottom-to-top.
this.renderPass = 'translucent';

for (this.currentLayer = 0, prevSourceId = null; this.currentLayer < layerIds.length; this.currentLayer++) {
for (this.currentLayer = 0; this.currentLayer < layerIds.length; this.currentLayer++) {
const layer = this.style._layers[layerIds[this.currentLayer]];
const sourceCache = sourceCaches[layer.source];

Expand All @@ -383,15 +421,8 @@ class Painter {
// separate clipping masks
const coords = (layer.type === 'symbol' ? coordsDescendingSymbol : coordsDescending)[layer.source];

if (layer.source !== prevSourceId && sourceCache) {
this.clearStencil();
if (sourceCache.getSource().isTileClipped) {
this._renderTileClippingMasks(coordsAscending[layer.source]);
}
}

this._renderTileClippingMasks(layer, coordsAscending[layer.source]);
this.renderLayer(this, sourceCache, layer, coords);
prevSourceId = layer.source;
}

if (this.options.showTileBoundaries) {
Expand Down
Loading