gpu: Track position fwidth explicitly

This is relevant went encountering repeat nodes, where the repeat cutoff
will make the fwidth of the position go wild otherwise.

Gradients require more work now, because we need to compute offsets
twice - once for the pixel, once for the offst.
This commit is contained in:
Benjamin Otte 2023-11-02 08:09:13 +01:00
parent 6cbf4667a4
commit a301f18ebf
2 changed files with 80 additions and 37 deletions

View File

@ -24,6 +24,32 @@ stack_pop (void)
return stack[stack_size]; return stack[stack_size];
} }
struct Position
{
/* pos.xy is the actual position
pos.zw is the fwidth() of it
*/
vec4 pos;
};
vec2
position (Position pos)
{
return pos.pos.xy;
}
vec2
position_fwidth (Position pos)
{
return pos.pos.zw;
}
Position
position_new (vec2 pos)
{
return Position (vec4 (pos, fwidth (pos)));
}
uint uint
read_uint (inout uint reader) read_uint (inout uint reader)
{ {
@ -85,18 +111,17 @@ read_gradient (inout uint reader)
void void
clip_pattern (inout uint reader, clip_pattern (inout uint reader,
inout vec4 color, inout vec4 color,
vec2 pos) Position pos)
{ {
Rect clip = read_rect (reader); Rect clip = read_rect (reader);
float alpha = rect_coverage (clip, pos); float alpha = rect_coverage (clip, position (pos), abs (position_fwidth (pos)));
color *= alpha; color *= alpha;
} }
void void
opacity_pattern (inout uint reader, opacity_pattern (inout uint reader,
inout vec4 color, inout vec4 color)
vec2 pos)
{ {
float opacity = read_float (reader); float opacity = read_float (reader);
@ -105,8 +130,7 @@ opacity_pattern (inout uint reader,
void void
color_matrix_pattern (inout uint reader, color_matrix_pattern (inout uint reader,
inout vec4 color, inout vec4 color)
vec2 pos)
{ {
mat4 matrix = read_mat4 (reader); mat4 matrix = read_mat4 (reader);
vec4 offset = read_vec4 (reader); vec4 offset = read_vec4 (reader);
@ -120,24 +144,24 @@ color_matrix_pattern (inout uint reader,
} }
void void
repeat_push_pattern (inout uint reader, repeat_push_pattern (inout uint reader,
inout vec2 pos) inout Position pos)
{ {
stack_push (vec4 (pos, 0.0, 0.0)); stack_push (pos.pos);
Rect bounds = read_rect (reader); Rect bounds = read_rect (reader);
vec2 size = rect_size (bounds); vec2 size = rect_size (bounds);
pos = mod (pos - bounds.bounds.xy, size); pos.pos.xy = mod (pos.pos.xy - bounds.bounds.xy, size);
/* make sure we have a positive result */ /* make sure we have a positive result */
pos = mix (pos, pos + size, lessThan (pos, vec2 (0.0))); pos.pos.xy = mix (pos.pos.xy, pos.pos.xy + size, lessThan (pos.pos.xy, vec2 (0.0)));
pos += bounds.bounds.xy; pos.pos.xy += bounds.bounds.xy;
} }
void void
position_pop_pattern (inout uint reader, position_pop_pattern (inout uint reader,
inout vec2 pos) inout Position pos)
{ {
pos = stack_pop ().xy; pos = Position (stack_pop ());
} }
void void
@ -188,22 +212,24 @@ mask_inverted_luminance_pattern (inout uint reader,
vec4 vec4
glyphs_pattern (inout uint reader, glyphs_pattern (inout uint reader,
vec2 pos) Position pos)
{ {
float opacity = 0.0; float opacity = 0.0;
vec4 color = color_premultiply (read_vec4 (reader)); vec4 color = color_premultiply (read_vec4 (reader));
uint num_glyphs = read_uint (reader); uint num_glyphs = read_uint (reader);
uint i; uint i;
vec2 p = position (pos);
vec2 dFdp = abs (position_fwidth (pos));
for (i = 0u; i < num_glyphs; i++) for (i = 0u; i < num_glyphs; i++)
{ {
uint tex_id = read_uint (reader); uint tex_id = read_uint (reader);
Rect glyph_bounds = read_rect (reader); Rect glyph_bounds = read_rect (reader);
vec4 tex_rect = read_vec4 (reader); vec4 tex_rect = read_vec4 (reader);
float coverage = rect_coverage (glyph_bounds, pos); float coverage = rect_coverage (glyph_bounds, p, dFdp);
if (coverage > 0.0) if (coverage > 0.0)
opacity += coverage * gsk_texture (tex_id, (pos - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)).a; opacity += coverage * gsk_texture (tex_id, (p - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)).a;
} }
return color * opacity; return color * opacity;
@ -211,27 +237,27 @@ glyphs_pattern (inout uint reader,
vec4 vec4
texture_pattern (inout uint reader, texture_pattern (inout uint reader,
vec2 pos) Position pos)
{ {
uint tex_id = read_uint (reader); uint tex_id = read_uint (reader);
vec4 tex_rect = read_vec4 (reader); vec4 tex_rect = read_vec4 (reader);
return gsk_texture (tex_id, (pos - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)); return gsk_texture (tex_id, (position (pos) - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw));
} }
vec4 vec4
straight_alpha_pattern (inout uint reader, straight_alpha_pattern (inout uint reader,
vec2 pos) Position pos)
{ {
uint tex_id = read_uint (reader); uint tex_id = read_uint (reader);
vec4 tex_rect = read_vec4 (reader); vec4 tex_rect = read_vec4 (reader);
return gsk_texture_straight_alpha (tex_id, (pos - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)); return gsk_texture_straight_alpha (tex_id, (position (pos) - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw));
} }
vec4 vec4
linear_gradient_pattern (inout uint reader, linear_gradient_pattern (inout uint reader,
vec2 pos, Position pos,
bool repeating) bool repeating)
{ {
vec2 start = read_vec2 (reader) * GSK_GLOBAL_SCALE; vec2 start = read_vec2 (reader) * GSK_GLOBAL_SCALE;
@ -240,8 +266,9 @@ linear_gradient_pattern (inout uint reader,
vec2 line = end - start; vec2 line = end - start;
float line_length = dot (line, line); float line_length = dot (line, line);
float offset = dot (pos - start, line) / line_length; float offset = dot (position (pos) - start, line) / line_length;
float d_offset = 0.5 * fwidth (offset); float other_offset = dot (position (pos) + position_fwidth (pos) - start, line) / line_length;
float d_offset = 0.5 * abs (offset - other_offset);
if (repeating) if (repeating)
return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset); return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset);
@ -251,7 +278,7 @@ linear_gradient_pattern (inout uint reader,
vec4 vec4
radial_gradient_pattern (inout uint reader, radial_gradient_pattern (inout uint reader,
vec2 pos, Position pos,
bool repeating) bool repeating)
{ {
vec2 center = read_vec2 (reader) * GSK_GLOBAL_SCALE; vec2 center = read_vec2 (reader) * GSK_GLOBAL_SCALE;
@ -260,9 +287,11 @@ radial_gradient_pattern (inout uint reader,
float end = read_float (reader); float end = read_float (reader);
Gradient gradient = read_gradient (reader); Gradient gradient = read_gradient (reader);
float offset = length ((pos - center) / radius); float offset = length ((position (pos) - center) / radius);
float other_offset = length ((position (pos) + position_fwidth (pos) - center) / radius);
offset = (offset - start) / (end - start); offset = (offset - start) / (end - start);
float d_offset = 0.5 * fwidth (offset); other_offset = (other_offset - start) / (end - start);
float d_offset = abs (0.5 * (offset - other_offset));
if (repeating) if (repeating)
return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset); return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset);
@ -272,19 +301,24 @@ radial_gradient_pattern (inout uint reader,
vec4 vec4
conic_gradient_pattern (inout uint reader, conic_gradient_pattern (inout uint reader,
vec2 pos) Position pos)
{ {
vec2 center = read_vec2 (reader); vec2 center = read_vec2 (reader);
float angle = read_float (reader); float angle = read_float (reader);
Gradient gradient = read_gradient (reader); Gradient gradient = read_gradient (reader);
/* scaling modifies angles, so be sure to use right coordinate system */ /* scaling modifies angles, so be sure to use right coordinate system */
pos = pos / GSK_GLOBAL_SCALE - center; vec2 dpos = position (pos) / GSK_GLOBAL_SCALE - center;
float offset = atan (pos.y, pos.x); vec2 dpos2 = (position (pos) + position_fwidth (pos)) / GSK_GLOBAL_SCALE - center;
float offset = atan (dpos.y, dpos.x);
float offset2 = atan (dpos2.y, dpos2.x);
offset = degrees (offset + angle) / 360.0; offset = degrees (offset + angle) / 360.0;
offset2 = degrees (offset2 + angle) / 360.0;
float overflow = fract (offset + 0.5); float overflow = fract (offset + 0.5);
float overflow2 = fract (offset2 + 0.5);
offset = fract (offset); offset = fract (offset);
float d_offset = max (0.00001, 0.5 * min (fwidth (offset), fwidth (overflow))); offset2 = fract (offset2);
float d_offset = max (0.00001, 0.5 * min (abs (offset - offset2), abs (overflow - overflow2)));
return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset); return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset);
} }
@ -299,9 +333,10 @@ color_pattern (inout uint reader)
vec4 vec4
pattern (uint reader, pattern (uint reader,
vec2 pos) vec2 pos_)
{ {
vec4 color = vec4 (1.0, 0.0, 0.8, 1.0); /* pink */ vec4 color = vec4 (1.0, 0.0, 0.8, 1.0); /* pink */
Position pos = position_new (pos_);
for(;;) for(;;)
{ {
@ -324,10 +359,10 @@ pattern (uint reader,
color = glyphs_pattern (reader, pos); color = glyphs_pattern (reader, pos);
break; break;
case GSK_GPU_PATTERN_COLOR_MATRIX: case GSK_GPU_PATTERN_COLOR_MATRIX:
color_matrix_pattern (reader, color, pos); color_matrix_pattern (reader, color);
break; break;
case GSK_GPU_PATTERN_OPACITY: case GSK_GPU_PATTERN_OPACITY:
opacity_pattern (reader, color, pos); opacity_pattern (reader, color);
break; break;
case GSK_GPU_PATTERN_LINEAR_GRADIENT: case GSK_GPU_PATTERN_LINEAR_GRADIENT:
color = linear_gradient_pattern (reader, pos, false); color = linear_gradient_pattern (reader, pos, false);

View File

@ -78,15 +78,23 @@ rect_get_coord (Rect r, vec2 pt)
#ifdef GSK_FRAGMENT_SHADER #ifdef GSK_FRAGMENT_SHADER
float float
rect_coverage (Rect r, vec2 p) rect_coverage (Rect r,
vec2 p,
vec2 dFdp)
{ {
vec2 dFdp = abs(fwidth (p));
Rect prect = Rect(vec4(p - 0.5 * dFdp, p + 0.5 * dFdp)); Rect prect = Rect(vec4(p - 0.5 * dFdp, p + 0.5 * dFdp));
Rect coverect = rect_intersect (r, prect); Rect coverect = rect_intersect (r, prect);
vec2 coverage = rect_size(coverect) / dFdp; vec2 coverage = rect_size(coverect) / dFdp;
return coverage.x * coverage.y; return coverage.x * coverage.y;
} }
float
rect_coverage (Rect r, vec2 p)
{
return rect_coverage (r, p, abs (fwidth (p)));
}
#endif #endif
#endif #endif