The rendering pipeline
The renderer converts streamed map data into a 240×320-pixel frame. It uses fixed memory and does not allocate heap memory.
Shared render path
obc-render is a no_std crate. The simulator and device use the same geometry code.
The render call receives these inputs:
| Input | Purpose |
|---|---|
| MapScene | Supplies styles, LOD data, candidates, geometry, and diagnostics. |
| Viewport | Defines camera position, scale, rotation, and panel size. |
| RenderConfig | Selects per-frame presentation options. |
| DrawTarget | Receives pixels. |
| Color function | Converts RGB565 styles to target pixels. |
| RenderScratch | Supplies all per-frame work buffers. |
The Reader adapter streams OBCM chunks through MapScene. The interface does not expose file offsets or cache slots.
Frame stages
The renderer performs these stages:
- Project map coordinates to screen coordinates.
- Select a level of detail.
- Find visible chunks.
- Select and decode features.
- Sort selected features by paint order.
- Rasterize polygons and lines.
- Draw route and rider overlays.
Projection
The Viewport stores camera position, zoom, latitude correction, and rotation.
to_screen keeps the camera delta as an integer before conversion to f32. It then corrects longitude, rotates, scales, and rounds.
to_map applies the inverse transform. Panning and viewport bounds use this operation.
Level of detail
An OBCM file contains pre-simplified level-of-detail (LOD) tiers. Each tier specifies its maximum meters per pixel.
The renderer selects the finest supported tier. The selection depends on zoom and latitude.
The selection does not depend on display size. Equal geographic views select the same tier on all hosts.
Visible chunks
Each LOD stores geometry in chunks. A quadtree indexes the chunks by geographic bounds.
The reader descends only into nodes that intersect the viewport. It streams candidates from each nonempty leaf.
The walk limits recursion depth and rejects backward child references. These checks protect the device from invalid map data.
The walk does not limit the total number of visible chunks. The next stage applies the global feature budget.
Feature selection
Dense views can exceed the frame buffers. Each style supplies a retention priority from 1 through 4.
Priority 1 has the highest retention priority. The z-index does not affect retention.
A 256-bit style mask removes hidden styles before geometry decode. The terrain-layer setting uses this mask.
Selection uses two passes:
- Pass A stores style, bounds, size, and an opaque source token.
- An in-memory selection admits candidates against point and ring budgets.
- Pass B decodes only admitted candidates into caller-owned buffers.
A full candidate can evict a lower-priority candidate. The decision applies across all visible chunks.
The renderer drops an invalid or oversized feature as one unit. It does not publish partial geometry.
Each selected feature becomes a compact span. The span references points and rings in the frame buffers.
RenderStats reports budget drops, decode failures, malformed data, source failures, cache activity, and stage time.
Paint order
The renderer sorts spans by z-index and collection sequence.
Priority controls feature retention. The z-index controls paint order. Collection sequence gives deterministic order for equal z-index values.
Polygon fill
The polygon filler uses the even-odd scanline rule. It sorts edge crossings for each row and fills between pairs.
The filler writes clipped horizontal rectangles. It skips a row if its crossing buffer is full.
Line stroke
Embedded Graphics strokes 1-pixel lines. The renderer converts wider segments to convex quadrilaterals and fills them as spans.
Discs close run ends and sharp joints. Normal line width changes with zoom and stays from 1 through 12 pixels.
Fixed-width styles bypass the zoom scale. Contours use this style property.
Style combinations
| Feature | Dashed | color2 | Result |
|---|---|---|---|
| Line | No | None | Solid stroke |
| Line | No | Set | Road casing and road fill |
| Line | Yes | None | Dashed stroke |
| Line | Yes | Set | Solid base and dashed top stroke |
| Polygon | Ignored | Set | Fill and ring outline |
Dashed lines use screen-space arc length after clipping. A railway style draws a solid color2 base and color dashes.
Road casing
A road casing uses color2 and adds 2 pixels to the road width. Casings run only at the finest LOD.
The renderer draws casings at the start of the road z-band. It then draws all road fills.
This order keeps the casing above land fills. It also prevents casing lines inside road intersections.
Polygon outlines
A polygon with color2 receives a closed outline at the finest LOD.
The renderer fills all polygons in one z-group first. It then draws all outlines in that group.
This order keeps shared walls between adjacent buildings.
Rain layer
The optional rain raster uses the gap between the ground and road z-bands. Roads, routes, markers, and UI chrome remain visible.
Only the rain-map screen requests this layer. A frame without rain uses the normal paint path.
The display path can use bilinear sampling. All weather decisions use nearest-neighbor samples from actual cells.
No-data cells do not take part in interpolation. The renderer reports when the zoom is outside the supported rain regime.
Map overlays
The renderer draws moving map content after the base map:
- Active route and direction chevrons
- Breadcrumb trail
- Waypoints and rider marker
- Map status and tool indicators
The route and breadcrumb use the shared line stroker. Markers use the shared polygon filler.
Frame storage and presentation
The device stores one RGB222 frame byte per pixel. The 240×320 frame uses 75 KiB.
Each byte has the 00_RR_GG_BB format. The framebuffer converts RGB565 pixels when it stores them.
The LS021 presenter hashes each row. It sends the changed row spans through the FLPR coprocessor.
The M33 renders the frame and publishes dirty rows. The FLPR reads shared SRAM and writes the panel wire format.
The simulator implements the same display contracts. Its final presenter writes changed rows to the host texture.
Transient overlays
A transient overlay is not stored in the clean base frame. The presenter reads the required base-frame window and composites the overlay.
Clearing the overlay presents the clean window again. It does not require a map render.
A base-frame update can exclude a live overlay region. This rule prevents overlay flicker during a map update.
Memory budgets
RenderScratch contains fixed-capacity buffers. The device initializes it in place in the shared scratch arena.
| Buffer | Purpose | Capacity |
|---|---|---|
| Frame points | Selected projected vertices | 16,323 |
| Frame ring lengths | Selected feature rings | 3,328 |
| Candidate spans | Candidate and draw records | 3,072 |
| Decode points | One decoded feature | 2,048 |
| Screen points | One drawn feature | 2,048 |
| Scanline crossings | One polygon row | 384 |
The board build checks the complete scratch size against its arena budget. Increasing a capacity is a device-memory decision.
Source map
- Renderer and scratch budgets: lib.rs
- Projection: viewport.rs
- Collection and selection: collect.rs
- Polygon and line rasterization: fill.rs, stroke.rs
- Streamed map contract: obc-map-scene
- OBCM adapter and quadtree walk: scene.rs, reader/mod.rs
- Frame and presenters: display contracts, LS021
See system architecture for the host loop. See data formats for the OBCM format.