/*
 * Copyright 2025 Rive
 */

#ifdef @VERTEX
ATTR_BLOCK_BEGIN(Attrs)
// [localVertexID, outset, fillCoverage, vertexType]
ATTR(0, float4, @a_patchVertexData);
ATTR(1, float4, @a_mirroredVertexData);
ATTR_BLOCK_END
#endif

VARYING_BLOCK_BEGIN
NO_PERSPECTIVE VARYING(0, float4, v_coverages);
VARYING_BLOCK_END

#ifdef @VERTEX
VERTEX_MAIN(@atlasVertexMain, Attrs, attrs, _vertexID, _instanceID)
{
    ATTR_UNPACK(_vertexID, attrs, @a_patchVertexData, float4);
    ATTR_UNPACK(_vertexID, attrs, @a_mirroredVertexData, float4);

    VARYING_INIT(v_coverages, float4);

    float4 pos;
    uint pathID;
    float2 vertexPosition;
    if (unpack_tessellated_path_vertex(@a_patchVertexData,
                                       @a_mirroredVertexData,
                                       _instanceID,
                                       pathID,
                                       vertexPosition,
                                       v_coverages VERTEX_CONTEXT_UNPACK))
    {
        // Offset from on-screen coordinates to atlas coordinates.
        uint4 pathData2 = STORAGE_BUFFER_LOAD4(@pathBuffer, pathID * 4u + 2u);
        float3 atlasTransform = uintBitsToFloat(pathData2.yzw);
        vertexPosition = vertexPosition * atlasTransform.x + atlasTransform.yz;

        pos = pixel_coord_to_clip_coord(vertexPosition,
                                        uniforms.atlasContentInverseViewport.x,
                                        uniforms.atlasContentInverseViewport.y);
#ifdef @POST_INVERT_Y
        pos.y = -pos.y;
#endif
    }
    else
    {
        pos = float4(uniforms.vertexDiscardValue,
                     uniforms.vertexDiscardValue,
                     uniforms.vertexDiscardValue,
                     uniforms.vertexDiscardValue);
    }

    VARYING_PACK(v_coverages);
    EMIT_VERTEX(pos);
}
#endif // @VERTEX

#ifdef @FRAGMENT

#ifdef @ATLAS_RENDER_TARGET_R32UI_FRAMEBUFFER_FETCH

// Store coverage as fp32 data bits in an r32ui color buffer, and use
// framebuffer-fetch to manipulate it.
layout(location = 0) inout highp uvec4 _fragCoverage;

#ifdef @ATLAS_FEATHERED_FILL
void main()
{
    float coverage = uintBitsToFloat(_fragCoverage.r);
    coverage += eval_feathered_fill(v_coverages);
    _fragCoverage.r = floatBitsToUint(coverage);
}
#endif

#ifdef @ATLAS_FEATHERED_STROKE
void main()
{
    float coverage = uintBitsToFloat(_fragCoverage.r);
    coverage = max(coverage, eval_feathered_stroke(v_coverages));
    _fragCoverage.r = floatBitsToUint(coverage);
}
#endif

#elif defined(@ATLAS_RENDER_TARGET_R32UI_PLS_EXT)

// Manipulate fp32 coverage in pixel local storage, which will be written out
// to an r32ui color buffer during a separate resolve step.
__pixel_localEXT PLS { layout(r32f) highp float _plsCoverage; };

#ifdef @ATLAS_FEATHERED_FILL
void main() { _plsCoverage += eval_feathered_fill(v_coverages); }
#endif

#ifdef @ATLAS_FEATHERED_STROKE
void main()
{
    _plsCoverage = max(_plsCoverage, eval_feathered_stroke(v_coverages));
}
#endif

#elif defined(@ATLAS_RENDER_TARGET_R32UI_PLS_ANGLE)

// Store and manipulate coverage as fp32 data bits in r32ui-texture-backed pixel
// local storage.
layout(binding = 0, r32ui) uniform highp upixelLocalANGLE _plsCoverage;

#ifdef @ATLAS_FEATHERED_FILL
void main()
{
    float coverage = uintBitsToFloat(pixelLocalLoadANGLE(_plsCoverage).r);
    coverage += eval_feathered_fill(v_coverages);
    pixelLocalStoreANGLE(_plsCoverage, uint4(floatBitsToUint(coverage)));
}
#endif

#ifdef @ATLAS_FEATHERED_STROKE
void main()
{
    float coverage = uintBitsToFloat(pixelLocalLoadANGLE(_plsCoverage).r);
    coverage = max(coverage, eval_feathered_stroke(v_coverages));
    pixelLocalStoreANGLE(_plsCoverage, uint4(floatBitsToUint(coverage)));
}
#endif

#elif defined(@ATLAS_RENDER_TARGET_R32I_ATOMIC_TEXTURE)

// Store coverage as 16:16 fixed point in an r32i texture, which we manipulate
// with atomics.
layout(binding = 0, r32i) uniform highp coherent iimage2D _atlasImage;
ivec2 image_coord() { return ivec2(floor(_fragCoord)); }
int fixedpoint_coverage(float coverage)
{
    return int(coverage * ATLAS_R32I_FIXED_POINT_FACTOR);
}

#ifdef @ATLAS_FEATHERED_FILL
void main()
{
    int coverage = fixedpoint_coverage(eval_feathered_fill(v_coverages));
    imageAtomicAdd(_atlasImage, image_coord(), coverage);
}
#endif

#ifdef @ATLAS_FEATHERED_STROKE
void main()
{
    int coverage = fixedpoint_coverage(eval_feathered_stroke(v_coverages));
    imageAtomicMax(_atlasImage, image_coord(), coverage);
}
#endif

#elif defined(@ATLAS_RENDER_TARGET_RGBA8_UNORM)

// We don't have any extensions to count high precision coverage. (This is very
// rare.). Just split up coverage across rgba8 components and hope for the best.

#ifdef @ATLAS_FEATHERED_FILL
FRAG_DATA_MAIN(half4, @atlasFillFragmentMain)
{
    VARYING_UNPACK(v_coverages, float4);
    half coverage = eval_feathered_fill(v_coverages TEXTURE_CONTEXT_FORWARD);
    // i.e., is abs(coverage) ~= FEATHER(1), allowing for some sub-8-bit slop in
    // the texture unit performing a clamp to edge.
    if (abs(coverage) > MAX_FEATHER - 1e-3)
    {
        // All the "fan triangles" in a feather have solid coverage. This is a
        // substantial number of triangles, so we dedicate 2 channels to
        // counting solid coverage (i.e, +1 or -1). These channels are also much
        // slower to overflow, so it preserves a basic skeleton of the feather
        // when the fractional channels overflow.
        EMIT_FRAG_DATA(coverage > .0
                           // B counts integer, positive coverage.
                           ? make_half4(.0, .0, 1. / 255., .0)
                           // A counts integer, negative coverage.
                           : make_half4(.0, .0, .0, 1. / 255.));
    }
    else
    {
        coverage *= 1. / ATLAS_UNORM8_COVERAGE_SCALE_FACTOR;
        EMIT_FRAG_DATA(make_half4(
            max(coverage, .0),  // R counts fractional, positive coverage.
            max(-coverage, .0), // G counts fractional, negative coverage.
            .0,
            .0));
    }
}
#endif // @ATLAS_FEATHERED_FILL

#ifdef @ATLAS_FEATHERED_STROKE
FRAG_DATA_MAIN(half4, @atlasStrokeFragmentMain)
{
    VARYING_UNPACK(v_coverages, float4);
    half coverage = eval_feathered_stroke(v_coverages TEXTURE_CONTEXT_FORWARD);
    // Strokes only have positive coverage, and since we only need to saturate
    // the max for stroking, we can just use the R channel.
    coverage *= 1. / ATLAS_UNORM8_COVERAGE_SCALE_FACTOR;
    EMIT_FRAG_DATA(make_half4(coverage, .0, .0, .0));
}
#endif // @ATLAS_FEATHERED_STROKE

#else

// This is the ideal case. We have full support for floating point color
// buffers, including blending. Render to float and let the fixed function blend
// hardware count the coverage.

#ifdef @ATLAS_FEATHERED_FILL
FRAG_DATA_MAIN(float, @atlasFillFragmentMain)
{
    VARYING_UNPACK(v_coverages, float4);
    EMIT_FRAG_DATA(eval_feathered_fill(v_coverages TEXTURE_CONTEXT_FORWARD));
}
#endif

#ifdef @ATLAS_FEATHERED_STROKE
FRAG_DATA_MAIN(float, @atlasStrokeFragmentMain)
{
    VARYING_UNPACK(v_coverages, float4);
    EMIT_FRAG_DATA(eval_feathered_stroke(v_coverages TEXTURE_CONTEXT_FORWARD));
}
#endif

#endif

#endif // FRAGMENT
