//===----------------------------------------------------------------------===//
//  Present.metal — final composite into the output window.
//
//  Draws the upscaled texture as a full-screen triangle, applying the chosen
//  aspect-ratio policy (fit with letterbox/pillarbox, or fill with crop). The
//  fullscreen-triangle trick avoids the diagonal seam a two-triangle quad
//  produces and issues one fewer primitive.
//===----------------------------------------------------------------------===//

#include <metal_stdlib>
using namespace metal;

struct PresentUniforms {
    float2 scale;       // UV scale about the centre; encodes fit vs. fill
    float2 offset;      // UV offset, recentres after scaling
    float4 background;  // letterbox colour
};

struct VSOut {
    float4 position [[position]];
    float2 uv;
};

vertex VSOut present_vertex(uint vid [[vertex_id]]) {
    // Oversized triangle covering the clip cube; the rasteriser clips it.
    float2 pos[3] = { float2(-1.0, -3.0), float2(-1.0, 1.0), float2(3.0, 1.0) };
    float2 uvs[3] = { float2( 0.0,  2.0), float2( 0.0, 0.0), float2(2.0, 0.0) };
    VSOut o;
    o.position = float4(pos[vid], 0.0, 1.0);
    o.uv = uvs[vid];
    return o;
}

fragment float4 present_fragment(VSOut in [[stage_in]],
                                 texture2d<float, access::sample> src [[texture(0)]],
                                 constant PresentUniforms &u          [[buffer(0)]]) {
    constexpr sampler s(coord::normalized, filter::linear, address::clamp_to_edge);

    float2 uv = in.uv * u.scale + u.offset;

    // Outside the source rectangle we are in the letterbox/pillarbox bars.
    // Sampling is clamp_to_edge, so without this test the edge pixels would
    // smear across the bars.
    if (any(uv < 0.0) || any(uv > 1.0)) {
        return u.background;
    }
    return float4(src.sample(s, uv).rgb, 1.0);
}
