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 on | Read |
|---|---|
| anything at all, first time | Frame & threads |
| frame order, threads, what runs where, redraw requests | Frame & threads |
| which tiles are chosen, LOD, fetching, decoding (MVT and MLT), caches | Tiles & LOD |
| the GL draw path, style layers, draw counts, shaders | GL vector-tile renderer |
| 3D terrain: elevation data, surfaces, the ground pass | 3D terrain |
| bridges and tunnels: chords, decks, the opt-in switch, what is still wrong | 3D bridges |
| glTF models placed from a tile layer: trees, turbines, instancing, spin | 3D models |
| z-fighting, see-through, content sinking into the ground | Depth model |
| labels: placement, flicker, glyph sharpness, anchoring onto terrain | Labels |
| hillshade, contour lines, contour labels, hypsometric tint | Hillshade & contours |
| sun, shadows, sky, fog | Lighting, sky & fog |
CompositeVectorTileLayer, style-driven slots | Composite layer |
| markers, popups, app-drawn lines/polygons, picking | Vector elements |
| navigation maneuver arrows on a route | Maneuver arrows |
| sun/moon/stars/aircraft placed in the sky | Celestial objects |
| full-screen effects, the relief look, layers drawn above them | Post-processing |
| making it faster, or measuring anything | Performance |
| "why don't we just do what tangram does?" | vs tangram-ng |
| which graphics API we target, ES 3.0, Metal, ANGLE, new platforms | Graphics API migration |
| spherical render projection: what works on the globe and what does not | Globe mode |
| an app drawing its own GL into the frame: slots, GL state, threads, context loss | Custom 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
| Path | What |
|---|---|
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, cartocss | tile 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.