Clay custom dispatch improvements & DPI scaling fixes (#26)

Co-authored-by: Zachary Levy <zachary@sunforge.is>
Reviewed-on: #26
This commit was merged in pull request #26.
This commit is contained in:
2026-05-06 04:17:24 +00:00
parent e8ffa28de3
commit 43f08ed30c
19 changed files with 627 additions and 407 deletions
+40 -10
View File
@@ -21,8 +21,8 @@ auto_segments :: proc(radius: f32, arc_degrees: f32) -> int {
// ----- Internal helpers -----
// Color is premultiplied: the tessellated fragment shader passes it through directly
// and the blend state is ONE, ONE_MINUS_SRC_ALPHA.
// Premultiplies the color before storing it on the vertex (see draw package doc's
// "Color and blending" section for why).
//INTERNAL
solid_vertex :: proc(position: draw.Vec2, color: draw.Color) -> draw.Vertex_2D {
return draw.Vertex_2D{position = position, color = draw.premultiply_color(color)}
@@ -108,16 +108,23 @@ triangle :: proc(
draw.prepare_shape(layer, vertices[:])
}
// Draw an anti-aliased triangle via extruded edge quads.
// Draw an anti-aliased triangle via extruded edge quads plus corner fan caps.
// Interior vertices get the full premultiplied color; outer fringe vertices get BLANK (0,0,0,0).
// The rasterizer linearly interpolates between them, producing a smooth 1-pixel AA band.
// `aa_px` controls the extrusion width in logical pixels (default 1.0).
// This proc emits 21 vertices (3 interior + 6 edge quads × 3 verts each).
// The rasterizer linearly interpolates between them, producing a smooth ~1-physical-pixel AA band.
// `aa_ppx` controls the extrusion width in *physical* pixels (default 1.0). The CPU divides by
// `dpi_scaling` here so the vertex stream stays in logical px; the mode-0 vertex shader scales
// back to physical at draw time. Net AA band is ~aa_ppx physical pixels regardless of DPI.
//
// Topology: 3 interior verts + 6 edge-quad triangles (×3 verts) + 3 corner-fan triangles (×3 verts)
// = 30 verts total. The corner fans plug the wedge gaps that would otherwise appear between
// adjacent edge fringes at each triangle vertex; without them, sharp corners show a small
// background-colored crescent. Apex vertex is full color, both fringe verts are BLANK, so the
// fan rasterizes as an alpha-falloff triangle that blends visually into the adjacent edge bands.
triangle_aa :: proc(
layer: ^draw.Layer,
v1, v2, v3: draw.Vec2,
color: draw.Color,
aa_px: f32 = draw.DFT_FEATHER_PX,
aa_ppx: f32 = draw.DFT_FEATHER_PPX,
origin: draw.Vec2 = {},
rotation: f32 = 0,
) {
@@ -164,7 +171,9 @@ triangle_aa :: proc(
normal_12 := edge_normal(p1, p2, centroid_x, centroid_y)
normal_20 := edge_normal(p2, p0, centroid_x, centroid_y)
extrude_distance := aa_px * draw.GLOB.dpi_scaling
// aa_ppx is in physical pixels; divide by dpi_scaling so the extrusion lives in logical-pixel
// space (the mode-0 vertex shader will scale back to physical at draw time).
extrude_distance := aa_ppx / draw.GLOB.dpi_scaling
// Outer fringe vertices: each edge vertex extruded outward
outer_0_01 := p0 + normal_01 * extrude_distance
@@ -178,8 +187,8 @@ triangle_aa :: proc(
// Outer fringe is BLANK = {0,0,0,0} which is already premul.
transparent := draw.BLANK
// 3 interior + 6 × 3 edge-quad = 21 vertices
vertices: [21]draw.Vertex_2D
// 3 interior + 6 edge-quad tris (×3 verts) + 3 corner-fan tris (×3 verts) = 30 vertices
vertices: [30]draw.Vertex_2D
// Interior triangle
vertices[0] = solid_vertex(p0, color)
@@ -210,6 +219,27 @@ triangle_aa :: proc(
vertices[19] = solid_vertex(outer_0_20, transparent)
vertices[20] = solid_vertex(outer_2_20, transparent)
// Corner fan caps: each fills the wedge gap between the two edge fringes meeting at a
// triangle vertex. Apex is full color; both fringe verts are BLANK, so the rasterizer
// produces a smooth alpha falloff across the wedge (matches the adjacent edge-band
// gradients at the shared edges, so the seams are invisible). Vertex order per fan:
// [apex, fringe-from-incoming-edge, fringe-from-outgoing-edge].
// Cap at p0 (between incoming edge p2→p0 and outgoing edge p0→p1)
vertices[21] = solid_vertex(p0, color)
vertices[22] = solid_vertex(outer_0_20, transparent)
vertices[23] = solid_vertex(outer_0_01, transparent)
// Cap at p1 (between incoming edge p0→p1 and outgoing edge p1→p2)
vertices[24] = solid_vertex(p1, color)
vertices[25] = solid_vertex(outer_1_01, transparent)
vertices[26] = solid_vertex(outer_1_12, transparent)
// Cap at p2 (between incoming edge p1→p2 and outgoing edge p2→p0)
vertices[27] = solid_vertex(p2, color)
vertices[28] = solid_vertex(outer_2_12, transparent)
vertices[29] = solid_vertex(outer_2_20, transparent)
draw.prepare_shape(layer, vertices[:])
}