diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index cced23aa9b..a3d942907e 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -340,6 +340,7 @@ struct _GskGLRenderer Program border_program; Program cross_fade_program; Program blend_program; + Program repeat_program; }; }; @@ -2082,6 +2083,90 @@ render_blend_node (GskGLRenderer *self, ops_draw (builder, vertex_data); } +static inline void +render_repeat_node (GskGLRenderer *self, + GskRenderNode *node, + RenderOpBuilder *builder) +{ + const float min_x = builder->dx + node->bounds.origin.x; + const float min_y = builder->dy + node->bounds.origin.y; + const float max_x = min_x + node->bounds.size.width; + const float max_y = min_y + node->bounds.size.height; + GskRenderNode *child = gsk_repeat_node_get_child (node); + const graphene_rect_t *child_bounds = gsk_repeat_node_peek_child_bounds (node); + TextureRegion region; + gboolean is_offscreen; + RenderOp op; + + if (child_bounds != NULL && + !graphene_rect_equal (child_bounds, &child->bounds)) + { + /* TODO: Implement these repeat nodes. */ + render_fallback_node (self, node, builder); + return; + } + + /* Draw the entire child on a texture */ + add_offscreen_ops (self, builder, + &child->bounds, + child, + ®ion, &is_offscreen, + RESET_CLIP | RESET_OPACITY); + + ops_set_program (builder, &self->repeat_program); + ops_set_texture (builder, region.texture_id); + op.op = OP_CHANGE_REPEAT; + op.repeat.child_bounds[0] = 0; /* Both currently unused */ + op.repeat.child_bounds[1] = 0; + op.repeat.child_bounds[2] = node->bounds.size.width / child_bounds->size.width; + op.repeat.child_bounds[3] = node->bounds.size.height / child_bounds->size.height; + + op.repeat.texture_rect[0] = region.x; + op.repeat.texture_rect[2] = region.x2; + + if (is_offscreen) + { + op.repeat.texture_rect[1] = region.y2; + op.repeat.texture_rect[3] = region.y; + } + else + { + op.repeat.texture_rect[1] = region.y; + op.repeat.texture_rect[3] = region.y2; + } + + ops_add (builder, &op); + + if (is_offscreen) + { + const GskQuadVertex offscreen_vertex_data[GL_N_VERTICES] = { + { { min_x, min_y }, { region.x, region.y2 }, }, + { { min_x, max_y }, { region.x, region.y }, }, + { { max_x, min_y }, { region.x2, region.y2 }, }, + + { { max_x, max_y }, { region.x2, region.y }, }, + { { min_x, max_y }, { region.x, region.y }, }, + { { max_x, min_y }, { region.x2, region.y2 }, }, + }; + + ops_draw (builder, offscreen_vertex_data); + } + else + { + const GskQuadVertex onscreen_vertex_data[GL_N_VERTICES] = { + { { min_x, min_y }, { region.x, region.y }, }, + { { min_x, max_y }, { region.x, region.y2 }, }, + { { max_x, min_y }, { region.x2, region.y }, }, + + { { max_x, max_y }, { region.x2, region.y2 }, }, + { { min_x, max_y }, { region.x, region.y2 }, }, + { { max_x, min_y }, { region.x2, region.y }, }, + }; + + ops_draw (builder, onscreen_vertex_data); + } +} + static inline void apply_viewport_op (const Program *program, const RenderOp *op) @@ -2368,6 +2453,14 @@ apply_blend_op (const Program *program, glUniform1i (program->blend.mode_location, op->blend.mode); } +static inline void +apply_repeat_op (const Program *program, + const RenderOp *op) +{ + glUniform4fv (program->repeat.child_bounds_location, 1, op->repeat.child_bounds); + glUniform4fv (program->repeat.texture_rect_location, 1, op->repeat.texture_rect); +} + static void gsk_gl_renderer_dispose (GObject *gobject) { @@ -2389,19 +2482,21 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, static const struct { const char *name; const char *fs; + const char *vs; } program_definitions[] = { - { "blit", "blit.fs.glsl" }, - { "color", "color.fs.glsl" }, - { "coloring", "coloring.fs.glsl" }, - { "color matrix", "color_matrix.fs.glsl" }, - { "linear gradient", "linear_gradient.fs.glsl" }, - { "blur", "blur.fs.glsl" }, - { "inset shadow", "inset_shadow.fs.glsl" }, - { "outset shadow", "outset_shadow.fs.glsl" }, + { "blit", "blit.fs.glsl" }, + { "color", "color.fs.glsl" }, + { "coloring", "coloring.fs.glsl" }, + { "color matrix", "color_matrix.fs.glsl" }, + { "linear gradient", "linear_gradient.fs.glsl" }, + { "blur", "blur.fs.glsl" }, + { "inset shadow", "inset_shadow.fs.glsl" }, + { "outset shadow", "outset_shadow.fs.glsl" }, { "unblurred outset shadow", "unblurred_outset_shadow.fs.glsl" }, - { "border", "border.fs.glsl" }, - { "cross fade", "cross_fade.fs.glsl" }, - { "blend", "blend.fs.glsl" }, + { "border", "border.fs.glsl" }, + { "cross fade", "cross_fade.fs.glsl" }, + { "blend", "blend.fs.glsl" }, + { "repeat", "repeat.fs.glsl" }, }; builder = gsk_shader_builder_new (); @@ -2454,6 +2549,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, prog->index = i; prog->id = gsk_shader_builder_create_program (builder, program_definitions[i].fs, + program_definitions[i].vs, &shader_error); if (shader_error != NULL) @@ -2461,8 +2557,8 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, g_propagate_prefixed_error (error, shader_error, "Unable to create '%s' program (from %s and %s):\n", program_definitions[i].name, - "blit.vs.glsl", - program_definitions[i].fs); + program_definitions[i].fs, + program_definitions[i].vs); g_object_unref (builder); return FALSE; @@ -2536,6 +2632,10 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, INIT_PROGRAM_UNIFORM_LOCATION (blend, source2); INIT_PROGRAM_UNIFORM_LOCATION (blend, mode); + /* repeat */ + INIT_PROGRAM_UNIFORM_LOCATION (repeat, child_bounds); + INIT_PROGRAM_UNIFORM_LOCATION (repeat, texture_rect); + g_object_unref (builder); return TRUE; } @@ -2863,8 +2963,11 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, render_blend_node (self, node, builder); break; - case GSK_REPEATING_LINEAR_GRADIENT_NODE: case GSK_REPEAT_NODE: + render_repeat_node (self, node, builder); + break; + + case GSK_REPEATING_LINEAR_GRADIENT_NODE: case GSK_CAIRO_NODE: default: { @@ -3151,6 +3254,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self, apply_unblurred_outset_shadow_op (program, op); break; + case OP_CHANGE_REPEAT: + apply_repeat_op (program, op); + break; + case OP_DRAW: OP_PRINT (" -> draw %ld, size %ld and program %d\n", op->draw.vao_offset, op->draw.vao_size, program->index); diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h index ff8fe642be..60e9317f7c 100644 --- a/gsk/gl/gskglrenderopsprivate.h +++ b/gsk/gl/gskglrenderopsprivate.h @@ -11,7 +11,7 @@ #include "gskrendernodeprivate.h" #define GL_N_VERTICES 6 -#define GL_N_PROGRAMS 12 +#define GL_N_PROGRAMS 13 @@ -60,6 +60,7 @@ enum { OP_PUSH_DEBUG_GROUP = 24, OP_POP_DEBUG_GROUP = 25, OP_CHANGE_BLEND = 26, + OP_CHANGE_REPEAT = 27, }; typedef struct @@ -139,6 +140,10 @@ typedef struct int source2_location; int mode_location; } blend; + struct { + int child_bounds_location; + int texture_rect_location; + } repeat; }; } Program; @@ -149,7 +154,7 @@ typedef struct union { float opacity; - graphene_matrix_t modelview; /* TODO: Make both matrix members just "matrix" */ + graphene_matrix_t modelview; graphene_matrix_t projection; const Program *program; int texture_id; @@ -221,6 +226,10 @@ typedef struct int source2; int mode; } blend; + struct { + float child_bounds[4]; + float texture_rect[4]; + } repeat; struct { char *filename; int width; diff --git a/gsk/gl/gskshaderbuilder.c b/gsk/gl/gskshaderbuilder.c index dc00371889..c057fcb3dc 100644 --- a/gsk/gl/gskshaderbuilder.c +++ b/gsk/gl/gskshaderbuilder.c @@ -257,6 +257,7 @@ gsk_shader_builder_set_common_vertex_shader (GskShaderBuilder *self, int gsk_shader_builder_create_program (GskShaderBuilder *builder, const char *fragment_shader, + const char *vertex_shader, GError **error) { int vertex_id; @@ -268,7 +269,16 @@ gsk_shader_builder_create_program (GskShaderBuilder *builder, g_return_val_if_fail (fragment_shader != NULL, -1); g_return_val_if_fail (builder->common_vertex_shader_id != 0, -1); - vertex_id = builder->common_vertex_shader_id; + if (vertex_shader == NULL) + vertex_id = builder->common_vertex_shader_id; + else + vertex_id = gsk_shader_builder_compile_shader (builder, GL_VERTEX_SHADER, + builder->vertex_preamble, + vertex_shader, + error); + if (vertex_id < 0) + return -1; + fragment_id = gsk_shader_builder_compile_shader (builder, GL_FRAGMENT_SHADER, builder->fragment_preamble, fragment_shader, diff --git a/gsk/gl/gskshaderbuilderprivate.h b/gsk/gl/gskshaderbuilderprivate.h index 025d7a2028..b91af84ea6 100644 --- a/gsk/gl/gskshaderbuilderprivate.h +++ b/gsk/gl/gskshaderbuilderprivate.h @@ -31,6 +31,7 @@ void gsk_shader_builder_set_common_vertex_shader (GskShad int gsk_shader_builder_create_program (GskShaderBuilder *builder, const char *fragment_shader, + const char *vertex_shader, GError **error); G_END_DECLS diff --git a/gsk/meson.build b/gsk/meson.build index dd8a4c967b..6819ac5426 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -12,6 +12,7 @@ gsk_private_gl_shaders = [ 'resources/glsl/border.fs.glsl', 'resources/glsl/cross_fade.fs.glsl', 'resources/glsl/blend.fs.glsl', + 'resources/glsl/repeat.fs.glsl', 'resources/glsl/es2_common.fs.glsl', 'resources/glsl/es2_common.vs.glsl', 'resources/glsl/gl3_common.fs.glsl', diff --git a/gsk/resources/glsl/repeat.fs.glsl b/gsk/resources/glsl/repeat.fs.glsl new file mode 100644 index 0000000000..a58047335e --- /dev/null +++ b/gsk/resources/glsl/repeat.fs.glsl @@ -0,0 +1,34 @@ + +uniform vec4 u_child_bounds; +uniform vec4 u_texture_rect; + + +float wrap(float f, float wrap_for) { + return mod(f, wrap_for); +} + +/* We get the texture coordinates via vUv, + * but that might be on a texture atlas, so we need to do the + * wrapping ourselves. + */ +void main() { + + /* We map the texture coordinate to [1;0], then wrap it and scale the result again */ + + float tw = u_texture_rect.z - u_texture_rect.x; + float th = u_texture_rect.w - u_texture_rect.y; + + float mapped_x = (vUv.x - u_texture_rect.x) / tw; + float mapped_y = (vUv.y - u_texture_rect.y) / th; + + float wrapped_x = wrap(mapped_x * u_child_bounds.z, 1.0); + float wrapped_y = wrap(mapped_y * u_child_bounds.w, 1.0); + + vec2 tp; + tp.x = u_texture_rect.x + (wrapped_x * tw); + tp.y = u_texture_rect.y + (wrapped_y * th); + + vec4 diffuse = Texture(u_source, tp); + + setOutputColor(diffuse * u_alpha); +}