Class MarkerLayer
GPU bulk marker tier: one instanced draw for a whole MarkerStore on the same transform as the tiles.
public sealed class MarkerLayer : IDisposable, IMarkerStyle
- Inheritance
-
objectMarkerLayer
- Implements
Constructors
MarkerLayer(MarkerStore)
Renders markers from a store managed by another layer, without copying.
Shares an existing store - typically another MarkerLayer's, or a store also fed to a SurfaceWorldMarkerRenderer - so the same markers render on this layer without copying, and a 2D-to-tabletop (or screen-to-in-texture) switch is a pure view swap. Mirrors SurfaceWorldMarkerRenderer(MarkerStore): the owning layer keeps its dirty-range fast path; this layer re-uploads whole when the store's Version moves.
This layer still builds its own SpatialIndex for
TryPick(MapViewTransform, ScreenPoint, double, out int), but never attaches it to
sharedStore.SpatialIndex: that property has a
single slot, and a second attach would silently steal it from the
owning layer, breaking that layer's own picking. So picking on a
shared-store MarkerLayer never finds anything - use the owning
layer for picking; this constructor is for a second render
consumer of the same markers, such as an in-texture layer stack
on SurfaceMapView.
public MarkerLayer(MarkerStore sharedStore)
Parameters
sharedStoreMarkerStore
MarkerLayer(int)
Creates a layer that owns a new MarkerStore sized for initialCapacity markers.
public MarkerLayer(int initialCapacity)
Parameters
initialCapacityintStore and GPU buffer hint, marker count.
Fields
FirstCustomShapeId
First shape id reserved for textures registered with RegisterShape(Texture2D, bool). Ids below this are procedural SDF shapes.
public const int FirstCustomShapeId = 16
Field Value
HideOffscreenMarkers
When true, markers whose center leaves the viewport are not drawn.
public bool HideOffscreenMarkers
Field Value
HoverScale
Scale applied to the hovered marker in the vertex shader.
public float HoverScale
Field Value
HoveredId
Slot index of the hovered marker for the vertex-shader grow, or -1 for none. Not a MarkerStore id; use MarkerStore.SlotOf.
public int HoveredId
Field Value
Map
The view this layer's markers belong to, read once per
PrepareDraw(MapViewTransform) purely for ElevationSampler - Store's
altitude resolution needs the current ground sampler, and a
bulk-tier MarkerLayer is constructed before its
owning view exists (ScreenMapView.Awake), so this is set
afterward rather than threaded through a constructor. Null,
the default, means flat: the same "no sampler" convention
CurrentSampler already documents.
public BaseMapView Map
Field Value
MaxAltitudeMercator
Upper altitude bound (mercator units), inclusive. See MinAltitudeMercator. PositiveInfinity, the default, means no upper bound.
public float MaxAltitudeMercator
Field Value
MinAltitudeMercator
Lower altitude bound (mercator units, AltitudeMercator)
a marker must meet to be uploaded/drawn by this layer, inclusive.
NegativeInfinity, the default, means no lower
bound - the layer draws everything. Per-layer-instance, not a
per-type constant: the same MarkerLayer class backs
a fullscreen view (unbounded) and a ground-only tier sharing a
store with a SurfaceWorldMarkerRenderer
(filtered to alt <= 0, so the two layers do not both draw
the same floating marker).
public float MinAltitudeMercator
Field Value
SpatialIndex
Spatial index kept in sync with Store for the lifetime of this layer (attached before any marker is added, in the constructor), and reused across picking, clustering and viewport tracking so those three consumers never build competing views of the same store.
public readonly MarkerSpatialIndex SpatialIndex
Field Value
Store
Marker data this layer draws.
public readonly MarkerStore Store
Field Value
Properties
ApplyZoomRange
When true, the default, each marker's stored min/max zoom window hides it outside that range. When false the windows stay stored but every marker on this layer is drawn and pickable at any zoom. Combined with the per-marker flag: both must be true to apply.
public bool ApplyZoomRange { get; set; }
Property Value
CustomShapes
Registered shape sources in shape-id order, so index i is shape id FirstCustomShapeId + i. Baked SDF entries belong to this layer; the others are the caller's own textures.
public IReadOnlyList<Texture2D> CustomShapes { get; }
Property Value
- IReadOnlyList<Texture2D>
OutlineColor
Layer-wide SDF outline color; alpha 0 disables.
public Color OutlineColor { get; set; }
Property Value
- Color
OutlineWidthPx
Layer-wide SDF outline width in screen px.
public float OutlineWidthPx { get; set; }
Property Value
ShapeAtlas
The packed shape atlas, or null before the first RegisterShape(Texture2D, bool). Exposed for leak gates and debugging.
public Texture2D ShapeAtlas { get; }
Property Value
- Texture2D
StemWidthPx
Vertical stem width in screen px, when StemsEnabled draws one.
public float StemWidthPx { get; set; }
Property Value
StemsEnabled
When true and any marker has altitude, draws vertical stems connecting raised markers to ground.
public bool StemsEnabled { get; set; }
Property Value
Visible
When false, this layer is not drawn. Clustering uses this to swap in bubbles.
public bool Visible { get; set; }
Property Value
WarmUpZoomThreshold
While this layer is invisible, PrepareDraw(MapViewTransform) starts
chunking its first upload into idle frames once view.State.Zoom
reaches this value, instead of paying the whole upload in the one
frame Visible flips true. PositiveInfinity, the default, disables warm-up.
This layer has no notion of a cluster threshold of its own - a
caller that swaps Visible off below some zoom
should set this a little below that same zoom.
public double WarmUpZoomThreshold { get; set; }
Property Value
Methods
BakeSdf(Texture2D, int)
Bakes a signed-distance field from the source alpha silhouette, encoded as alpha with 0.5 at the edge.
public static Texture2D BakeSdf(Texture2D src, int spreadPx = 6)
Parameters
srcTexture2DCPU-readable source texture.
spreadPxintSearch radius in texels.
Returns
- Texture2D
RGBA32 texture, rgb white so the shader tints, alpha is the distance field.
Dispose()
Releases GPU buffers, the material, and any distance fields this layer baked.
public void Dispose()
FillCommandBuffer(CommandBuffer)
Records the prepared bulk draw into cb. Structured
buffers bind through a per-draw property block: command buffers do
not snapshot material buffer state.
public void FillCommandBuffer(CommandBuffer cb)
Parameters
cbCommandBufferCommand buffer already bound to the camera target.
PrepareDraw(MapViewTransform)
Uploads dirty markers and stashes uniforms for view.
Call from the game thread before FillCommandBuffer(CommandBuffer).
public void PrepareDraw(MapViewTransform view)
Parameters
viewMapViewTransformCurrent map view.
RegisterCustomShape(Texture2D, bool)
Registers a readable texture as a marker shape and returns the shape to pass to the store.
public MarkerShape RegisterCustomShape(Texture2D readableTexture, bool asSdf = false)
Parameters
readableTextureTexture2DCPU-readable texture, for example a sprite rasterized from an imported SVG.
asSdfboolTrue bakes the alpha silhouette into a distance field so the shape stays crisp at any size (monochrome, tinted by the marker color). False keeps a full-color sprite.
Returns
- MarkerShape
Shape whose numeric value is at or above FirstCustomShapeId.
Remarks
The source texture must be CPU-readable (tick Read/Write Enabled on the texture importer), because both the distance-field bake and the atlas pack read its pixels. A layer holds at most 64 custom shapes; the sixty-fifth registration throws.
Every registration repacks the whole shape atlas. That is a registration-time cost and never a per-frame one, but registering many shapes one at a time pays it many times over. To register a set, use RegisterCustomShapes(IReadOnlyList<CustomShapeRequest>, List<MarkerShape>), which packs once.
Ownership: with asSdf true the layer bakes a new
texture, owns it, and destroys it on Dispose(), while
your source stays yours. With asSdf false the
layer packs your texture and never destroys it, so keep it alive for
as long as the layer draws that shape.
A full-color sprite shape:
MarkerShape cafe = layer.RegisterCustomShape(cafeSprite);
layer.Store.Add(new LatLon(48.8566, 2.3522), 0, 24f,
MarkerSizeMode.Pixels, Color.white, cafe);
A monochrome silhouette baked to a distance field, so it stays crisp at any size and takes the marker color as its tint:
MarkerShape arrow = layer.RegisterCustomShape(arrowSilhouette, asSdf: true);
layer.Store.Add(new LatLon(48.8566, 2.3522), 0, 24f,
MarkerSizeMode.Pixels, Color.red, arrow);
RegisterCustomShapes(IReadOnlyList<CustomShapeRequest>, List<MarkerShape>)
Registers several shapes at once, packing the shape atlas exactly once.
public void RegisterCustomShapes(IReadOnlyList<CustomShapeRequest> requests, List<MarkerShape> ids)
Parameters
requestsIReadOnlyList<CustomShapeRequest>Shapes to register, in the order their ids are wanted.
idsList<MarkerShape>Cleared, then filled with one shape per request, in request order.
Remarks
Same per-shape contract as RegisterCustomShape(Texture2D, bool): CPU-readable sources, at most 64 shapes per layer, baked distance fields owned by the layer and plain sprites owned by you.
The one difference is cost. Registering n shapes individually packs the atlas n times; this packs once for the whole batch, which is why it exists.
A batch is all or nothing. Whether it is refused for exceeding the 64-shape limit or one of its textures turns out not to be readable, the layer is left exactly as it was, so the same batch can be handed back after the bad texture is fixed without the failed attempt having consumed any shape slots.
RegisterShape(Texture2D, bool)
Registers a readable texture as a marker shape and returns the id to store in MarkerInstanceData.Shape.
public int RegisterShape(Texture2D readableTexture, bool asSdf = false)
Parameters
readableTextureTexture2DCPU-readable texture, for example a sprite rasterized from an imported SVG.
asSdfboolTrue bakes the alpha silhouette into a distance field so the shape stays crisp at any size (monochrome, tinted by the marker color). False keeps a full-color sprite.
Returns
- int
Shape id at or above FirstCustomShapeId, as an int.
Remarks
Identical to RegisterCustomShape(Texture2D, bool) except for the return type. Prefer RegisterCustomShape(Texture2D, bool), which hands back the MarkerShape the store methods already take, so no cast is needed at the call site.
Render(MapViewTransform)
Uploads dirty markers if needed and draws the bulk tier for this view.
public void Render(MapViewTransform view)
Parameters
viewMapViewTransformCurrent map view.
TryPick(MapViewTransform, ScreenPoint, double, out int)
Nearest marker to screen within
radiusPx, or false when this layer is hidden.
public bool TryPick(MapViewTransform view, ScreenPoint screen, double radiusPx, out int slot)
Parameters
viewMapViewTransformCurrent map view.
screenScreenPointPick point in screen px, origin top-left.
radiusPxdoubleMaximum distance in screen px.
slotintHit slot index, or -1. Use MarkerStore.HandleOf for an id.
Returns
- bool
True when a marker is within
radiusPx.
Remarks
Every interaction path goes through here rather than calling MarkerPicker directly, because Visible was consulted by the renderer alone: below the cluster zoom the markers were not drawn yet stayed hoverable, draggable, clickable and viewport-tracked, 100,000 of them in LansMapStress. Reports a slot index, like MarkerPicker; MarkerStore.HandleOf turns it into an id.
TryPick(MapViewTransform, ScreenPoint, double, out int, out double)
Nearest marker to screen within radiusPx, or false when this layer is hidden.
public bool TryPick(MapViewTransform view, ScreenPoint screen, double radiusPx, out int slot, out double distanceSq)
Parameters
viewMapViewTransformCurrent map view.
screenScreenPointPick point in screen px, origin top-left.
radiusPxdoubleMaximum distance in screen px.
slotintHit slot index, or -1. Use MarkerStore.HandleOf for an id.
distanceSqdoubleSquared distance to the hit in screen px squared.
Returns
- bool
True when a marker is within
radiusPx.