Skip to main content

How the renderer works

What the render path does, how it is implemented, how it compares to tangram-ng, and why it differs where it does.

Split so a reader — human or agent — can open one page and get a whole subsystem. Every page states its scope at the top and links out rather than repeating.

Pick your page​

You are working onRead
anything at all, first timeFrame & threads
frame order, threads, what runs where, redraw requestsFrame & threads
which tiles are chosen, LOD, fetching, decoding (MVT and MLT), cachesTiles & LOD
the GL draw path, style layers, draw counts, shadersGL vector-tile renderer
3D terrain: elevation data, surfaces, the ground pass3D terrain
bridges and tunnels: chords, decks, the opt-in switch, what is still wrong3D bridges
glTF models placed from a tile layer: trees, turbines, instancing, spin3D models
z-fighting, see-through, content sinking into the groundDepth model
labels: placement, flicker, glyph sharpness, anchoring onto terrainLabels
hillshade, contour lines, contour labels, hypsometric tintHillshade & contours
sun, shadows, sky, fogLighting, sky & fog
CompositeVectorTileLayer, style-driven slotsComposite layer
markers, popups, app-drawn lines/polygons, pickingVector elements
navigation maneuver arrows on a routeManeuver arrows
sun/moon/stars/aircraft placed in the skyCelestial objects
full-screen effects, the relief look, layers drawn above themPost-processing
making it faster, or measuring anythingPerformance
"why don't we just do what tangram does?"vs tangram-ng
which graphics API we target, ES 3.0, Metal, ANGLE, new platformsGraphics API migration
spherical render projection: what works on the globe and what does notGlobe mode
an app drawing its own GL into the frame: slots, GL state, threads, context lossCustom layers

The dated measurement history is a separate lab notebook: Performance log. These pages are the current design; that one is what was tried.

One frame​

Detail in Frame & threads. The PROF sections of a -PprofileRender build map onto these boxes one-for-one.

Two rules that shaped the render code​

1. tangram-ng is the reference implementation, and we copy it. Where it does something differently, we adopt its way rather than designing an alternative. Its constants are copied, not derived — every constant this project derived instead turned out wrong (failure catalogue in Depth model). Each mechanism is ported whole: half of their depth model was measurably worse than none of it, three times over. When comparing, read their scene files (res/scenes/*.yaml), not only their shaders — the shader defaults are overridden there.

2. Flat content is draped, and live geometry is the exception. Fills and lines are baked per tile into a drape texture that the shared ground samples (TerrainOptions.DrapeFillsEnabled / DrapeLinesEnabled, both default on; --es drape / --es drapeLines in the demo). Draping the lines took the city pan from ~14 to ~27 fps (Performance); what stays live is filtered by NoDrapeLayerFilter. Mechanism in 3D terrain.

Where the render code lives​

PathWhat
all/native/renderers/frame orchestration (MapRenderer), per-kind renderers, TileRenderer (the bridge into vt)
all/native/renderers/workers/CullWorker, VTLabelPlacementWorker, BillboardPlacementWorker
all/native/layers/TileLayer, VectorTileLayer, RasterTileLayer, HillshadeRasterTileLayer, CompositeVectorTileLayer
all/native/terrain/ElevationManager, ElevationTileGrid, TerrainTileTransformer
all/native/datasources/tile sources, including the on-the-fly ContourTileDataSource
libs-massif/vt/the GL renderer: GLTileRenderer, TileSurfaceBuilder, Label, LabelCuller, shaders
libs-massif/mapnikvt, cartocsstile decoding and style evaluation

vt has no logger — probe it with __android_log_print(4, "massif", …), and throttle a probe shared by several renderer instances with a prime modulus.