From 6bce14bee2f6c48ee68933d788612b2acdef5405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Fri, 3 Nov 2017 13:09:02 +0100 Subject: [PATCH] Rework the GL renderer --- gsk/gskgldriver.c | 42 +- gsk/gskglrenderer.c | 900 ++++++++++++--------- gsk/gskshaderbuilder.c | 2 - gsk/meson.build | 4 + gsk/resources/glsl/blend.fs.glsl | 3 +- gsk/resources/glsl/blend.vs.glsl | 1 - gsk/resources/glsl/blit.fs.glsl | 2 +- gsk/resources/glsl/color.fs.glsl | 6 +- gsk/resources/glsl/color_matrix.fs.glsl | 20 + gsk/resources/glsl/color_matrix.vs.glsl | 6 + gsk/resources/glsl/gl3_common.fs.glsl | 3 +- gsk/resources/glsl/gl3_common.vs.glsl | 1 + gsk/resources/glsl/linear_gradient.fs.glsl | 43 + gsk/resources/glsl/linear_gradient.vs.glsl | 5 + 14 files changed, 633 insertions(+), 405 deletions(-) create mode 100644 gsk/resources/glsl/color_matrix.fs.glsl create mode 100644 gsk/resources/glsl/color_matrix.vs.glsl create mode 100644 gsk/resources/glsl/linear_gradient.fs.glsl create mode 100644 gsk/resources/glsl/linear_gradient.vs.glsl diff --git a/gsk/gskgldriver.c b/gsk/gskgldriver.c index dbc3977527..68c9148aea 100644 --- a/gsk/gskgldriver.c +++ b/gsk/gskgldriver.c @@ -520,7 +520,7 @@ int gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver, int position_id, int uv_id, - int n_quads, + int n_vertices, GskQuadVertex *quads) { @@ -530,7 +530,7 @@ gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver, g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1); g_return_val_if_fail (driver->in_frame, -1); - v = find_vao (driver->vaos, position_id, uv_id, n_quads, quads); + v = find_vao (driver->vaos, position_id, uv_id, n_vertices, quads); if (v != NULL && !v->in_use) { GSK_NOTE (OPENGL, g_print ("Reusing VAO(%d)\n", v->vao_id)); @@ -543,17 +543,20 @@ gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver, glGenBuffers (1, &buffer_id); glBindBuffer (GL_ARRAY_BUFFER, buffer_id); - glBufferData (GL_ARRAY_BUFFER, sizeof (GskQuadVertex) * n_quads, quads, GL_STATIC_DRAW); + glBufferData (GL_ARRAY_BUFFER, sizeof (GskQuadVertex) * n_vertices, quads, GL_STATIC_DRAW); glEnableVertexAttribArray (position_id); glVertexAttribPointer (position_id, 2, GL_FLOAT, GL_FALSE, sizeof (GskQuadVertex), (void *) G_STRUCT_OFFSET (GskQuadVertex, position)); - glEnableVertexAttribArray (uv_id); - glVertexAttribPointer (uv_id, 2, GL_FLOAT, GL_FALSE, - sizeof (GskQuadVertex), - (void *) G_STRUCT_OFFSET (GskQuadVertex, uv)); + if (uv_id != -1) + { + glEnableVertexAttribArray (uv_id); + glVertexAttribPointer (uv_id, 2, GL_FLOAT, GL_FALSE, + sizeof (GskQuadVertex), + (void *) G_STRUCT_OFFSET (GskQuadVertex, uv)); + } glBindBuffer (GL_ARRAY_BUFFER, 0); glBindVertexArray (0); @@ -563,8 +566,8 @@ gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver, v->buffer_id = buffer_id; v->position_id = position_id; v->uv_id = uv_id; - v->n_quads = n_quads; - v->quads = g_memdup (quads, sizeof (GskQuadVertex) * n_quads); + v->n_quads = n_vertices; + v->quads = g_memdup (quads, sizeof (GskQuadVertex) * n_vertices); v->in_use = TRUE; g_hash_table_insert (driver->vaos, GINT_TO_POINTER (vao_id), v); @@ -572,8 +575,8 @@ gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver, if (GSK_DEBUG_CHECK (OPENGL)) { int i; - g_print ("New VAO(%d) for quad[%d] : {\n", v->vao_id, n_quads); - for (i = 0; i < n_quads; i++) + g_print ("New VAO(%d) for quad[%d] : {\n", v->vao_id, n_vertices); + for (i = 0; i < n_vertices; i++) { g_print (" { x:%.2f, y:%.2f } { u:%.2f, v:%.2f }\n", quads[i].position[0], quads[i].position[1], @@ -641,6 +644,8 @@ gsk_gl_driver_create_render_target (GskGLDriver *driver, g_array_append_val (t->fbos, f); + g_assert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + glBindFramebuffer (GL_FRAMEBUFFER, driver->default_fbo.fbo_id); return fbo_id; @@ -719,7 +724,9 @@ gsk_gl_driver_bind_vao (GskGLDriver *driver, glBindVertexArray (v->vao_id); glBindBuffer (GL_ARRAY_BUFFER, v->buffer_id); glEnableVertexAttribArray (v->position_id); - glEnableVertexAttribArray (v->uv_id); + + if (v->uv_id != -1) + glEnableVertexAttribArray (v->uv_id); driver->bound_vao = v; } @@ -757,9 +764,14 @@ gsk_gl_driver_bind_render_target (GskGLDriver *driver, } out: - status = glCheckFramebufferStatus (GL_FRAMEBUFFER); - return status == GL_FRAMEBUFFER_COMPLETE; + if (texture_id != 0) + { + status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + g_assert_cmpint (status, ==, GL_FRAMEBUFFER_COMPLETE); + } + + return TRUE; } void @@ -856,6 +868,4 @@ gsk_gl_driver_init_texture_with_surface (GskGLDriver *driver, if (t->min_filter != GL_NEAREST) glGenerateMipmap (GL_TEXTURE_2D); - - glBindTexture (GL_TEXTURE_2D, 0); } diff --git a/gsk/gskglrenderer.c b/gsk/gskglrenderer.c index e1c31548fc..88ee17bf24 100644 --- a/gsk/gskglrenderer.c +++ b/gsk/gskglrenderer.c @@ -21,6 +21,29 @@ #define SHADER_VERSION_GL3_LEGACY 130 #define SHADER_VERSION_GL3 150 +#define ORTHO_NEAR_PLANE -10000 +#define ORTHO_FAR_PLANE 10000 + +#define HIGHLIGHT_FALLBACK 0 + +static void G_GNUC_UNUSED +dump_framebuffer (const char *filename, int w, int h) +{ + int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w); + guchar *data = g_malloc (h * stride); + cairo_surface_t *s; + + glReadPixels (0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, data); + s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, w, h, stride); + cairo_surface_write_to_png (s, filename); + + cairo_surface_destroy (s); + g_free (data); +} + + +static void +gsk_gl_renderer_setup_render_mode (GskGLRenderer *self); typedef struct { int id; /* Common locations (gl_common)*/ @@ -31,28 +54,40 @@ typedef struct { int position_location; int alpha_location; int blendMode_location; + int viewport_location; + int projection_location; /* Shader-specific locations */ union { struct { int color_location; }; + struct { + int color_matrix_location; + int color_offset_location; + }; + struct { + int n_color_stops_location; + int color_stops_location; + int color_offsets_location; + int start_point_location; + int end_point_location; + }; }; } Program; -typedef struct { - int render_target_id; - int vao_id; - int buffer_id; - int texture_id; - int program_id; - - Program *program; -} RenderData; +#define INIT_PROGRAM_UNIFORM_LOCATION(program_name, location_name, uniform_name) \ + G_STMT_START{\ + self->program_name.location_name = glGetUniformLocation(self->program_name.id, uniform_name);\ + g_assert (self->program_name.location_name != 0); \ + }G_STMT_END enum { - MODE_COLOR = 1, + MODE_BLIT = 1, + MODE_COLOR, MODE_TEXTURE, + MODE_COLOR_MATRIX, + MODE_LINEAR_GRADIENT, N_MODES }; @@ -65,6 +100,7 @@ typedef struct { graphene_size_t size; graphene_matrix_t mvp; + graphene_matrix_t projection; float opacity; float z; @@ -73,18 +109,40 @@ typedef struct { struct { GdkRGBA color; } color_data; + struct { + graphene_matrix_t color_matrix; + graphene_vec4_t color_offset; + } color_matrix_data; + struct { + int n_color_stops; + float color_offsets[8]; + float color_stops[4 * 8]; + graphene_point_t start_point; + graphene_point_t end_point; + } linear_gradient_data; }; const char *name; GskBlendMode blend_mode; - RenderData render_data; - RenderData *parent_data; + /* The render target this item will draw itself on */ + int parent_render_target; + /* In case this item creates a new render target, this is its id */ + int render_target; + int vao_id; + int texture_id; + const Program *program; GArray *children; } RenderItem; +static void +destroy_render_item (RenderItem *item) +{ + if (item->children) + g_array_unref (item->children); +} enum { @@ -93,6 +151,8 @@ enum { MASK, ALPHA, BLEND_MODE, + VIEWPORT, + PROJECTION, N_UNIFORMS }; @@ -123,8 +183,10 @@ struct _GskGLRenderer { GskRenderer parent_instance; + int scale_factor; + graphene_matrix_t mvp; - graphene_frustum_t frustum; + graphene_rect_t viewport; guint frame_buffer; guint depth_stencil_buffer; @@ -136,11 +198,12 @@ struct _GskGLRenderer GdkGLContext *gl_context; GskGLDriver *gl_driver; GskGLProfiler *gl_profiler; - GskShaderBuilder *shader_builder; Program blend_program; Program blit_program; Program color_program; + Program color_matrix_program; + Program linear_gradient_program; GArray *render_items; @@ -192,6 +255,7 @@ gsk_gl_renderer_create_buffers (GskGLRenderer *self, } gsk_gl_driver_create_render_target (self->gl_driver, self->texture_id, TRUE, TRUE); + gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id); self->has_buffers = TRUE; } @@ -219,24 +283,29 @@ gsk_gl_renderer_destroy_buffers (GskGLRenderer *self) } static void -init_common_locations (GskGLRenderer *self, - Program *prog) +init_common_locations (GskGLRenderer *self, + GskShaderBuilder *builder, + Program *prog) { prog->source_location = - gsk_shader_builder_get_uniform_location (self->shader_builder, prog->id, self->uniforms[SOURCE]); + gsk_shader_builder_get_uniform_location (builder, prog->id, self->uniforms[SOURCE]); prog->mask_location = - gsk_shader_builder_get_uniform_location (self->shader_builder, prog->id, self->uniforms[MASK]); + gsk_shader_builder_get_uniform_location (builder, prog->id, self->uniforms[MASK]); prog->mvp_location = - gsk_shader_builder_get_uniform_location (self->shader_builder, prog->id, self->uniforms[MVP]); + gsk_shader_builder_get_uniform_location (builder, prog->id, self->uniforms[MVP]); prog->alpha_location = - gsk_shader_builder_get_uniform_location (self->shader_builder, prog->id, self->uniforms[ALPHA]); + gsk_shader_builder_get_uniform_location (builder, prog->id, self->uniforms[ALPHA]); prog->blendMode_location = - gsk_shader_builder_get_uniform_location (self->shader_builder, prog->id, self->uniforms[BLEND_MODE]); + gsk_shader_builder_get_uniform_location (builder, prog->id, self->uniforms[BLEND_MODE]); + prog->viewport_location = gsk_shader_builder_get_uniform_location (builder, prog->id, + self->uniforms[VIEWPORT]); + prog->projection_location = gsk_shader_builder_get_uniform_location (builder, prog->id, + self->uniforms[PROJECTION]); prog->position_location = - gsk_shader_builder_get_attribute_location (self->shader_builder, prog->id, self->attributes[POSITION]); + gsk_shader_builder_get_attribute_location (builder, prog->id, self->attributes[POSITION]); prog->uv_location = - gsk_shader_builder_get_attribute_location (self->shader_builder, prog->id, self->attributes[UV]); + gsk_shader_builder_get_attribute_location (builder, prog->id, self->attributes[UV]); } static gboolean @@ -256,7 +325,9 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, self->uniforms[MASK] = gsk_shader_builder_add_uniform (builder, "uMask"); self->uniforms[ALPHA] = gsk_shader_builder_add_uniform (builder, "uAlpha"); self->uniforms[BLEND_MODE] = gsk_shader_builder_add_uniform (builder, "uBlendMode"); - + self->uniforms[VIEWPORT] = gsk_shader_builder_add_uniform (builder, "uViewport"); + self->uniforms[PROJECTION] = gsk_shader_builder_add_uniform (builder, "uProjection"); + self->attributes[POSITION] = gsk_shader_builder_add_attribute (builder, "aPosition"); self->attributes[UV] = gsk_shader_builder_add_attribute (builder, "aUv"); @@ -293,10 +364,6 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, if (GSK_RENDER_MODE_CHECK (SHADERS)) gsk_shader_builder_add_define (builder, "GSK_DEBUG", "1"); #endif - /* Keep a pointer to query for the uniform and attribute locations - * when rendering the scene - */ - self->shader_builder = builder; self->blend_program.id = gsk_shader_builder_create_program (builder, "blend.vs.glsl", "blend.fs.glsl", &shader_error); @@ -305,10 +372,9 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, g_propagate_prefixed_error (error, shader_error, "Unable to create 'blend' program: "); - g_object_unref (builder); goto out; } - init_common_locations (self, &self->blend_program); + init_common_locations (self, builder, &self->blend_program); self->blit_program.id = gsk_shader_builder_create_program (builder, "blit.vs.glsl", "blit.fs.glsl", &shader_error); @@ -317,10 +383,9 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, g_propagate_prefixed_error (error, shader_error, "Unable to create 'blit' program: "); - g_object_unref (builder); goto out; } - init_common_locations (self, &self->blit_program); + init_common_locations (self, builder, &self->blit_program); self->color_program.id = gsk_shader_builder_create_program (builder, "color.vs.glsl", "color.fs.glsl", &shader_error); @@ -329,26 +394,50 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, g_propagate_prefixed_error (error, shader_error, "Unable to create 'color' program: "); - g_object_unref (builder); goto out; } - init_common_locations (self, &self->color_program); - self->color_program.color_location = gsk_shader_builder_get_uniform_location (self->shader_builder, - self->color_program.id, - g_quark_from_string("uColor")); - self->color_program.color_location = glGetUniformLocation(self->color_program.id, "uColor"); - g_assert(self->color_program.color_location >= 0); + init_common_locations (self, builder, &self->color_program); + INIT_PROGRAM_UNIFORM_LOCATION (color_program, color_location, "uColor"); + + self->color_matrix_program.id = gsk_shader_builder_create_program (builder, + "color_matrix.vs.glsl", + "color_matrix.fs.glsl", + &shader_error); + if (shader_error != NULL) + { + g_propagate_prefixed_error (error, + shader_error, + "Unable to create 'color_matrix' program: "); + goto out; + } + init_common_locations (self, builder, &self->color_matrix_program); + INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_matrix_location, "uColorMatrix"); + INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_offset_location, "uColorOffset"); + + self->linear_gradient_program.id = gsk_shader_builder_create_program (builder, + "linear_gradient.vs.glsl", + "linear_gradient.fs.glsl", + &shader_error); + if (shader_error != NULL) + { + g_propagate_prefixed_error (error, + shader_error, + "Unable to create 'linear_gradient' program: "); + goto out; + } + init_common_locations (self, builder, &self->linear_gradient_program); + INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_stops_location, "uColorStops"); + INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_offsets_location, "uColorOffsets"); + INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, n_color_stops_location, "uNumColorStops"); + INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, start_point_location, "uStartPoint"); + INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, end_point_location, "uEndPoint"); res = TRUE; out: - return res; -} -static void -gsk_gl_renderer_destroy_programs (GskGLRenderer *self) -{ - g_clear_object (&self->shader_builder); + g_object_unref (builder); + return res; } static gboolean @@ -358,6 +447,8 @@ gsk_gl_renderer_realize (GskRenderer *renderer, { GskGLRenderer *self = GSK_GL_RENDERER (renderer); + self->scale_factor = gdk_window_get_scale_factor (window); + /* If we didn't get a GdkGLContext before realization, try creating * one now, for our exclusive use. */ @@ -374,8 +465,8 @@ gsk_gl_renderer_realize (GskRenderer *renderer, gdk_gl_context_make_current (self->gl_context); g_assert (self->gl_driver == NULL); - self->gl_driver = gsk_gl_driver_new (self->gl_context); self->gl_profiler = gsk_gl_profiler_new (self->gl_context); + self->gl_driver = gsk_gl_driver_new (self->gl_context); GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n")); if (!gsk_gl_renderer_create_programs (self, error)) @@ -399,8 +490,14 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer) */ g_array_set_size (self->render_items, 0); + + glDeleteProgram (self->blend_program.id); + glDeleteProgram (self->blit_program.id); + glDeleteProgram (self->color_program.id); + glDeleteProgram (self->color_matrix_program.id); + glDeleteProgram (self->linear_gradient_program.id); + gsk_gl_renderer_destroy_buffers (self); - gsk_gl_renderer_destroy_programs (self); g_clear_object (&self->gl_profiler); g_clear_object (&self->gl_driver); @@ -424,12 +521,12 @@ gsk_gl_renderer_begin_draw_frame (GskRenderer *renderer, window = gsk_renderer_get_window (renderer); whole_window = (GdkRectangle) { 0, 0, - gdk_window_get_width (window), - gdk_window_get_height (window) + gdk_window_get_width (window) * self->scale_factor, + gdk_window_get_height (window) * self->scale_factor }; damage = gdk_gl_context_get_damage (self->gl_context); cairo_region_union (damage, update_area); - + if (cairo_region_contains_rectangle (damage, &whole_window) == CAIRO_REGION_OVERLAP_IN) { self->render_mode = RENDER_FULL; @@ -458,66 +555,80 @@ gsk_gl_renderer_begin_draw_frame (GskRenderer *renderer, static void gsk_gl_renderer_resize_viewport (GskGLRenderer *self, - const graphene_rect_t *viewport, - int scale_factor) + const graphene_rect_t *viewport) { - int width = viewport->size.width * scale_factor; - int height = viewport->size.height * scale_factor; + int width = viewport->size.width; + int height = viewport->size.height; GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %d, %d) [scale:%d]\n", width, height, - scale_factor)); + self->scale_factor)); + graphene_rect_init (&self->viewport, 0, 0, width, height); glViewport (0, 0, width, height); } -static void -gsk_gl_renderer_update_frustum (GskGLRenderer *self, - const graphene_matrix_t *modelview, - const graphene_matrix_t *projection) -{ - GSK_NOTE (TRANSFORMS, g_print ("Updating the modelview/projection\n")); - - graphene_matrix_multiply (modelview, projection, &self->mvp); - - graphene_frustum_init_from_matrix (&self->frustum, &self->mvp); - - GSK_NOTE (TRANSFORMS, - g_print ("Renderer MVP:\n"); - graphene_matrix_print (&self->mvp); - g_print ("\n")); -} - #define N_VERTICES 6 static void -render_item (GskGLRenderer *self, - RenderItem *item) +render_item (GskGLRenderer *self, + const RenderItem *item) { - float mvp[16]; - float opacity; + float mat[16]; + const gboolean draw_children = item->children != NULL && + item->children->len > 0; + const gboolean drawing_offscreen = item->parent_render_target != 0; - if (item->children != NULL) + /*g_message ("Rendering %s with %u children and parent render target %d", item->name, item->children ? item->children->len : 0, item->parent_render_target);*/ + + if (draw_children) { - if (gsk_gl_driver_bind_render_target (self->gl_driver, item->render_data.render_target_id)) - { - glViewport (0, 0, item->size.width, item->size.height); + guint i; + guint p; + graphene_rect_t prev_viewport; - glClearColor (0.0, 0.0, 0.0, 0.0); - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + prev_viewport = self->viewport; + + gsk_gl_driver_bind_render_target (self->gl_driver, item->render_target); + glDisable (GL_SCISSOR_TEST); + glClearColor (0.0, 0.0, 0.0, 0.0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + graphene_rect_init (&self->viewport, + item->min.x, item->min.y, item->size.width * self->scale_factor, item->size.height * self->scale_factor); + glViewport (0, 0, item->size.width * self->scale_factor, item->size.height * self->scale_factor); + + p = item->children->len; + for (i = 0; i < p; i ++) + { + const RenderItem *child = &g_array_index (item->children, RenderItem, i); + + g_assert (child->parent_render_target == item->render_target); + render_item (self, child); } + + /* At this point, all the child items should've been drawn */ + gsk_gl_driver_bind_render_target (self->gl_driver, 0); + /* TODO: Manage pre-/post-framebuffer-state in the driver? */ + /* Resets the scissor test, etc. */ + if (!drawing_offscreen) + gsk_gl_renderer_setup_render_mode (self); + + graphene_rect_init_from_rect (&self->viewport, &prev_viewport); + glViewport (0, 0, self->viewport.size.width, self->viewport.size.height); } - gsk_gl_driver_bind_vao (self->gl_driver, item->render_data.vao_id); - glUseProgram (item->render_data.program->id); + if (drawing_offscreen) + g_assert (gsk_gl_driver_bind_render_target (self->gl_driver, item->parent_render_target)); + + glUseProgram (item->program->id); switch(item->mode) { case MODE_COLOR: { - glUniform4f (item->render_data.program->color_location, + glUniform4f (item->program->color_location, item->color_data.color.red, item->color_data.color.green, item->color_data.color.blue, @@ -527,22 +638,48 @@ render_item (GskGLRenderer *self, case MODE_TEXTURE: { - g_assert(item->render_data.texture_id != 0); + g_assert(item->texture_id != 0); /* Use texture unit 0 for the source */ - glUniform1i (item->render_data.program->source_location, 0); - gsk_gl_driver_bind_source_texture (self->gl_driver, item->render_data.texture_id); + glUniform1i (item->program->source_location, 0); + gsk_gl_driver_bind_source_texture (self->gl_driver, item->texture_id); + } + break; - if (item->parent_data != NULL) - { - glUniform1i (item->render_data.program->blendMode_location, item->blend_mode); + case MODE_BLIT: + g_assert (item->program == &self->blit_program); + glUniform1i (item->program->source_location, 0); + gsk_gl_driver_bind_source_texture (self->gl_driver, item->render_target); + break; - /* Use texture unit 1 for the mask */ - if (item->parent_data->texture_id != 0) - { - glUniform1i (item->render_data.program->mask_location, 1); - gsk_gl_driver_bind_mask_texture (self->gl_driver, item->parent_data->texture_id); - } - } + case MODE_COLOR_MATRIX: + { + float vec[4]; + + glUniform1i (item->program->source_location, 0); + gsk_gl_driver_bind_source_texture (self->gl_driver, item->render_target); + + graphene_matrix_to_float (&item->color_matrix_data.color_matrix, mat); + glUniformMatrix4fv (item->program->color_matrix_location, 1, GL_FALSE, mat); + + graphene_vec4_to_float (&item->color_matrix_data.color_offset, vec); + glUniform4fv (item->program->color_offset_location, 1, vec); + } + break; + + case MODE_LINEAR_GRADIENT: + { + glUniform1i (item->program->n_color_stops_location, + item->linear_gradient_data.n_color_stops); + glUniform4fv (item->program->color_stops_location, + item->linear_gradient_data.n_color_stops, + item->linear_gradient_data.color_stops); + glUniform1fv (item->program->color_offsets_location, + item->linear_gradient_data.n_color_stops, + item->linear_gradient_data.color_offsets); + glUniform2f (item->program->start_point_location, + item->linear_gradient_data.start_point.x, item->linear_gradient_data.start_point.y); + glUniform2f (item->program->end_point_location, + item->linear_gradient_data.end_point.x, item->linear_gradient_data.end_point.y); } break; @@ -550,85 +687,18 @@ render_item (GskGLRenderer *self, g_assert_not_reached (); } - /* Pass the opacity component */ - if (item->children != NULL) - opacity = 1.0; - else - opacity = item->opacity; - - glUniform1f (item->render_data.program->alpha_location, opacity); - - /* Pass the mvp to the vertex shader */ - GSK_NOTE (TRANSFORMS, graphene_matrix_print (&item->mvp)); - graphene_matrix_to_float (&item->mvp, mvp); - glUniformMatrix4fv (item->render_data.program->mvp_location, 1, GL_FALSE, mvp); - - /* Draw the quad */ - GSK_NOTE2 (OPENGL, TRANSFORMS, - g_print ("Drawing item <%s>[%p] (w:%g, h:%g) with opacity: %g blend mode: %d\n", - item->name, - item, - item->size.width, item->size.height, - item->opacity, - item->blend_mode)); + /* Common uniforms */ + graphene_matrix_to_float (&item->mvp, mat); + glUniformMatrix4fv (item->program->mvp_location, 1, GL_FALSE, mat); + graphene_matrix_to_float (&item->projection, mat); + glUniformMatrix4fv (item->program->projection_location, 1, GL_FALSE, mat); + glUniform1f (item->program->alpha_location, item->opacity); + glUniform4f (item->program->viewport_location, + self->viewport.origin.x, self->viewport.origin.y, + self->viewport.size.width, self->viewport.size.height); + gsk_gl_driver_bind_vao (self->gl_driver, item->vao_id); glDrawArrays (GL_TRIANGLES, 0, N_VERTICES); - -#ifdef G_ENABLE_DEBUG - gsk_profiler_counter_inc (gsk_renderer_get_profiler (GSK_RENDERER (self)), - self->profile_counters.draw_calls); -#endif - - /* Render all children items, so we can take the result - * render target texture during the compositing - */ - if (item->children != NULL) - { - int i; - - for (i = 0; i < item->children->len; i++) - { - RenderItem *child = &g_array_index (item->children, RenderItem, i); - - render_item (self, child); - } - - /* Bind the parent render target */ - if (item->parent_data != NULL) - gsk_gl_driver_bind_render_target (self->gl_driver, item->parent_data->render_target_id); - - /* Bind the same VAO, as the render target is created with the same size - * and vertices as the texture target - */ - gsk_gl_driver_bind_vao (self->gl_driver, item->render_data.vao_id); - - /* Since we're rendering the target texture, we only need the blit program */ - glUseProgram (self->blit_program.id); - - /* Use texture unit 0 for the render target */ - glUniform1i (item->render_data.program->source_location, 0); - gsk_gl_driver_bind_source_texture (self->gl_driver, item->render_data.render_target_id); - - /* Pass the opacity component; if we got here, we know that the original render - * target is neither fully opaque nor at full opacity - */ - glUniform1f (item->render_data.program->alpha_location, item->opacity); - - /* Pass the mvp to the vertex shader */ - GSK_NOTE (TRANSFORMS, graphene_matrix_print (&item->mvp)); - graphene_matrix_to_float (&item->mvp, mvp); - glUniformMatrix4fv (item->render_data.program->mvp_location, 1, GL_FALSE, mvp); - - /* Draw the quad */ - GSK_NOTE2 (OPENGL, TRANSFORMS, - g_print ("Drawing offscreen item <%s>[%p] (w:%g, h:%g) with opacity: %g\n", - item->name, - item, - item->size.width, item->size.height, - item->opacity)); - - glDrawArrays (GL_TRIANGLES, 0, N_VERTICES); - } } static void @@ -652,10 +722,31 @@ project_item (const graphene_matrix_t *projection, return graphene_vec4_get_z (&vec) / graphene_vec4_get_w (&vec); } -static gboolean -render_node_needs_render_target (GskRenderNode *node) +static void +init_framebuffer_for_node (GskGLRenderer *self, + RenderItem *item, + GskRenderNode *node, + const graphene_matrix_t *projection, + graphene_matrix_t *out_projection) { - return FALSE; + item->render_target = gsk_gl_driver_create_texture (self->gl_driver, + item->size.width * self->scale_factor, + item->size.height * self->scale_factor); + gsk_gl_driver_bind_source_texture (self->gl_driver, item->render_target); + gsk_gl_driver_init_texture_empty (self->gl_driver, item->render_target); + gsk_gl_driver_create_render_target (self->gl_driver, item->render_target, TRUE, TRUE); + + item->children = g_array_new (FALSE, FALSE, sizeof (RenderItem)); + g_array_set_clear_func (item->children, (GDestroyNotify)destroy_render_item); + + g_assert (projection != NULL); + graphene_matrix_init_ortho (out_projection, + item->min.x, + item->min.x + item->size.width * self->scale_factor, + item->min.y, + item->min.y + item->size.height * self->scale_factor, + ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); + graphene_matrix_scale (out_projection, 1, -1, 1); } static void @@ -664,75 +755,121 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, const graphene_matrix_t *modelview, GArray *render_items, GskRenderNode *node, - RenderItem *parent) + int render_target) { RenderItem item; - RenderItem *ritem = NULL; - int program_id; - int scale_factor; + + /* We handle container nodes here directly */ + if (gsk_render_node_get_node_type (node) == GSK_CONTAINER_NODE) + { + guint i, p; + + for (i = 0, p = gsk_container_node_get_n_children (node); i < p; i++) + { + GskRenderNode *child = gsk_container_node_get_child (node, i); + gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, render_target); + } + return; + } memset (&item, 0, sizeof (RenderItem)); item.name = node->name != NULL ? node->name : "unnamed"; - /* The texture size */ - item.size.width = node->bounds.size.width * scale_factor; - item.size.height = node->bounds.size.height * scale_factor; + /*g_message ("%s: %s", __FUNCTION__, item.name);*/ + item.size.width = node->bounds.size.width; + item.size.height = node->bounds.size.height; /* Each render item is an axis-aligned bounding box that we * transform using the given transformation matrix */ item.min.x = node->bounds.origin.x; item.min.y = node->bounds.origin.y; - item.min.z = 0.f; + + /*g_message ("%s: %f, %f", item.name, item.min.x, item.min.y);*/ item.max.x = item.min.x + node->bounds.size.width; item.max.y = item.min.y + node->bounds.size.height; - item.max.z = 0.f; /* The location of the item, in normalized world coordinates */ - graphene_matrix_multiply (modelview, &self->mvp, &item.mvp); + graphene_matrix_multiply (modelview, projection, &item.mvp); item.z = project_item (projection, modelview); item.opacity = 1.0; + item.projection = *projection; item.blend_mode = GSK_BLEND_MODE_DEFAULT; - /* Back-pointer to the parent node */ - if (parent != NULL) - item.parent_data = &(parent->render_data); - else - item.parent_data = NULL; + item.parent_render_target = render_target; - /* Select the program to use */ - if (parent != NULL) - { - item.render_data.program = &self->blend_program; - program_id = self->blend_program.id; - } - else - { - item.render_data.program = &self->blit_program; - program_id = self->blit_program.id; - } - - if (render_node_needs_render_target (node)) - { - item.render_data.render_target_id = - gsk_gl_driver_create_texture (self->gl_driver, item.size.width, item.size.height); - gsk_gl_driver_init_texture_empty (self->gl_driver, item.render_data.render_target_id); - gsk_gl_driver_create_render_target (self->gl_driver, item.render_data.render_target_id, TRUE, TRUE); - - item.children = g_array_new (FALSE, FALSE, sizeof (RenderItem)); - } - else - { - item.render_data.render_target_id = self->texture_id; - item.children = NULL; - } + item.mode = MODE_BLIT; + item.program = &self->blit_program; + item.render_target = 0; + item.children = NULL; switch (gsk_render_node_get_node_type (node)) { + case GSK_OPACITY_NODE: + { + GskRenderNode *child = gsk_opacity_node_get_child (node); + graphene_matrix_t p; + graphene_matrix_t identity; + + graphene_matrix_init_identity (&identity); + init_framebuffer_for_node (self, &item, node, projection, &p); + gsk_gl_renderer_add_render_item (self, &p, &identity, item.children, child, + item.render_target); + item.mode = MODE_BLIT; + item.opacity = gsk_opacity_node_get_opacity (node); + } + break; + + case GSK_CLIP_NODE: + { + GskRenderNode *child = gsk_clip_node_get_child (node); + graphene_matrix_t p; + graphene_matrix_t identity; + + graphene_matrix_init_identity (&identity); + init_framebuffer_for_node (self, &item, node, projection, &p); + gsk_gl_renderer_add_render_item (self, &p, &identity, item.children, child, + item.render_target); + item.mode = MODE_BLIT; + } + break; + + case GSK_ROUNDED_CLIP_NODE: + { + GskRenderNode *child = gsk_rounded_clip_node_get_child (node); + graphene_matrix_t p; + graphene_matrix_t identity; + + graphene_matrix_init_identity (&identity); + init_framebuffer_for_node (self, &item, node, projection, &p); + gsk_gl_renderer_add_render_item (self, &p, &identity, item.children, child, + item.render_target); + item.mode = MODE_BLIT; + } + break; + + case GSK_COLOR_MATRIX_NODE: + { + GskRenderNode *child = gsk_color_matrix_node_get_child (node); + graphene_matrix_t p; + graphene_matrix_t identity; + + graphene_matrix_init_identity (&identity); + init_framebuffer_for_node (self, &item, node, projection, &p); + gsk_gl_renderer_add_render_item (self, &p, &identity, item.children, child, + item.render_target); + + item.mode = MODE_COLOR_MATRIX; + item.program = &self->color_matrix_program; + item.color_matrix_data.color_matrix = *gsk_color_matrix_node_peek_color_matrix (node); + item.color_matrix_data.color_offset = *gsk_color_matrix_node_peek_color_offset (node); + } + break; + case GSK_TEXTURE_NODE: { GdkTexture *texture = gsk_texture_node_get_texture (node); @@ -740,10 +877,10 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, get_gl_scaling_filters (node, &gl_min_filter, &gl_mag_filter); - item.render_data.texture_id = gsk_gl_driver_get_texture_for_texture (self->gl_driver, - texture, - gl_min_filter, - gl_mag_filter); + item.texture_id = gsk_gl_driver_get_texture_for_texture (self->gl_driver, + texture, + gl_min_filter, + gl_mag_filter); item.mode = MODE_TEXTURE; } break; @@ -758,13 +895,12 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, get_gl_scaling_filters (node, &gl_min_filter, &gl_mag_filter); - /* Upload the Cairo surface to a GL texture */ - item.render_data.texture_id = gsk_gl_driver_create_texture (self->gl_driver, - item.size.width, - item.size.height); - gsk_gl_driver_bind_source_texture (self->gl_driver, item.render_data.texture_id); + item.texture_id = gsk_gl_driver_create_texture (self->gl_driver, + item.size.width, + item.size.height); + gsk_gl_driver_bind_source_texture (self->gl_driver, item.texture_id); gsk_gl_driver_init_texture_with_surface (self->gl_driver, - item.render_data.texture_id, + item.texture_id, (cairo_surface_t *)surface, gl_min_filter, gl_mag_filter); @@ -774,71 +910,39 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, case GSK_COLOR_NODE: { - const GdkRGBA *c = gsk_color_node_peek_color(node); - program_id = self->color_program.id; - item.render_data.program = &self->color_program; item.mode = MODE_COLOR; - item.color_data.color= *c; + item.program = &self->color_program; + item.color_data.color= *gsk_color_node_peek_color (node); } break; - case GSK_COLOR_MATRIX_NODE: + case GSK_LINEAR_GRADIENT_NODE: { - GskRenderNode *child = gsk_color_matrix_node_get_child (node); + int n_color_stops = MIN (8, gsk_linear_gradient_node_get_n_color_stops (node)); + const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node); + const graphene_point_t *start = gsk_linear_gradient_node_peek_start (node); + const graphene_point_t *end = gsk_linear_gradient_node_peek_end (node); + int i; - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); - } - return; + item.mode = MODE_LINEAR_GRADIENT; + item.program = &self->linear_gradient_program; - case GSK_SHADOW_NODE: - { - GskRenderNode *child = gsk_shadow_node_get_child (node); - - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); - } - return; - - case GSK_REPEAT_NODE: - { - GskRenderNode *child = gsk_repeat_node_get_child (node); - - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); - } - return; - - case GSK_BLEND_NODE: - { - GskRenderNode *child = gsk_blend_node_get_bottom_child (node); - - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); - - child = gsk_blend_node_get_top_child (node); - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); - } - return; - - case GSK_CROSS_FADE_NODE: - { - GskRenderNode *child = gsk_cross_fade_node_get_start_child (node); - - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); - - child = gsk_cross_fade_node_get_end_child (node); - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); - } - return; - - case GSK_CONTAINER_NODE: - { - guint i, p; - - for (i = 0, p = gsk_container_node_get_n_children (node); i < p; i++) + for (i = 0; i < n_color_stops; i ++) { - GskRenderNode *child = gsk_container_node_get_child (node, i); - gsk_gl_renderer_add_render_item (self, projection, modelview, render_items, child, ritem); + const GskColorStop *stop = stops + i; + + item.linear_gradient_data.color_stops[(i * 4) + 0] = stop->color.red; + item.linear_gradient_data.color_stops[(i * 4) + 1] = stop->color.green; + item.linear_gradient_data.color_stops[(i * 4) + 2] = stop->color.blue; + item.linear_gradient_data.color_stops[(i * 4) + 3] = stop->color.alpha; + item.linear_gradient_data.color_offsets[i] = stop->offset; } + + item.linear_gradient_data.n_color_stops = n_color_stops; + item.linear_gradient_data.start_point = *start; + item.linear_gradient_data.end_point = *end; } - return; + break; case GSK_TRANSFORM_NODE: { @@ -850,47 +954,57 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, projection, &transformed_mv, render_items, gsk_transform_node_get_child (node), - ritem); + render_target); } return; case GSK_NOT_A_RENDER_NODE: + case GSK_CONTAINER_NODE: g_assert_not_reached (); return; - case GSK_LINEAR_GRADIENT_NODE: case GSK_REPEATING_LINEAR_GRADIENT_NODE: case GSK_BORDER_NODE: case GSK_INSET_SHADOW_NODE: case GSK_OUTSET_SHADOW_NODE: - case GSK_OPACITY_NODE: - case GSK_CLIP_NODE: - case GSK_ROUNDED_CLIP_NODE: case GSK_TEXT_NODE: case GSK_BLUR_NODE: + case GSK_SHADOW_NODE: + case GSK_CROSS_FADE_NODE: + case GSK_BLEND_NODE: + case GSK_REPEAT_NODE: default: { cairo_surface_t *surface; cairo_t *cr; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - item.size.width, - item.size.height); - cairo_surface_set_device_scale (surface, scale_factor, scale_factor); + item.size.width * self->scale_factor, + item.size.height * self->scale_factor); + cairo_surface_set_device_scale (surface, self->scale_factor, self->scale_factor); cr = cairo_create (surface); - cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y); + cairo_save (cr); + cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y); gsk_render_node_draw (node, cr); + cairo_restore (cr); + +#if HIGHLIGHT_FALLBACK + cairo_move_to (cr, 0, 0); + cairo_rectangle (cr, 0, 0, item.size.width, item.size.height); + cairo_set_source_rgba (cr, 1, 0, 0, 1); + cairo_stroke (cr); +#endif cairo_destroy (cr); /* Upload the Cairo surface to a GL texture */ - item.render_data.texture_id = gsk_gl_driver_create_texture (self->gl_driver, - item.size.width, - item.size.height); - gsk_gl_driver_bind_source_texture (self->gl_driver, item.render_data.texture_id); + item.texture_id = gsk_gl_driver_create_texture (self->gl_driver, + item.size.width * self->scale_factor, + item.size.height * self->scale_factor); + gsk_gl_driver_bind_source_texture (self->gl_driver, item.texture_id); gsk_gl_driver_init_texture_with_surface (self->gl_driver, - item.render_data.texture_id, + item.texture_id, surface, GL_NEAREST, GL_NEAREST); @@ -900,66 +1014,62 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, break; } - item.render_data.program_id = program_id; - /* Create the vertex buffers holding the geometry of the quad */ - { - GskQuadVertex vertex_data[N_VERTICES] = { - { { item.min.x, item.min.y }, { 0, 0 }, }, - { { item.min.x, item.max.y }, { 0, 1 }, }, - { { item.max.x, item.min.y }, { 1, 0 }, }, + if (item.render_target == 0) + { + GskQuadVertex vertex_data[N_VERTICES] = { + { { item.min.x, item.min.y }, { 0, 0 }, }, + { { item.min.x, item.max.y }, { 0, 1 }, }, + { { item.max.x, item.min.y }, { 1, 0 }, }, - { { item.max.x, item.max.y }, { 1, 1 }, }, - { { item.min.x, item.max.y }, { 0, 1 }, }, - { { item.max.x, item.min.y }, { 1, 0 }, }, - }; + { { item.max.x, item.max.y }, { 1, 1 }, }, + { { item.min.x, item.max.y }, { 0, 1 }, }, + { { item.max.x, item.min.y }, { 1, 0 }, }, + }; - item.render_data.vao_id = - gsk_gl_driver_create_vao_for_quad (self->gl_driver, - item.render_data.program->position_location, - item.render_data.program->uv_location, - N_VERTICES, - vertex_data); - } + item.vao_id = gsk_gl_driver_create_vao_for_quad (self->gl_driver, + item.program->position_location, + item.program->uv_location, + N_VERTICES, + vertex_data); + } + else + { + GskQuadVertex vertex_data[N_VERTICES] = { + { { item.min.x, item.min.y }, { 0, 1 }, }, + { { item.min.x, item.max.y }, { 0, 0 }, }, + { { item.max.x, item.min.y }, { 1, 1 }, }, + + { { item.max.x, item.max.y }, { 1, 0 }, }, + { { item.min.x, item.max.y }, { 0, 0 }, }, + { { item.max.x, item.min.y }, { 1, 1 }, }, + }; + + item.vao_id = gsk_gl_driver_create_vao_for_quad (self->gl_driver, + item.program->position_location, + item.program->uv_location, + N_VERTICES, + vertex_data); + } GSK_NOTE (OPENGL, g_print ("Adding node <%s>[%p] to render items\n", node->name != NULL ? node->name : "unnamed", node)); g_array_append_val (render_items, item); - ritem = &g_array_index (render_items, RenderItem, render_items->len - 1); - - if (item.children != NULL) - render_items = item.children; } -static gboolean +static void gsk_gl_renderer_validate_tree (GskGLRenderer *self, GskRenderNode *root, const graphene_matrix_t *projection) { - graphene_matrix_t identity; - - if (self->gl_context == NULL) - { - GSK_NOTE (OPENGL, g_print ("No valid GL context associated to the renderer")); - return FALSE; - } - - graphene_matrix_init_identity (&identity); + graphene_matrix_t modelview; + graphene_matrix_init_scale (&modelview, self->scale_factor, self->scale_factor, 1.0f); gdk_gl_context_make_current (self->gl_context); - gsk_gl_driver_begin_frame (self->gl_driver); - - GSK_NOTE (OPENGL, g_print ("RenderNode -> RenderItem\n")); - gsk_gl_renderer_add_render_item (self, projection, &identity, self->render_items, root, NULL); - - GSK_NOTE (OPENGL, g_print ("Total render items: %d\n", - self->render_items->len)); - - gsk_gl_driver_end_frame (self->gl_driver); - - return TRUE; + gsk_gl_renderer_add_render_item (self, projection, &modelview, self->render_items, root, + self->texture_id); } static void @@ -1003,15 +1113,31 @@ gsk_gl_renderer_setup_render_mode (GskGLRenderer *self) { GdkDrawingContext *context = gsk_renderer_get_drawing_context (GSK_RENDERER (self)); GdkWindow *window = gsk_renderer_get_window (GSK_RENDERER (self)); - GdkRectangle extents; - int scale_factor = gdk_window_get_scale_factor (window); + cairo_region_t *clip = gdk_drawing_context_get_clip (context); + cairo_rectangle_int_t extents; + int window_height; - cairo_region_get_extents (gdk_drawing_context_get_clip (context), &extents); + /* Fall back to RENDER_FULL */ + if (clip == NULL) + { + glDisable (GL_SCISSOR_TEST); + return; + } + + g_assert (cairo_region_num_rectangles (clip) == 1); + + window_height = gdk_window_get_height (window) * self->scale_factor; + + cairo_region_get_extents (clip, &extents); + /*cairo_region_get_rectangle (clip, 0, &extents);*/ - glScissor (extents.x * scale_factor, - (gdk_window_get_height (window) - extents.height - extents.y) * scale_factor, - extents.width * scale_factor, extents.height * scale_factor); glEnable (GL_SCISSOR_TEST); + glScissor (extents.x * self->scale_factor, + window_height - (extents.height * self->scale_factor) - (extents.y * self->scale_factor), + extents.width * self->scale_factor, + extents.height * self->scale_factor); + + cairo_region_destroy (clip); break; } @@ -1021,8 +1147,6 @@ gsk_gl_renderer_setup_render_mode (GskGLRenderer *self) } } -#define ORTHO_NEAR_PLANE -10000 -#define ORTHO_FAR_PLANE 10000 static void gsk_gl_renderer_do_render (GskRenderer *renderer, @@ -1042,22 +1166,29 @@ gsk_gl_renderer_do_render (GskRenderer *renderer, profiler = gsk_renderer_get_profiler (renderer); #endif + if (self->gl_context == NULL) + { + GSK_NOTE (OPENGL, g_print ("No valid GL context associated to the renderer")); + return; + } + + self->viewport = *viewport; + /* Set up the modelview and projection matrices to fit our viewport */ graphene_matrix_init_scale (&modelview, scale_factor, scale_factor, 1.0); graphene_matrix_init_ortho (&projection, viewport->origin.x, - viewport->origin.x + viewport->size.width * scale_factor, - viewport->origin.y + viewport->size.height * scale_factor, + viewport->origin.x + viewport->size.width, viewport->origin.y, + viewport->origin.y + viewport->size.height, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); - gsk_gl_renderer_update_frustum (self, &modelview, &projection); - - if (!gsk_gl_renderer_validate_tree (self, root, &projection)) - return; + if (self->texture_id == 0) + graphene_matrix_scale (&projection, 1, -1, 1); gsk_gl_driver_begin_frame (self->gl_driver); + gsk_gl_renderer_validate_tree (self, root, &projection); #ifdef G_ENABLE_DEBUG gsk_gl_profiler_begin_gpu_region (self->gl_profiler); @@ -1065,8 +1196,8 @@ gsk_gl_renderer_do_render (GskRenderer *renderer, #endif /* Ensure that the viewport is up to date */ - if (gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id)) - gsk_gl_renderer_resize_viewport (self, viewport, scale_factor); + gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id); + gsk_gl_renderer_resize_viewport (self, viewport); gsk_gl_renderer_setup_render_mode (self); @@ -1076,12 +1207,13 @@ gsk_gl_renderer_do_render (GskRenderer *renderer, glDepthFunc (GL_LEQUAL); glEnable (GL_BLEND); + /* Pre-multiplied alpha! */ glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation (GL_FUNC_ADD); - GSK_NOTE (OPENGL, g_print ("Rendering %u items\n", self->render_items->len)); for (i = 0; i < self->render_items->len; i++) { - RenderItem *item = &g_array_index (self->render_items, RenderItem, i); + const RenderItem *item = &g_array_index (self->render_items, RenderItem, i); render_item (self, item); } @@ -1109,37 +1241,39 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer, { GskGLRenderer *self = GSK_GL_RENDERER (renderer); GdkTexture *texture; - cairo_surface_t *surface; - cairo_t *cr; + int stride; + guchar *data; + int width, height; g_return_val_if_fail (self->gl_context != NULL, NULL); self->render_mode = RENDER_FULL; + width = ceilf (viewport->size.width); + height = ceilf (viewport->size.height); gdk_gl_context_make_current (self->gl_context); + /* Prepare our framebuffer */ gsk_gl_driver_begin_frame (self->gl_driver); - gsk_gl_renderer_create_buffers (self, ceilf (viewport->size.width), ceilf (viewport->size.height), 1); + gsk_gl_renderer_create_buffers (self, width, height, 1); + gsk_gl_renderer_clear (self); gsk_gl_driver_end_frame (self->gl_driver); + /* Render the actual scene */ gsk_gl_renderer_do_render (renderer, root, viewport, 1); - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - ceilf (viewport->size.width), - ceilf (viewport->size.height)); - cr = cairo_create (surface); - gdk_cairo_draw_from_gl (cr, - gsk_renderer_get_window (renderer), - self->texture_id, - GL_TEXTURE, - 1.0, - 0, 0, - viewport->size.width, - viewport->size.height); - cairo_destroy (cr); + /* Prepare memory for the glReadPixels call */ + stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); + data = g_malloc (height * stride); - texture = gdk_texture_new_for_surface (surface); - cairo_surface_destroy (surface); + /* Bind our framebuffer again and read from it */ + gsk_gl_driver_begin_frame (self->gl_driver); + gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id); + glReadPixels (0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + gsk_gl_driver_end_frame (self->gl_driver); + + /* Create texture from the downloaded data */ + texture = gdk_texture_new_for_data (data, width, height, stride); return texture; } @@ -1149,18 +1283,20 @@ gsk_gl_renderer_render (GskRenderer *renderer, GskRenderNode *root) { GskGLRenderer *self = GSK_GL_RENDERER (renderer); + GdkWindow *window = gsk_renderer_get_window (renderer); graphene_rect_t viewport; - int scale_factor; if (self->gl_context == NULL) return; gdk_gl_context_make_current (self->gl_context); - gsk_renderer_get_viewport (renderer, &viewport); - scale_factor = gdk_window_get_scale_factor (gsk_renderer_get_window (renderer)); + viewport.origin.x = 0; + viewport.origin.y = 0; + viewport.size.width = gdk_window_get_width (window) * self->scale_factor; + viewport.size.height = gdk_window_get_height (window) * self->scale_factor; - gsk_gl_renderer_do_render (renderer, root, &viewport, scale_factor); + gsk_gl_renderer_do_render (renderer, root, &viewport, self->scale_factor); gdk_gl_context_make_current (self->gl_context); gsk_gl_renderer_clear_tree (self); @@ -1190,6 +1326,8 @@ gsk_gl_renderer_init (GskGLRenderer *self) graphene_matrix_init_identity (&self->mvp); self->render_items = g_array_new (FALSE, FALSE, sizeof (RenderItem)); + g_array_set_clear_func (self->render_items, (GDestroyNotify)destroy_render_item); + self->scale_factor = 1; #ifdef G_ENABLE_DEBUG { diff --git a/gsk/gskshaderbuilder.c b/gsk/gskshaderbuilder.c index 7085c4158d..d34360c0f6 100644 --- a/gsk/gskshaderbuilder.c +++ b/gsk/gskshaderbuilder.c @@ -41,8 +41,6 @@ shader_program_free (gpointer data) g_clear_pointer (&p->uniform_locations, g_hash_table_unref); g_clear_pointer (&p->attribute_locations, g_hash_table_unref); - glDeleteProgram (p->program_id); - g_slice_free (ShaderProgram, data); } diff --git a/gsk/meson.build b/gsk/meson.build index f51eff51c9..95b093b53d 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -5,6 +5,10 @@ gsk_private_source_shaders = [ 'resources/glsl/blit.vs.glsl', 'resources/glsl/color.fs.glsl', 'resources/glsl/color.vs.glsl', + 'resources/glsl/color_matrix.fs.glsl', + 'resources/glsl/color_matrix.vs.glsl', + 'resources/glsl/linear_gradient.fs.glsl', + 'resources/glsl/linear_gradient.vs.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/blend.fs.glsl b/gsk/resources/glsl/blend.fs.glsl index 39b3e4c657..1473d05384 100644 --- a/gsk/resources/glsl/blend.fs.glsl +++ b/gsk/resources/glsl/blend.fs.glsl @@ -57,6 +57,5 @@ void main() { // Use red for debugging missing blend modes res = vec3(1.0, 0.0, 0.0); } - - setOutputColor(vec4(res, Cs.a * uAlpha)); + setOutputColor(vec4(res, Cs.a) * uAlpha); } diff --git a/gsk/resources/glsl/blend.vs.glsl b/gsk/resources/glsl/blend.vs.glsl index 1208513f33..a453e32c9a 100644 --- a/gsk/resources/glsl/blend.vs.glsl +++ b/gsk/resources/glsl/blend.vs.glsl @@ -1,6 +1,5 @@ void main() { gl_Position = uMVP * vec4(aPosition, 0.0, 1.0); - // Flip the sampling vUv = vec2(aUv.x, aUv.y); } diff --git a/gsk/resources/glsl/blit.fs.glsl b/gsk/resources/glsl/blit.fs.glsl index ceb88ef50a..9f2eb4c5a2 100644 --- a/gsk/resources/glsl/blit.fs.glsl +++ b/gsk/resources/glsl/blit.fs.glsl @@ -1,5 +1,5 @@ void main() { vec4 diffuse = Texture(uSource, vUv); - setOutputColor(vec4(diffuse.xyz, diffuse.a * uAlpha)); + setOutputColor(diffuse * uAlpha); } diff --git a/gsk/resources/glsl/color.fs.glsl b/gsk/resources/glsl/color.fs.glsl index 62d4ba05de..6b7e34dc78 100644 --- a/gsk/resources/glsl/color.fs.glsl +++ b/gsk/resources/glsl/color.fs.glsl @@ -1,5 +1,9 @@ uniform vec4 uColor; void main() { - setOutputColor(uColor); + vec4 color = uColor; + + // Pre-multiply alpha + color.rgb *= color.a; + setOutputColor(color * uAlpha); } diff --git a/gsk/resources/glsl/color_matrix.fs.glsl b/gsk/resources/glsl/color_matrix.fs.glsl new file mode 100644 index 0000000000..9025bfb9c6 --- /dev/null +++ b/gsk/resources/glsl/color_matrix.fs.glsl @@ -0,0 +1,20 @@ +uniform mat4 uColorMatrix; +uniform vec4 uColorOffset; + +void main() { + vec4 diffuse = Texture(uSource, vUv); + vec4 color; + + color = diffuse; + + // Un-premultilpy + if (color.a != 0.0) + color.rgb /= color.a; + + color = uColorMatrix * diffuse + uColorOffset; + color = clamp(color, 0.0f, 1.0f); + + color.rgb *= color.a; + + setOutputColor(color * uAlpha); +} diff --git a/gsk/resources/glsl/color_matrix.vs.glsl b/gsk/resources/glsl/color_matrix.vs.glsl new file mode 100644 index 0000000000..1208513f33 --- /dev/null +++ b/gsk/resources/glsl/color_matrix.vs.glsl @@ -0,0 +1,6 @@ +void main() { + gl_Position = uMVP * vec4(aPosition, 0.0, 1.0); + + // Flip the sampling + vUv = vec2(aUv.x, aUv.y); +} diff --git a/gsk/resources/glsl/gl3_common.fs.glsl b/gsk/resources/glsl/gl3_common.fs.glsl index 50f72f5ba1..8ee5a14b32 100644 --- a/gsk/resources/glsl/gl3_common.fs.glsl +++ b/gsk/resources/glsl/gl3_common.fs.glsl @@ -3,8 +3,9 @@ precision highp float; uniform sampler2D uSource; uniform sampler2D uMask; uniform mat4 uMVP; -uniform float uAlpha; +uniform float uAlpha = 1.0; uniform int uBlendMode; +uniform vec4 uViewport; in vec2 vUv; diff --git a/gsk/resources/glsl/gl3_common.vs.glsl b/gsk/resources/glsl/gl3_common.vs.glsl index b90c2fea4e..f82f99c399 100644 --- a/gsk/resources/glsl/gl3_common.vs.glsl +++ b/gsk/resources/glsl/gl3_common.vs.glsl @@ -1,4 +1,5 @@ uniform mat4 uMVP; +uniform mat4 uProjection; in vec2 aPosition; in vec2 aUv; diff --git a/gsk/resources/glsl/linear_gradient.fs.glsl b/gsk/resources/glsl/linear_gradient.fs.glsl new file mode 100644 index 0000000000..cbbc71cc4b --- /dev/null +++ b/gsk/resources/glsl/linear_gradient.fs.glsl @@ -0,0 +1,43 @@ +uniform vec4 uColorStops[8]; +uniform float uColorOffsets[8]; +uniform int uNumColorStops; +uniform vec2 uStartPoint; +uniform vec2 uEndPoint; + +vec4 fragCoord() { + vec4 f = gl_FragCoord; + f.x += uViewport.x; + f.y = (uViewport.y + uViewport.w) - f.y; + return f; +} + +void main() { + float maxDist = length(uEndPoint - uStartPoint); + + // Position relative to startPoint + vec2 pos = fragCoord().xy - uStartPoint; + + // Gradient direction + vec2 gradient = uEndPoint - uStartPoint; + float gradientLength = length(gradient); + + // Current pixel, projected onto the line between the start point and the end point + // The projection will be relative to the start point! + vec2 proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient; + + // Offset of the current pixel + float offset = length(proj) / maxDist; + + vec4 color = uColorStops[0]; + for (int i = 1; i < uNumColorStops; i ++) { + if (offset >= uColorOffsets[i - 1]) { + float o = (offset - uColorOffsets[i - 1]) / (uColorOffsets[i] - uColorOffsets[i - 1]); + color = mix(uColorStops[i - 1], uColorStops[i], o); + } + } + + /* Pre-multiply */ + color.rgb *= color.a; + + setOutputColor(color * uAlpha); +} diff --git a/gsk/resources/glsl/linear_gradient.vs.glsl b/gsk/resources/glsl/linear_gradient.vs.glsl new file mode 100644 index 0000000000..a453e32c9a --- /dev/null +++ b/gsk/resources/glsl/linear_gradient.vs.glsl @@ -0,0 +1,5 @@ +void main() { + gl_Position = uMVP * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +}