concinnity-core 0.19.0

Runtime vocabulary for the Concinnity engine: GPU layouts, ECS components, registry, CPU kernels
Documentation
// Screen-space text and sprite overlay: single source for every backend.
//
// One alpha-blended quad per glyph, sprite or background box, drawn in the
// composite pass so it sits on top of the tonemapped image. Vertex positions
// arrive in logical pixels with the origin at the top left; the vertex stage
// maps them straight to NDC.
//
// The fragment stage carries three cases, selected per vertex rather than per
// pipeline: a negative u marks a solid background box, a positive `mode` marks
// a textured sprite quad whose `mode` is the alpha multiplier, and everything
// else samples the glyph atlas as a signed distance field.
//
// METAL_BINDINGS selects the Metal host's constant shape, which is a host
// difference rather than a target one: Vulkan pushes the window size while the
// Metal encoder writes it to vertex buffer(0).

// Window size in logical points, 16 B. Mirrors `TextUniforms` in
// gfx/render_types.rs.
struct TextUniforms
{
    float win_width;
    float win_height;
    float _pad0;
    float _pad1;
};

#ifdef METAL_BINDINGS
ConstantBuffer<TextUniforms> uni : register(b0);
#else
[[vk::push_constant]] ConstantBuffer<TextUniforms> uni;
#endif

// The glyph atlas, or a sprite's own RGBA image on the textured path. A
// combined sampler lowers to a texture(0) + sampler(0) pair on Metal, which is
// what that host binds.
[[vk::binding(0, 0)]] Sampler2D<float4> atlas;

// Matches `TextVertex` (32 B: pos at 0, uv at 8, colour at 16, mode at 28),
// which the host input layouts declare and `text_vertex_layout_matches_shaders`
// pins.
//
// `mode` takes a semantic of its own rather than TEXCOORD1: slangc appends its
// own index to whatever the semantic spells, so a trailing digit is multiplied
// by ten on the way out (TEXCOORD1 lands as TEXCOORD index 10). That is
// harmless between two stages that agree, which is why the varyings below keep
// their TEXCOORDn names, but a vertex input has to match the index a host input
// layout declares.
struct TextVertexIn
{
    [[vk::location(0)]] float2 pos : POSITION;
    [[vk::location(1)]] float2 uv : TEXCOORD0;
    [[vk::location(2)]] float3 color : COLOR;
    [[vk::location(3)]] float mode : MODE;
};

// The varyings lead deliberately: D3D packs a stage signature in declaration
// order and links the two stages by matching semantic *and* register, and this
// fragment reads no position at all.
struct TextVertexOut
{
    [[vk::location(0)]] float2 uv : TEXCOORD0;
    [[vk::location(1)]] float3 color : TEXCOORD1;
    [[vk::location(2)]] float mode : TEXCOORD2;
    float4 position : SV_Position;
};

// Vulkan is the exception rather than the rule, exactly as in `fullscreen.slang`:
// the composite pass rasterises through a standard positive-height viewport
// there, so pixel (0,0) already maps to NDC (-1,-1) and the remap is linear.
// Metal and DirectX put NDC +1 at the top of the screen, so they flip.
float text_ndc_y(float pixel_y, float win_height)
{
    __target_switch
    {
    case spirv:
        return (pixel_y / win_height) * 2.0 - 1.0;
    default:
        return 1.0 - (pixel_y / win_height) * 2.0;
    }
}

[shader("vertex")]
TextVertexOut text_vertex_main(TextVertexIn v)
{
    TextVertexOut o;
    o.position = float4(
        (v.pos.x / uni.win_width) * 2.0 - 1.0,
        text_ndc_y(v.pos.y, uni.win_height),
        0.0,
        1.0);
    o.uv = v.uv;
    o.color = v.color;
    o.mode = v.mode;
    return o;
}

[shader("fragment")]
float4 text_fragment_main(TextVertexOut i) : SV_Target
{
    // A negative u marks a solid background-box vertex (a TextLabel.background
    // quad emitted by gfx::text::build_text_calls): emit the colour directly,
    // alpha carried through in v, no atlas sample.
    if (i.uv.x < 0.0)
    {
        return float4(i.color, i.uv.y);
    }
    // A positive mode marks a textured quad (a Sprite with a texture): the
    // bound atlas is the sprite's own RGBA image, tinted by the vertex colour
    // with the mode value as the quad's alpha multiplier.
    if (i.mode > 0.0)
    {
        float4 tex = atlas.Sample(i.uv);
        return float4(tex.rgb * i.color, tex.a * i.mode);
    }
    // The atlas stores a signed distance field: 0.5 = edge, > 0.5 = inside.
    // fwidth gives the screen-space derivative of d, so the smoothstep spans
    // exactly one screen pixel and the glyph stays crisp at any text scale or
    // display density. Sampling d directly as alpha ramps the whole distance
    // field and reads as blurry.
    float d = atlas.Sample(i.uv).r;
    float aa = fwidth(d);
    float a = smoothstep(0.5 - aa, 0.5 + aa, d);
    return float4(i.color, a);
}