Compare commits

...

4 Commits

Author SHA1 Message Date
Matthias Clasen
b9d21f1f1a ngl: Add a shader for short linear gradients
Almost all our linear gradients have 2 stops.
Add a simple shader for that case.
2021-03-10 08:44:32 -05:00
Matthias Clasen
8baf29063c ngl: Skip outset shadows when possible
If the current clip is entirely contained in
the outline of an outset shadow, we don't need
to draw anything.
2021-03-10 08:44:32 -05:00
Matthias Clasen
70bf1ed0e4 ngl: Special-case CSS backgrounds
Recognize the rounded-clip-with-color-inside situation
and do manual nine-slicing for it to minimize the number
of pixels send through clipping shaders.
2021-03-10 08:44:32 -05:00
Matthias Clasen
a041c18676 Add shader statistics again
This roughly counts how many pixels we feed through
each fragment shader.
2021-03-09 21:21:02 -05:00
5 changed files with 315 additions and 5 deletions

View File

@@ -7,6 +7,7 @@ gsk_private_gl_shaders = [
'resources/glsl/coloring.glsl',
'resources/glsl/color.glsl',
'resources/glsl/linear_gradient.glsl',
'resources/glsl/linear_gradient_2.glsl',
'resources/glsl/radial_gradient.glsl',
'resources/glsl/conic_gradient.glsl',
'resources/glsl/color_matrix.glsl',

View File

@@ -57,6 +57,12 @@ GSK_NGL_DEFINE_PROGRAM (linear_gradient,
GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points)
GSK_NGL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat))
GSK_NGL_DEFINE_PROGRAM (linear_gradient_2,
"/org/gtk/libgsk/glsl/linear_gradient_2.glsl",
GSK_NGL_ADD_UNIFORM (1, LINEAR_GRADIENT_2_COLOR_STOPS, u_color_stops)
GSK_NGL_ADD_UNIFORM (2, LINEAR_GRADIENT_2_POINTS, u_points)
GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_2_REPEAT, u_repeat))
GSK_NGL_DEFINE_PROGRAM (outset_shadow,
"/org/gtk/libgsk/glsl/outset_shadow.glsl",
GSK_NGL_ADD_UNIFORM (1, OUTSET_SHADOW_COLOR, u_color)

View File

@@ -286,6 +286,8 @@ gsk_ngl_renderer_dispose (GObject *object)
G_OBJECT_CLASS (gsk_ngl_renderer_parent_class)->dispose (object);
}
extern void dump_program_counters (void);
static void
gsk_ngl_renderer_class_init (GskNglRendererClass *klass)
{
@@ -298,6 +300,8 @@ gsk_ngl_renderer_class_init (GskNglRendererClass *klass)
renderer_class->unrealize = gsk_ngl_renderer_unrealize;
renderer_class->render = gsk_ngl_renderer_render;
renderer_class->render_texture = gsk_ngl_renderer_render_texture;
g_atexit (dump_program_counters);
}
static void

View File

@@ -874,6 +874,9 @@ gsk_ngl_render_job_update_clip (GskNglRenderJob *job,
return TRUE;
}
static void update_program_counters (GskNglRenderJob *job,
float width, float height);
/* load_vertex_data_with_region */
static inline void
gsk_ngl_render_job_load_vertices_from_offscreen (GskNglRenderJob *job,
@@ -888,6 +891,8 @@ gsk_ngl_render_job_load_vertices_from_offscreen (GskNglRenderJob *jo
float y1 = offscreen->was_offscreen ? offscreen->area.y2 : offscreen->area.y;
float y2 = offscreen->was_offscreen ? offscreen->area.y : offscreen->area.y2;
update_program_counters (job, max_x - min_x, max_y - min_y);
vertices[0].position[0] = min_x;
vertices[0].position[1] = min_y;
vertices[0].uv[0] = offscreen->area.x;
@@ -933,6 +938,8 @@ gsk_ngl_render_job_draw (GskNglRenderJob *job,
float max_x = min_x + width;
float max_y = min_y + height;
update_program_counters (job, width, height);
vertices[0].position[0] = min_x;
vertices[0].position[1] = min_y;
vertices[0].uv[0] = 0;
@@ -986,6 +993,8 @@ gsk_ngl_render_job_draw_coords (GskNglRenderJob *job,
{
GskNglDrawVertex *vertices = gsk_ngl_command_queue_add_vertices (job->command_queue);
update_program_counters (job, max_x - min_x, max_y - min_y);
vertices[0].position[0] = min_x;
vertices[0].position[1] = min_y;
vertices[0].uv[0] = 0;
@@ -1030,6 +1039,68 @@ gsk_ngl_render_job_draw_offscreen_rect (GskNglRenderJob *job,
gsk_ngl_render_job_draw_coords (job, min_x, min_y, max_x, max_y);
}
typedef struct {
#define GSK_NGL_NO_UNIFORMS
#define GSK_NGL_ADD_UNIFORM(pos, KEY, name)
#define GSK_NGL_DEFINE_PROGRAM(name, resource, uniforms) \
guint64 name ## _no_clip; \
guint64 name ## _rect_clip; \
guint64 name;
# include "gsknglprograms.defs"
#undef GSK_NGL_NO_UNIFORMS
#undef GSK_NGL_ADD_UNIFORM
#undef GSK_NGL_DEFINE_PROGRAM
} ProgramCounters;
static ProgramCounters counters;
void dump_program_counters (void);
void
dump_program_counters (void)
{
#define GSK_NGL_NO_UNIFORMS
#define GSK_NGL_ADD_UNIFORM(pos, KEY, name)
#define PRINT_ONE(name) \
g_print ("%lu " #name "\n", counters.name);
#define GSK_NGL_DEFINE_PROGRAM(name, resource, uniforms) \
PRINT_ONE(name ## _no_clip) \
PRINT_ONE(name ## _rect_clip) \
PRINT_ONE(name)
# include "gsknglprograms.defs"
#undef GSK_NGL_NO_UNIFORMS
#undef GSK_NGL_ADD_UNIFORM
#undef GSK_NGL_DEFINE_PROGRAM
}
static void
update_prog_counters (GskNglRenderJob *job,
GskNglProgram *program,
float width, float height)
{
guint64 pixels = ceilf (width) * ceilf (height);
#define GSK_NGL_NO_UNIFORMS
#define GSK_NGL_ADD_UNIFORM(pos, KEY, name)
#define GSK_NGL_DEFINE_PROGRAM(name, resource, uniforms) \
if (program == job->driver->name) \
counters.name += pixels; \
else if (program == job->driver->name ## _no_clip) \
counters.name ## _no_clip += pixels; \
else if (program == job->driver->name ## _rect_clip) \
counters.name ## _rect_clip += pixels;
# include "gsknglprograms.defs"
#undef GSK_NGL_NO_UNIFORMS
#undef GSK_NGL_ADD_UNIFORM
#undef GSK_NGL_DEFINE_PROGRAM
}
static void
update_program_counters (GskNglRenderJob *job,
float width, float height)
{
update_prog_counters (job, job->current_program, width, height);
}
static inline void
gsk_ngl_render_job_begin_draw (GskNglRenderJob *job,
GskNglProgram *program)
@@ -1422,6 +1493,37 @@ gsk_ngl_render_job_visit_linear_gradient_node (GskNglRenderJob *job,
gsk_ngl_render_job_end_draw (job);
}
static inline void
gsk_ngl_render_job_visit_linear_gradient_2_node (GskNglRenderJob *job,
const GskRenderNode *node)
{
const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL);
const graphene_point_t *start = gsk_linear_gradient_node_get_start (node);
const graphene_point_t *end = gsk_linear_gradient_node_get_end (node);
int n_color_stops = gsk_linear_gradient_node_get_n_color_stops (node);
gboolean repeat = gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE;
float x1 = job->offset_x + start->x;
float x2 = job->offset_x + end->x;
float y1 = job->offset_y + start->y;
float y2 = job->offset_y + end->y;
g_assert (n_color_stops == 2);
gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, linear_gradient_2));
gsk_ngl_program_set_uniform1fv (job->current_program,
UNIFORM_LINEAR_GRADIENT_2_COLOR_STOPS, 0,
n_color_stops * 5,
(const float *)stops);
gsk_ngl_program_set_uniform4f (job->current_program,
UNIFORM_LINEAR_GRADIENT_2_POINTS, 0,
x1, y1, x2 - x1, y2 - y1);
gsk_ngl_program_set_uniform1i (job->current_program,
UNIFORM_LINEAR_GRADIENT_2_REPEAT, 0,
repeat);
gsk_ngl_render_job_draw_rect (job, &node->bounds);
gsk_ngl_render_job_end_draw (job);
}
static inline void
gsk_ngl_render_job_visit_conic_gradient_node (GskNglRenderJob *job,
const GskRenderNode *node)
@@ -1564,6 +1666,84 @@ gsk_ngl_render_job_visit_clip_node (GskNglRenderJob *job,
gsk_ngl_render_job_visit_clipped_child (job, child, clip);
}
static inline void
gsk_ngl_render_job_visit_rounded_color_node (GskNglRenderJob *job,
const GskRenderNode *node)
{
const GskRenderNode *child = gsk_rounded_clip_node_get_child (node);
const GskRoundedRect *clip = gsk_rounded_clip_node_get_clip (node);
const graphene_rect_t *outer;
graphene_rect_t inner;
graphene_rect_t rect;
outer = &node->bounds;
rounded_rect_get_inner (clip, &inner);
graphene_rect_intersection (outer, &inner, &inner);
gsk_ngl_render_job_begin_draw (job, job->driver->color);
gsk_ngl_program_set_uniform_color (job->current_program,
UNIFORM_COLOR_COLOR, 0,
gsk_color_node_get_color (child));
/* top left */
rect.origin.x = outer->origin.x;
rect.origin.y = outer->origin.y;
rect.size.width = inner.origin.x - rect.origin.x;
rect.size.height = inner.origin.y - rect.origin.y;
gsk_ngl_render_job_draw_rect (job, &rect);
/* top right */
rect.origin.x = inner.origin.x + inner.size.width;
rect.origin.y = outer->origin.y;
rect.size.width = outer->origin.x + outer->size.width - rect.origin.x;
rect.size.height = inner.origin.y - rect.origin.y;
gsk_ngl_render_job_draw_rect (job, &rect);
/* bottom right */
rect.origin.x = inner.origin.x + inner.size.width;
rect.origin.y = inner.origin.y + inner.size.height;
rect.size.width = outer->origin.x + outer->size.width - rect.origin.x;
rect.size.height = outer->origin.y + outer->size.height - rect.origin.y;
gsk_ngl_render_job_draw_rect (job, &rect);
/* bottom left */
rect.origin.x = outer->origin.x;
rect.origin.y = inner.origin.y + inner.size.height;
rect.size.width = inner.origin.x - rect.origin.x;
rect.size.height = outer->origin.y + outer->size.height - rect.origin.y;
gsk_ngl_render_job_draw_rect (job, &rect);
gsk_ngl_render_job_end_draw (job);
gsk_ngl_render_job_begin_draw (job, job->driver->color_no_clip);
gsk_ngl_program_set_uniform_color (job->current_program,
UNIFORM_COLOR_COLOR, 0,
gsk_color_node_get_color (child));
/* left side */
rect.origin.x = outer->origin.x;
rect.origin.y = inner.origin.y;
rect.size.width = inner.origin.x - rect.origin.x;
rect.size.height = inner.size.height;
gsk_ngl_render_job_draw_rect (job, &rect);
/* center */
rect.origin.x = inner.origin.x;
rect.origin.y = outer->origin.y;
rect.size.width = inner.size.width;
rect.size.height = outer->size.height;
gsk_ngl_render_job_draw_rect (job, &rect);
/* right side */
rect.origin.x = inner.origin.x + inner.size.width;
rect.origin.y = inner.origin.y;
rect.size.width = outer->origin.x + outer->size.width - rect.origin.x;
rect.size.height = inner.size.height;
gsk_ngl_render_job_draw_rect (job, &rect);
gsk_ngl_render_job_end_draw (job);
}
static inline void
gsk_ngl_render_job_visit_rounded_clip_node (GskNglRenderJob *job,
const GskRenderNode *node)
@@ -1586,6 +1766,16 @@ gsk_ngl_render_job_visit_rounded_clip_node (GskNglRenderJob *job,
transformed_clip.corner[i].height = clip->corner[i].height * scale_y;
}
if (gsk_render_node_get_node_type (child) == GSK_COLOR_NODE &&
job->current_clip->is_fully_contained)
{
/* special-case CSS backgrounds */
gsk_ngl_render_job_push_clip (job, &transformed_clip);
gsk_ngl_render_job_visit_rounded_color_node (job, node);
gsk_ngl_render_job_pop_clip (job);
return;
}
if (job->current_clip->is_rectilinear)
{
GskRoundedRect intersected_clip;
@@ -1869,6 +2059,11 @@ gsk_ngl_render_job_visit_border_node (GskNglRenderJob *job,
program = CHOOSE_PROGRAM (job, border);
update_prog_counters (job, program, max_x - min_x, widths[0]);
update_prog_counters (job, program, max_y - min_y, widths[1]);
update_prog_counters (job, program, max_x - min_x, widths[2]);
update_prog_counters (job, program, max_y - min_y, widths[3]);
gsk_ngl_program_set_uniform4fv (program,
UNIFORM_BORDER_WIDTHS, 0,
1,
@@ -2831,6 +3026,8 @@ gsk_ngl_render_job_visit_text_node (GskNglRenderJob *job,
glyph_x2 = glyph_x + glyph->ink_rect.width;
glyph_y2 = glyph_y + glyph->ink_rect.height;
update_program_counters (job, glyph->ink_rect.width, glyph->ink_rect.height);
vertices[base+0].position[0] = glyph_x;
vertices[base+0].position[1] = glyph_y;
vertices[base+0].uv[0] = tx;
@@ -3529,7 +3726,9 @@ gsk_ngl_render_job_visit_node (GskNglRenderJob *job,
case GSK_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
if (gsk_linear_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS)
if (gsk_linear_gradient_node_get_n_color_stops (node) == 2)
gsk_ngl_render_job_visit_linear_gradient_2_node (job, node);
else if (gsk_linear_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS)
gsk_ngl_render_job_visit_linear_gradient_node (job, node);
else
gsk_ngl_render_job_visit_as_fallback (job, node);
@@ -3540,10 +3739,26 @@ gsk_ngl_render_job_visit_node (GskNglRenderJob *job,
break;
case GSK_OUTSET_SHADOW_NODE:
if (gsk_outset_shadow_node_get_blur_radius (node) > 0)
gsk_ngl_render_job_visit_blurred_outset_shadow_node (job, node);
else
gsk_ngl_render_job_visit_unblurred_outset_shadow_node (job, node);
{
const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node);
GskRoundedRect transformed_outline;
gsk_ngl_render_job_transform_bounds (job, &outline->bounds, &transformed_outline.bounds);
for (guint i = 0; i < G_N_ELEMENTS (transformed_outline.corner); i++)
{
transformed_outline.corner[i].width = outline->corner[i].width * job->scale_x;
transformed_outline.corner[i].height = outline->corner[i].height * job->scale_y;
}
if (!gsk_rounded_rect_contains_rect (&transformed_outline, &job->current_clip->rect.bounds))
{
if (gsk_outset_shadow_node_get_blur_radius (node) > 0)
gsk_ngl_render_job_visit_blurred_outset_shadow_node (job, node);
else
gsk_ngl_render_job_visit_unblurred_outset_shadow_node (job, node);
}
}
break;
case GSK_RADIAL_GRADIENT_NODE:

View File

@@ -0,0 +1,84 @@
// VERTEX_SHADER
uniform vec4 u_points;
_NOPERSPECTIVE_ _OUT_ vec4 info;
void main() {
gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
vec2 mv0 = u_modelview[0].xy;
vec2 mv1 = u_modelview[1].xy;
vec2 offset = aPosition - u_points.xy;
vec2 coord = vec2(dot(mv0, offset),
dot(mv1, offset));
// Original equation:
// VS | maxDist = length(end - start);
// VS | gradient = end - start;
// VS | gradientLength = length(gradient);
// FS | pos = frag_coord - start
// FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient
// FS | offset = length(proj) / maxDist
// Simplified formula derivation:
// 1. Notice that maxDist = gradientLength:
// offset = length(proj) / gradientLength
// 2. Let gnorm = gradient / gradientLength, then:
// proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) =
// = dot(gnorm, pos) * gnorm
// 3. Since gnorm is unit length then:
// length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos)
// 4. We can avoid the FS division by passing a scaled pos from the VS:
// offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
// 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
vec2 gradient = vec2(dot(mv0, u_points.zw),
dot(mv1, u_points.zw));
float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
info = rcp_gradient_length * vec4(coord, gradient);
}
// FRAGMENT_SHADER:
uniform float u_color_stops[2 * 5];
uniform bool u_repeat;
_NOPERSPECTIVE_ _IN_ vec4 info;
float get_offset(int index) {
return u_color_stops[5 * index];
}
vec4 get_color(int index) {
int base = 5 * index + 1;
return vec4(u_color_stops[base],
u_color_stops[base + 1],
u_color_stops[base + 2],
u_color_stops[base + 3]);
}
void main() {
float offset = dot(info.xy, info.zw);
if (u_repeat) {
offset = fract(offset);
}
float offset0 = get_offset(0);
float offset1 = get_offset(1);
if (offset < offset0) {
gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
}
else if (offset < offset1) {
float f = (offset - offset0) / (offset1 - offset0);
vec4 curr_color = gsk_premultiply(get_color(0));
vec4 next_color = gsk_premultiply(get_color(1));
vec4 color = mix(curr_color, next_color, f);
gskSetOutputColor(color * u_alpha);
return;
}
else {
gskSetOutputColor(gsk_scaled_premultiply(get_color(1), u_alpha));
}
}