Skip to content
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
12 changes: 9 additions & 3 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,16 @@ independently, so the unit of tessellation is the tile:

- **Layout** runs per (tile × style-layer) on worker threads: decode →
filter → evaluate data-driven properties → tessellate into *buckets*.
A bucket's vertices are **tile-local f32** (origin at tile corner); each
tile draws with its own tile→clip matrix (lookout's origin-relative rule:
A bucket's vertices are **tile-local f32** (origin at tile corner), so a
cached bucket is reusable at any camera (lookout's origin-relative rule:
absolute world f32 quantizes visibly at depth; `overlay.zig` proved the
fix).
fix). At CONCATENATION the buckets are rebased onto one scene origin, and
the scene draws with one matrix, one draw per (tile × layer). The host
therefore picks the world copy at the antimeridian, per tile and per scene,
because a per-vertex choice splits a primitive lying across the seam
(`Camera.placeTileX`). A tile's own bounds also travel with its geometry,
because a merged scene has no per-tile draw state to clip against
(`scene.CLIP_NONE`).
- **Fills and lines** draw straight from resident buckets — pan/zoom/rotate
never re-tessellates; a zoom change is a matrix change until the tile set
itself changes (then new tiles lay out async while old ones keep drawing —
Expand Down
1 change: 1 addition & 0 deletions examples/macos/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ int main(int argc, const char *argv[]) {
if ((ev = getenv("CHARTTABLE_LON"))) v.lon = atof(ev);
if ((ev = getenv("CHARTTABLE_LAT"))) v.lat = atof(ev);
if ((ev = getenv("CHARTTABLE_ZOOM"))) v.zoom = atof(ev);
if ((ev = getenv("CHARTTABLE_BEARING"))) v.bearing_deg = atof(ev);

BOOL composing = NO;
#ifdef USE_TILE57_COMPOSE
Expand Down
2 changes: 1 addition & 1 deletion shaders/d3d12/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Three root parameters and one static sampler, shared by all five pipelines:

Both CBVs are root descriptors pointed at a per-draw slice of one upload-heap
ring, so a draw costs one address write and no descriptor allocation. The block
is `scene.Uniforms`, 128 bytes; HLSL's constant-buffer packing lands every field
is `scene.Uniforms`, 144 bytes; HLSL's constant-buffer packing lands every field
at the offset the struct declares.

## Conventions this backend does not have to correct for
Expand Down
28 changes: 28 additions & 0 deletions shaders/d3d12/fill.frag.hlsl
Original file line number Diff line number Diff line change
@@ -1,11 +1,39 @@
// Flat color straight from stream B, blended in paint order by the
// fixed-function blend state.

// b0 is visible to both stages (the root signature says ALL), so the pixel
// stage reads the same block the vertex stage did.
cbuffer U : register(b0) {
column_major float4x4 u_mvp;
float2 u_px_to_clip;
float u_size_scale;
float u_zoom;
float u_zoom_t;
float u_world_per_px;
float u_rot_sin;
float u_rot_cos;
float4 u_color;
float2 u_anchor_px;
float2 u_cell_px;
float4 u_clip_rect;
};

// A tile's triangles paint their own tile. The geometry keeps the buffered
// overhang a line's joins are built from, and the draw trims it
// (scene.CLIP_NONE). A draw with no tile of its own is set to CLIP_NONE and
// pays one compare.
bool clipped(float2 world) {
return world.x < u_clip_rect.x || world.y < u_clip_rect.y ||
world.x > u_clip_rect.z || world.y > u_clip_rect.w;
}

struct PSIn {
float4 pos : SV_Position;
float4 color : TEXCOORD0;
float2 world : TEXCOORD1;
};

float4 main(PSIn i) : SV_Target {
if (clipped(i.world)) discard;
return i.color;
}
31 changes: 24 additions & 7 deletions shaders/d3d12/fill.vert.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ cbuffer U : register(b0) {
float u_size_scale;
float u_zoom; // fractional zoom * 256
float u_zoom_t;
float u_wrap_x;
float u_world_per_px;
float u_rot_sin;
float u_rot_cos;
float4 u_color;
float2 u_anchor_px;
float2 u_cell_px;
float4 u_clip_rect; // the tile a triangle draw may paint: x0, y0, x1, y1
};

struct VSIn {
Expand All @@ -35,15 +36,16 @@ struct VSIn {
struct VSOut {
float4 pos : SV_Position;
float4 color : TEXCOORD0;
float2 world : TEXCOORD1;
};

// Longitude is cyclic: draw each vertex at the world instance nearest the
// camera, so a view straddling the antimeridian is seamless. The world period
// is exactly 1.0 in ANY translated frame, so this works on tile-local
// coordinates as long as u_wrap_x is stated in the same frame.
// Longitude is cyclic, so a tile has a world copy every 1.0 world units and
// one of them is drawn. The host picks it, once per tile (Camera.placeTileX)
// and once per scene in the matrix (Camera.mvpOrigin wraps its x delta).
// Picking it here, per vertex, split every primitive lying across the
// half-world seam.
float4 project(float2 p) {
float2 world = float2(p.x + round(u_wrap_x - p.x), p.y);
return mul(u_mvp, float4(world, 0.0, 1.0));
return mul(u_mvp, float4(p, 0.0, 1.0));
}

// The per-vertex zoom visibility window, quantized to 1/256 zoom steps and
Expand All @@ -57,6 +59,20 @@ bool gate(uint zwin) {
// (ox, oy) is added AFTER projection, in reference px. map_align means it is
// stated in the MAP frame: a rotated view must turn it, or the pen shears to
// |cos(rotation)| of its width.
// The world position a vertex's fragments cover: the vertex position plus its
// screen-space (ox, oy) converted to world units. The offset is applied after
// projection, so a line's stroke reaches ground its anchor does not, and a
// clip test against the anchor alone cuts the stroke along its length.
//
// Undoing the projection needs no inverse. The linear part of mvp is scale
// times rotation, so R(-view) applied to the offset and divided by pixels per
// world unit gives the world delta of the clip-space offset.
float2 world_of(float2 p, float2 off) {
float2 back = float2( off.x * u_rot_cos + off.y * u_rot_sin,
-off.x * u_rot_sin + off.y * u_rot_cos);
return p + back * u_size_scale * u_world_per_px;
}

float2 screen_offset(float2 off, uint flags) {
if ((flags & 1) != 0) {
off = float2(off.x * u_rot_cos - off.y * u_rot_sin,
Expand All @@ -82,5 +98,6 @@ VSOut main(VSIn i) {
// Clamped: mid-gesture zoom_t can leave [0,1]; hold the end color
// rather than wrapping to the far one.
o.color = lerp(i.a_color, i.a_color_hi, saturate(u_zoom_t));
o.world = world_of(i.a_pos, off);
return o;
}
11 changes: 6 additions & 5 deletions shaders/d3d12/overlay.vert.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ cbuffer U : register(b0) {
float u_size_scale;
float u_zoom;
float u_zoom_t;
float u_wrap_x;
float u_world_per_px;
float u_rot_sin;
float u_rot_cos;
float4 u_color;
float2 u_anchor_px;
float2 u_cell_px;
float4 u_clip_rect; // the tile a triangle draw may paint: x0, y0, x1, y1
};

struct VSIn {
Expand All @@ -28,10 +29,10 @@ struct VSOut {

VSOut main(VSIn i) {
VSOut o;
// The chart shader's antimeridian wrap: draw at the world instance nearest
// the camera. A whole world width is 1.0 in the relative frame too.
float2 world = float2(i.a_world.x + round(u_wrap_x - i.a_world.x), i.a_world.y);
float4 clip = mul(u_mvp, float4(world, 0.0, 1.0));
// The host places the whole frame at once, in the matrix
// (Camera.mvpOrigin). A per-vertex wrap split any primitive lying across
// the half-world seam.
float4 clip = mul(u_mvp, float4(i.a_world, 0.0, 1.0));
// z = 0 is the near plane. The chart's paint-order depths are all in (0,1)
// and this pass writes no depth, so host content is never hidden by the
// chart and never hides it from a later pass.
Expand Down
28 changes: 28 additions & 0 deletions shaders/d3d12/pattern.frag.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,41 @@
Texture2D cell_tex : register(t0);
SamplerState cell_smp : register(s0);

// b0 is visible to both stages (the root signature says ALL), so the pixel
// stage reads the same block the vertex stage did.
cbuffer U : register(b0) {
column_major float4x4 u_mvp;
float2 u_px_to_clip;
float u_size_scale;
float u_zoom;
float u_zoom_t;
float u_world_per_px;
float u_rot_sin;
float u_rot_cos;
float4 u_color;
float2 u_anchor_px;
float2 u_cell_px;
float4 u_clip_rect;
};

// A tile's triangles paint their own tile. The geometry keeps the buffered
// overhang a line's joins are built from, and the draw trims it
// (scene.CLIP_NONE). A draw with no tile of its own is set to CLIP_NONE and
// pays one compare.
bool clipped(float2 world) {
return world.x < u_clip_rect.x || world.y < u_clip_rect.y ||
world.x > u_clip_rect.z || world.y > u_clip_rect.w;
}

struct PSIn {
float4 pos : SV_Position;
float2 anchor : TEXCOORD0;
float2 cell : TEXCOORD1;
float2 world : TEXCOORD2;
};

float4 main(PSIn i) : SV_Target {
if (clipped(i.world)) discard;
float2 sz = max(i.cell, float2(1.0, 1.0));
float2 uv = frac((i.pos.xy - i.anchor) / sz);
float4 c = cell_tex.Sample(cell_smp, uv);
Expand Down
15 changes: 12 additions & 3 deletions shaders/d3d12/pattern.vert.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ cbuffer U : register(b0) {
float u_size_scale;
float u_zoom;
float u_zoom_t;
float u_wrap_x;
float u_world_per_px;
float u_rot_sin;
float u_rot_cos;
float4 u_color;
float2 u_anchor_px;
float2 u_cell_px;
float4 u_clip_rect; // the tile a triangle draw may paint: x0, y0, x1, y1
};

struct VSIn {
Expand All @@ -30,12 +31,19 @@ struct VSOut {
float4 pos : SV_Position;
float2 anchor : TEXCOORD0;
float2 cell : TEXCOORD1;
float2 world : TEXCOORD2;
};

// See fill.vert.hlsl: the world position a vertex's fragments actually land on.
float2 world_of(float2 p, float2 off) {
float2 back = float2( off.x * u_rot_cos + off.y * u_rot_sin,
-off.x * u_rot_sin + off.y * u_rot_cos);
return p + back * u_size_scale * u_world_per_px;
}

VSOut main(VSIn i) {
VSOut o;
float2 world = float2(i.a_pos.x + round(u_wrap_x - i.a_pos.x), i.a_pos.y);
float4 clip = mul(u_mvp, float4(world, 0.0, 1.0));
float4 clip = mul(u_mvp, float4(i.a_pos, 0.0, 1.0));
float2 off = i.a_off;
if ((i.a_flags & 1) != 0) {
off = float2(off.x * u_rot_cos - off.y * u_rot_sin,
Expand All @@ -50,5 +58,6 @@ VSOut main(VSIn i) {
o.pos = vis ? clip : float4(0.0, 0.0, 2.0, 1.0);
o.anchor = u_anchor_px;
o.cell = u_cell_px;
o.world = world_of(i.a_pos, off);
return o;
}
3 changes: 2 additions & 1 deletion shaders/d3d12/sdf.frag.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ cbuffer U : register(b1) {
float u_size_scale;
float u_zoom;
float u_zoom_t;
float u_wrap_x;
float u_world_per_px;
float u_rot_sin;
float u_rot_cos;
float4 u_color; // the SDF halo color for this draw
float2 u_anchor_px;
float2 u_cell_px;
float4 u_clip_rect; // the tile a triangle draw may paint: x0, y0, x1, y1
};

struct PSIn {
Expand Down
6 changes: 3 additions & 3 deletions shaders/d3d12/sprite.vert.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ cbuffer U : register(b0) {
float u_size_scale;
float u_zoom;
float u_zoom_t;
float u_wrap_x;
float u_world_per_px;
float u_rot_sin;
float u_rot_cos;
float4 u_color;
float2 u_anchor_px;
float2 u_cell_px;
float4 u_clip_rect; // the tile a triangle draw may paint: x0, y0, x1, y1
};

struct VSIn {
Expand Down Expand Up @@ -45,8 +46,7 @@ VSOut main(VSIn i) {
bool flip = ((i.a_pack >> 8) & 0xFF) != 0;
float tangent = float((i.a_pack >> 16) & 0xFF) / 256.0 * 6.2831853071795864;

float2 world = float2(i.a_pos.x + round(u_wrap_x - i.a_pos.x), i.a_pos.y);
float4 clip = mul(u_mvp, float4(world, 0.0, 1.0));
float4 clip = mul(u_mvp, float4(i.a_pos, 0.0, 1.0));

float2 off = i.a_off;
// Keep a tangent-rotated run (line-following text) upright: if the run,
Expand Down
Loading
Loading