blob: 4c3dafbb606377115ae9db4565d7a224fc87f185 [file] [log] [blame] [edit]
/*
* 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