From e9e373913ee5e5d1380fcd8634afb1b6237ae391 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 20 Aug 2021 22:50:41 -0400 Subject: gsk: Drop the gl renderer ngl supports all the same platforms as gl now, and has seen more improvements in the last cycle. --- gsk/gl/glutilsprivate.h | 317 -- gsk/gl/gskgldriver.c | 849 ----- gsk/gl/gskgldriverprivate.h | 86 - gsk/gl/gskglglyphcache.c | 397 --- gsk/gl/gskglglyphcacheprivate.h | 92 - gsk/gl/gskgliconcache.c | 275 -- gsk/gl/gskgliconcacheprivate.h | 44 - gsk/gl/gskglimage.c | 64 - gsk/gl/gskglimageprivate.h | 41 - gsk/gl/gskglnodesample.c | 51 - gsk/gl/gskglnodesampleprivate.h | 28 - gsk/gl/gskglprofiler.c | 181 - gsk/gl/gskglprofilerprivate.h | 18 - gsk/gl/gskglrenderer.c | 4559 ------------------------- gsk/gl/gskglrenderer.h | 52 - gsk/gl/gskglrendererprivate.h | 14 - gsk/gl/gskglrenderops.c | 976 ------ gsk/gl/gskglrenderopsprivate.h | 353 -- gsk/gl/gskglshaderbuilder.c | 271 -- gsk/gl/gskglshaderbuilderprivate.h | 42 - gsk/gl/gskglshadowcache.c | 141 - gsk/gl/gskglshadowcacheprivate.h | 31 - gsk/gl/gskgltextureatlas.c | 309 -- gsk/gl/gskgltextureatlasprivate.h | 72 - gsk/gl/opbuffer.c | 137 - gsk/gl/opbuffer.h | 306 -- gsk/gl/resources/blend.glsl | 310 -- gsk/gl/resources/blit.glsl | 13 - gsk/gl/resources/blur.glsl | 55 - gsk/gl/resources/border.glsl | 40 - gsk/gl/resources/color.glsl | 18 - gsk/gl/resources/color_matrix.glsl | 25 - gsk/gl/resources/coloring.glsl | 22 - gsk/gl/resources/conic_gradient.glsl | 73 - gsk/gl/resources/cross_fade.glsl | 20 - gsk/gl/resources/custom.glsl | 21 - gsk/gl/resources/inset_shadow.glsl | 41 - gsk/gl/resources/linear_gradient.glsl | 95 - gsk/gl/resources/outset_shadow.glsl | 33 - gsk/gl/resources/preamble.fs.glsl | 139 - gsk/gl/resources/preamble.glsl | 57 - gsk/gl/resources/preamble.vs.glsl | 69 - gsk/gl/resources/radial_gradient.glsl | 74 - gsk/gl/resources/repeat.glsl | 41 - gsk/gl/resources/unblurred_outset_shadow.glsl | 42 - gsk/gl/stb_rect_pack.c | 431 --- gsk/gl/stb_rect_pack.h | 191 -- gsk/gskglshader.c | 5 +- gsk/gskrenderer.c | 8 +- gsk/meson.build | 37 +- gsk/ngl/gskglprofiler.c | 181 + gsk/ngl/gskglprofilerprivate.h | 18 + gsk/ngl/gsknglcommandqueueprivate.h | 2 +- gsk/ngl/gskngltexturelibraryprivate.h | 2 +- gsk/ngl/stb_rect_pack.c | 431 +++ gsk/ngl/stb_rect_pack.h | 191 ++ 56 files changed, 828 insertions(+), 11563 deletions(-) delete mode 100644 gsk/gl/glutilsprivate.h delete mode 100644 gsk/gl/gskgldriver.c delete mode 100644 gsk/gl/gskgldriverprivate.h delete mode 100644 gsk/gl/gskglglyphcache.c delete mode 100644 gsk/gl/gskglglyphcacheprivate.h delete mode 100644 gsk/gl/gskgliconcache.c delete mode 100644 gsk/gl/gskgliconcacheprivate.h delete mode 100644 gsk/gl/gskglimage.c delete mode 100644 gsk/gl/gskglimageprivate.h delete mode 100644 gsk/gl/gskglnodesample.c delete mode 100644 gsk/gl/gskglnodesampleprivate.h delete mode 100644 gsk/gl/gskglprofiler.c delete mode 100644 gsk/gl/gskglprofilerprivate.h delete mode 100644 gsk/gl/gskglrenderer.c delete mode 100644 gsk/gl/gskglrenderer.h delete mode 100644 gsk/gl/gskglrendererprivate.h delete mode 100644 gsk/gl/gskglrenderops.c delete mode 100644 gsk/gl/gskglrenderopsprivate.h delete mode 100644 gsk/gl/gskglshaderbuilder.c delete mode 100644 gsk/gl/gskglshaderbuilderprivate.h delete mode 100644 gsk/gl/gskglshadowcache.c delete mode 100644 gsk/gl/gskglshadowcacheprivate.h delete mode 100644 gsk/gl/gskgltextureatlas.c delete mode 100644 gsk/gl/gskgltextureatlasprivate.h delete mode 100644 gsk/gl/opbuffer.c delete mode 100644 gsk/gl/opbuffer.h delete mode 100644 gsk/gl/resources/blend.glsl delete mode 100644 gsk/gl/resources/blit.glsl delete mode 100644 gsk/gl/resources/blur.glsl delete mode 100644 gsk/gl/resources/border.glsl delete mode 100644 gsk/gl/resources/color.glsl delete mode 100644 gsk/gl/resources/color_matrix.glsl delete mode 100644 gsk/gl/resources/coloring.glsl delete mode 100644 gsk/gl/resources/conic_gradient.glsl delete mode 100644 gsk/gl/resources/cross_fade.glsl delete mode 100644 gsk/gl/resources/custom.glsl delete mode 100644 gsk/gl/resources/inset_shadow.glsl delete mode 100644 gsk/gl/resources/linear_gradient.glsl delete mode 100644 gsk/gl/resources/outset_shadow.glsl delete mode 100644 gsk/gl/resources/preamble.fs.glsl delete mode 100644 gsk/gl/resources/preamble.glsl delete mode 100644 gsk/gl/resources/preamble.vs.glsl delete mode 100644 gsk/gl/resources/radial_gradient.glsl delete mode 100644 gsk/gl/resources/repeat.glsl delete mode 100644 gsk/gl/resources/unblurred_outset_shadow.glsl delete mode 100644 gsk/gl/stb_rect_pack.c delete mode 100644 gsk/gl/stb_rect_pack.h create mode 100644 gsk/ngl/gskglprofiler.c create mode 100644 gsk/ngl/gskglprofilerprivate.h create mode 100644 gsk/ngl/stb_rect_pack.c create mode 100644 gsk/ngl/stb_rect_pack.h (limited to 'gsk') diff --git a/gsk/gl/glutilsprivate.h b/gsk/gl/glutilsprivate.h deleted file mode 100644 index f035bda26e..0000000000 --- a/gsk/gl/glutilsprivate.h +++ /dev/null @@ -1,317 +0,0 @@ - -#pragma once - -#define SANITY_CHECKS 0 - - - -#define rounded_rect_top_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \ - r->bounds.origin.y, \ - r->corner[0].width, r->corner[0].height)) - -#define rounded_rect_top_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[1].width, \ - r->bounds.origin.y, \ - r->corner[1].width, r->corner[1].height)) - -#define rounded_rect_bottom_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[2].width, \ - r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \ - r->corner[2].width, r->corner[2].height)) - -#define rounded_rect_bottom_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \ - r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \ - r->corner[3].width, r->corner[3].height)) - - -#define rounded_rect_corner0(r) rounded_rect_top_left(r) -#define rounded_rect_corner1(r) rounded_rect_top_right(r) -#define rounded_rect_corner2(r) rounded_rect_bottom_right(r) -#define rounded_rect_corner3(r) rounded_rect_bottom_left(r) - -#define rounded_rect_corner(r, i) (rounded_rect_corner ##i(r)) -#define graphene_size_non_zero(s) (s->width > 0 && s->height > 0) -#define rounded_rect_has_corner(r, i) (r->corner[i].width > 0 && r->corner[i].height > 0) - -#define rect_contains_point(r, _x, _y) (_x >= (r)->origin.x && _x <= (r)->origin.x + (r)->size.width && \ - _y >= (r)->origin.y && _y <= (r)->origin.y + (r)->size.height) - -enum { - NINE_SLICE_TOP_LEFT = 0, - NINE_SLICE_TOP_CENTER = 1, - NINE_SLICE_TOP_RIGHT = 2, - NINE_SLICE_LEFT_CENTER = 3, - NINE_SLICE_CENTER = 4, - NINE_SLICE_RIGHT_CENTER = 5, - NINE_SLICE_BOTTOM_LEFT = 6, - NINE_SLICE_BOTTOM_CENTER = 7, - NINE_SLICE_BOTTOM_RIGHT = 8, -}; -#define NINE_SLICE_SIZE 9 /* Hah. */ - -typedef struct -{ - int texture_id; - float x; - float y; - float x2; - float y2; -} TextureRegion; - -static inline bool G_GNUC_PURE -slice_is_visible (const cairo_rectangle_int_t *r) -{ - return (r->width > 0 && r->height > 0); -} - -static inline void -nine_slice_rounded_rect (const GskRoundedRect *rect, - cairo_rectangle_int_t *out_rects) -{ - const graphene_point_t *origin = &rect->bounds.origin; - const graphene_size_t *size = &rect->bounds.size; - const int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height, - rect->corner[GSK_CORNER_TOP_RIGHT].height)); - const int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height, - rect->corner[GSK_CORNER_BOTTOM_RIGHT].height)); - const int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width, - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width)); - const int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width, - rect->corner[GSK_CORNER_BOTTOM_LEFT].width)); - - /* Top left */ - out_rects[0] = (cairo_rectangle_int_t) { - origin->x, origin->y, - left_width, top_height, - }; - - /* Top center */ - out_rects[1] = (cairo_rectangle_int_t) { - origin->x + size->width / 2.0 - 0.5, origin->y, - 1, top_height, - }; - - /* Top right */ - out_rects[2] = (cairo_rectangle_int_t) { - origin->x + size->width - right_width, origin->y, - right_width, top_height - }; - - /* Left center */ - out_rects[3] = (cairo_rectangle_int_t) { - origin->x, origin->y + size->height / 2, - left_width, 1, - }; - - /* center */ - out_rects[4] = (cairo_rectangle_int_t) { - origin->x + size->width / 2.0 - 0.5, - origin->y + size->height / 2.0 - 0.5, - 1, 1 - }; - - /* Right center */ - out_rects[5] = (cairo_rectangle_int_t) { - origin->x + size->width - right_width, - origin->y + (size->height / 2.0) - 0.5, - right_width, - 1, - }; - - /* Bottom Left */ - out_rects[6] = (cairo_rectangle_int_t) { - origin->x, origin->y + size->height - bottom_height, - left_width, bottom_height, - }; - - /* Bottom center */ - out_rects[7] = (cairo_rectangle_int_t) { - origin->x + (size->width / 2.0) - 0.5, - origin->y + size->height - bottom_height, - 1, bottom_height, - }; - - /* Bottom right */ - out_rects[8] = (cairo_rectangle_int_t) { - origin->x + size->width - right_width, - origin->y + size->height - bottom_height, - right_width, bottom_height, - }; - -#if SANITY_CHECKS - g_assert_cmpfloat (size->width, >=, left_width + right_width); - g_assert_cmpfloat (size->height, >=, top_height + bottom_height); -#endif -} - -static inline void -nine_slice_grow (cairo_rectangle_int_t *slices, - const int amount) -{ - /* top left */ - slices[0].x -= amount; - slices[0].y -= amount; - if (amount > slices[0].width) - slices[0].width += amount * 2; - else - slices[0].width += amount; - - if (amount > slices[0].height) - slices[0].height += amount * 2; - else - slices[0].height += amount; - - - /* Top center */ - slices[1].y -= amount; - if (amount > slices[1].height) - slices[1].height += amount * 2; - else - slices[1].height += amount; - - /* top right */ - slices[2].y -= amount; - if (amount > slices[2].width) - { - slices[2].x -= amount; - slices[2].width += amount * 2; - } - else - { - slices[2].width += amount; - } - - if (amount > slices[2].height) - slices[2].height += amount * 2; - else - slices[2].height += amount; - - - - slices[3].x -= amount; - if (amount > slices[3].width) - slices[3].width += amount * 2; - else - slices[3].width += amount; - - /* Leave Britney^Wcenter alone */ - - if (amount > slices[5].width) - { - slices[5].x -= amount; - slices[5].width += amount * 2; - } - else - { - slices[5].width += amount; - } - - - /* Bottom left */ - slices[6].x -= amount; - if (amount > slices[6].width) - { - slices[6].width += amount * 2; - } - else - { - slices[6].width += amount; - } - - if (amount > slices[6].height) - { - slices[6].y -= amount; - slices[6].height += amount * 2; - } - else - { - slices[6].height += amount; - } - - - /* Bottom center */ - if (amount > slices[7].height) - { - slices[7].y -= amount; - slices[7].height += amount * 2; - } - else - { - slices[7].height += amount; - } - - if (amount > slices[8].width) - { - slices[8].x -= amount; - slices[8].width += amount * 2; - } - else - { - slices[8].width += amount; - } - - if (amount > slices[8].height) - { - slices[8].y -= amount; - slices[8].height += amount * 2; - } - else - { - slices[8].height += amount; - } - -#if SANITY_CHECKS - { - for (int i = 0; i < 9; i ++) - { - g_assert_cmpint (slices[i].x, >=, 0); - g_assert_cmpint (slices[i].y, >=, 0); - g_assert_cmpint (slices[i].width, >=, 0); - g_assert_cmpint (slices[i].height, >=, 0); - } - - /* Rows don't overlap */ - for (int i = 0; i < 3; i++) - { - g_assert_cmpint (slices[i * 3 + 0].x + slices[i * 3 + 0].width, <, slices[i * 3 + 1].x); - } - } -#endif - -} - -static inline void -nine_slice_to_texture_coords (const cairo_rectangle_int_t *slices, - const int texture_width, - const int texture_height, - TextureRegion *out_regions) -{ - const float fw = (float)texture_width; - const float fh = (float)texture_height; - int i; - - for (i = 0; i < 9; i++) - { - out_regions[i] = (TextureRegion) { - 0, /* Texture id */ - slices[i].x / fw, - 1.0 - ((slices[i].y + slices[i].height) / fh), - (slices[i].x + slices[i].width) / fw, - 1.0 - (slices[i].y / fh), - }; - } - -#if SANITY_CHECKS - { - for (i = 0; i < 9; i++) - { - const TextureRegion *r = &out_regions[i]; - g_assert_cmpfloat (r->x, >=, 0); - g_assert_cmpfloat (r->x, <=, 1); - g_assert_cmpfloat (r->y, >=, 0); - g_assert_cmpfloat (r->y, <=, 1); - - g_assert_cmpfloat (r->x, <, r->x2); - g_assert_cmpfloat (r->y, <, r->y2); - } - } -#endif -} diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c deleted file mode 100644 index 8fe726b315..0000000000 --- a/gsk/gl/gskgldriver.c +++ /dev/null @@ -1,849 +0,0 @@ -#include "config.h" - -#include "gskgldriverprivate.h" - -#include "gskdebugprivate.h" -#include "gskprofilerprivate.h" -#include "gdk/gdkglcontextprivate.h" -#include "gdk/gdktextureprivate.h" -#include "gdk/gdkgltextureprivate.h" -#include "gdkmemorytextureprivate.h" - -#include -#include - - typedef struct { - GLuint fbo_id; - GLuint depth_stencil_id; -} Fbo; - -typedef struct { - GLuint texture_id; - int width; - int height; - GLuint min_filter; - GLuint mag_filter; - Fbo fbo; - GdkTexture *user; - guint in_use : 1; - guint permanent : 1; - - /* TODO: Make this optional and not for every texture... */ - TextureSlice *slices; - guint n_slices; -} Texture; - -struct _GskGLDriver -{ - GObject parent_instance; - - GdkGLContext *gl_context; - GskProfiler *profiler; - struct { - GQuark created_textures; - GQuark reused_textures; - GQuark surface_uploads; - } counters; - - Fbo default_fbo; - - GHashTable *textures; /* texture_id -> Texture */ - GHashTable *pointer_textures; /* pointer -> texture_id */ - - const Texture *bound_source_texture; - - int max_texture_size; - - gboolean in_frame : 1; -}; - -G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT) - -static void -upload_gdk_texture (GdkTexture *source_texture, - int target, - int x_offset, - int y_offset, - int width, - int height) -{ - cairo_surface_t *surface = NULL; - GdkMemoryFormat data_format; - const guchar *data; - gsize data_stride; - gsize bpp; - - g_return_if_fail (source_texture != NULL); - g_return_if_fail (x_offset + width <= gdk_texture_get_width (source_texture)); - g_return_if_fail (y_offset + height <= gdk_texture_get_height (source_texture)); - - /* Note: GdkGLTextures are already handled before we reach this and reused as-is */ - - if (GDK_IS_MEMORY_TEXTURE (source_texture)) - { - GdkMemoryTexture *memory_texture = GDK_MEMORY_TEXTURE (source_texture); - data = gdk_memory_texture_get_data (memory_texture); - data_format = gdk_memory_texture_get_format (memory_texture); - data_stride = gdk_memory_texture_get_stride (memory_texture); - } - else - { - /* Fall back to downloading to a surface */ - surface = gdk_texture_download_surface (source_texture); - cairo_surface_flush (surface); - data = cairo_image_surface_get_data (surface); - data_format = GDK_MEMORY_DEFAULT; - data_stride = cairo_image_surface_get_stride (surface); - } - - bpp = gdk_memory_format_bytes_per_pixel (data_format); - - gdk_gl_context_upload_texture (gdk_gl_context_get_current (), - data + x_offset * bpp + y_offset * data_stride, - width, height, data_stride, - data_format, target); - - if (surface) - cairo_surface_destroy (surface); -} - -static Texture * -texture_new (void) -{ - return g_slice_new0 (Texture); -} - -static inline void -fbo_clear (const Fbo *f) -{ - if (f->depth_stencil_id != 0) - glDeleteRenderbuffers (1, &f->depth_stencil_id); - - glDeleteFramebuffers (1, &f->fbo_id); -} - -static void -texture_free (gpointer data) -{ - Texture *t = data; - guint i; - - if (t->user) - gdk_texture_clear_render_data (t->user); - - if (t->fbo.fbo_id != 0) - fbo_clear (&t->fbo); - - if (t->texture_id != 0) - { - glDeleteTextures (1, &t->texture_id); - } - else - { - g_assert_cmpint (t->n_slices, >, 0); - - for (i = 0; i < t->n_slices; i ++) - glDeleteTextures (1, &t->slices[i].texture_id); - } - - g_slice_free (Texture, t); -} - -static void -gsk_gl_driver_set_texture_parameters (GskGLDriver *self, - int min_filter, - int mag_filter) -{ - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -static void -gsk_gl_driver_finalize (GObject *gobject) -{ - GskGLDriver *self = GSK_GL_DRIVER (gobject); - - gdk_gl_context_make_current (self->gl_context); - - g_clear_pointer (&self->textures, g_hash_table_unref); - g_clear_pointer (&self->pointer_textures, g_hash_table_unref); - g_clear_object (&self->profiler); - - if (self->gl_context == gdk_gl_context_get_current ()) - gdk_gl_context_clear_current (); - - G_OBJECT_CLASS (gsk_gl_driver_parent_class)->finalize (gobject); -} - -static void -gsk_gl_driver_class_init (GskGLDriverClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->finalize = gsk_gl_driver_finalize; -} - -static void -gsk_gl_driver_init (GskGLDriver *self) -{ - self->textures = g_hash_table_new_full (NULL, NULL, NULL, texture_free); - - self->max_texture_size = -1; - -#ifdef G_ENABLE_DEBUG - self->profiler = gsk_profiler_new (); - self->counters.created_textures = gsk_profiler_add_counter (self->profiler, - "created_textures", - "Textures created this frame", - TRUE); - self->counters.reused_textures = gsk_profiler_add_counter (self->profiler, - "reused_textures", - "Textures reused this frame", - TRUE); - self->counters.surface_uploads = gsk_profiler_add_counter (self->profiler, - "surface_uploads", - "Texture uploads from surfaces this frame", - TRUE); -#endif -} - -GskGLDriver * -gsk_gl_driver_new (GdkGLContext *context) -{ - GskGLDriver *self; - g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); - - self = (GskGLDriver *) g_object_new (GSK_TYPE_GL_DRIVER, NULL); - self->gl_context = context; - - return self; -} - -void -gsk_gl_driver_begin_frame (GskGLDriver *self) -{ - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - g_return_if_fail (!self->in_frame); - - self->in_frame = TRUE; - - if (self->max_texture_size < 0) - { - glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint *) &self->max_texture_size); - GSK_NOTE (OPENGL, g_message ("GL max texture size: %d", self->max_texture_size)); - } - - glBindFramebuffer (GL_FRAMEBUFFER, 0); - - glActiveTexture (GL_TEXTURE0); - glBindTexture (GL_TEXTURE_2D, 0); - - glActiveTexture (GL_TEXTURE0 + 1); - glBindTexture (GL_TEXTURE_2D, 0); - - glBindVertexArray (0); - glUseProgram (0); - - glActiveTexture (GL_TEXTURE0); - -#ifdef G_ENABLE_DEBUG - gsk_profiler_reset (self->profiler); -#endif -} - -gboolean -gsk_gl_driver_in_frame (GskGLDriver *self) -{ - return self->in_frame; -} - -void -gsk_gl_driver_end_frame (GskGLDriver *self) -{ - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - g_return_if_fail (self->in_frame); - - self->bound_source_texture = NULL; - - self->default_fbo.fbo_id = 0; - -#ifdef G_ENABLE_DEBUG - GSK_NOTE (OPENGL, - g_message ("Textures created: %" G_GINT64_FORMAT "\n" - " Textures reused: %" G_GINT64_FORMAT "\n" - " Surface uploads: %" G_GINT64_FORMAT, - gsk_profiler_counter_get (self->profiler, self->counters.created_textures), - gsk_profiler_counter_get (self->profiler, self->counters.reused_textures), - gsk_profiler_counter_get (self->profiler, self->counters.surface_uploads))); -#endif - - GSK_NOTE (OPENGL, - g_message ("*** Frame end: textures=%d", - g_hash_table_size (self->textures))); - - self->in_frame = FALSE; -} - -int -gsk_gl_driver_collect_textures (GskGLDriver *self) -{ - GHashTableIter iter; - gpointer value_p = NULL; - int old_size; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0); - g_return_val_if_fail (!self->in_frame, 0); - - old_size = g_hash_table_size (self->textures); - - g_hash_table_iter_init (&iter, self->textures); - while (g_hash_table_iter_next (&iter, NULL, &value_p)) - { - Texture *t = value_p; - - if (t->user || t->permanent) - continue; - - if (t->in_use) - { - t->in_use = FALSE; - - if (t->fbo.fbo_id != 0) - { - fbo_clear (&t->fbo); - t->fbo.fbo_id = 0; - } - } - else - { - /* Remove from self->pointer_textures. */ - /* TODO: Is there a better way for this? */ - if (self->pointer_textures) - { - GHashTableIter pointer_iter; - gpointer value; - gpointer p; - - g_hash_table_iter_init (&pointer_iter, self->pointer_textures); - while (g_hash_table_iter_next (&pointer_iter, &p, &value)) - { - if (GPOINTER_TO_INT (value) == t->texture_id) - { - g_hash_table_iter_remove (&pointer_iter); - break; - } - } - } - - g_hash_table_iter_remove (&iter); - } - } - - return old_size - g_hash_table_size (self->textures); -} - - -GdkGLContext * -gsk_gl_driver_get_gl_context (GskGLDriver *self) -{ - return self->gl_context; -} - -int -gsk_gl_driver_get_max_texture_size (GskGLDriver *self) -{ - if (self->max_texture_size < 0) - { - if (gdk_gl_context_get_use_es (self->gl_context)) - return 2048; - - return 1024; - } - - return self->max_texture_size; -} - -static Texture * -gsk_gl_driver_get_texture (GskGLDriver *self, - int texture_id) -{ - Texture *t; - - if (g_hash_table_lookup_extended (self->textures, GINT_TO_POINTER (texture_id), NULL, (gpointer *) &t)) - return t; - - return NULL; -} - -static Texture * -create_texture (GskGLDriver *self, - float fwidth, - float fheight) -{ - guint texture_id; - Texture *t; - int width = ceilf (fwidth); - int height = ceilf (fheight); - - g_assert (width > 0); - g_assert (height > 0); - - if (width > self->max_texture_size || - height > self->max_texture_size) - { - g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...", - width, height, - self->max_texture_size); - - width = MIN (width, self->max_texture_size); - height = MIN (height, self->max_texture_size); - } - - glGenTextures (1, &texture_id); - t = texture_new (); - t->texture_id = texture_id; - t->width = width; - t->height = height; - t->min_filter = GL_NEAREST; - t->mag_filter = GL_NEAREST; - t->in_use = TRUE; - g_hash_table_insert (self->textures, GINT_TO_POINTER (texture_id), t); -#ifdef G_ENABLE_DEBUG - gsk_profiler_counter_inc (self->profiler, self->counters.created_textures); -#endif - - return t; -} - -static void -gsk_gl_driver_release_texture (gpointer data) -{ - Texture *t = data; - - t->user = NULL; -} - -void -gsk_gl_driver_slice_texture (GskGLDriver *self, - GdkTexture *texture, - TextureSlice **out_slices, - guint *out_n_slices) -{ - const int max_texture_size = gsk_gl_driver_get_max_texture_size (self) / 4; // XXX Too much? - const int cols = (texture->width / max_texture_size) + 1; - const int rows = (texture->height / max_texture_size) + 1; - int col, row; - int x = 0, y = 0; /* Position in the texture */ - TextureSlice *slices; - Texture *tex; - - g_assert (texture->width > max_texture_size || texture->height > max_texture_size); - - - tex = gdk_texture_get_render_data (texture, self); - - if (tex != NULL) - { - g_assert (tex->n_slices > 0); - *out_slices = tex->slices; - *out_n_slices = tex->n_slices; - return; - } - - slices = g_new0 (TextureSlice, cols * rows); - - for (col = 0; col < cols; col ++) - { - const int slice_width = MIN (max_texture_size, texture->width - x); - - for (row = 0; row < rows; row ++) - { - const int slice_height = MIN (max_texture_size, texture->height - y); - const int slice_index = (col * rows) + row; - guint texture_id; - - glGenTextures (1, &texture_id); - -#ifdef G_ENABLE_DEBUG - gsk_profiler_counter_inc (self->profiler, self->counters.created_textures); -#endif - glBindTexture (GL_TEXTURE_2D, texture_id); - gsk_gl_driver_set_texture_parameters (self, GL_NEAREST, GL_NEAREST); - upload_gdk_texture (texture, GL_TEXTURE_2D, x, y, slice_width, slice_height); - -#ifdef G_ENABLE_DEBUG - gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads); -#endif - - slices[slice_index].rect = (GdkRectangle){x, y, slice_width, slice_height}; - slices[slice_index].texture_id = texture_id; - - y += slice_height; - } - - y = 0; - x += slice_width; - } - - /* Allocate one Texture for the entire thing. */ - tex = texture_new (); - tex->width = texture->width; - tex->height = texture->height; - tex->min_filter = GL_NEAREST; - tex->mag_filter = GL_NEAREST; - tex->in_use = TRUE; - tex->slices = slices; - tex->n_slices = cols * rows; - - /* Use texture_free as destroy notify here since we are not inserting this Texture - * into self->textures! */ - gdk_texture_set_render_data (texture, self, tex, texture_free); - - *out_slices = slices; - *out_n_slices = cols * rows; -} - -int -gsk_gl_driver_get_texture_for_texture (GskGLDriver *self, - GdkTexture *texture, - int min_filter, - int mag_filter) -{ - Texture *t; - GdkTexture *downloaded_texture = NULL; - GdkTexture *source_texture; - - if (GDK_IS_GL_TEXTURE (texture)) - { - GdkGLTexture *gl_texture = (GdkGLTexture *) texture; - GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture); - - if (gdk_gl_context_is_shared (self->gl_context, texture_context)) - { - /* A GL texture from the same GL context is a simple task... */ - return gdk_gl_texture_get_id (gl_texture); - } - else - { - cairo_surface_t *surface; - - /* In this case, we have to temporarily make the texture's context the current one, - * download its data into our context and then create a texture from it. */ - if (texture_context) - gdk_gl_context_make_current (texture_context); - - surface = gdk_texture_download_surface (texture); - downloaded_texture = gdk_texture_new_for_surface (surface); - cairo_surface_destroy (surface); - - gdk_gl_context_make_current (self->gl_context); - - source_texture = downloaded_texture; - } - } - else - { - t = gdk_texture_get_render_data (texture, self); - - if (t) - { - if (t->min_filter == min_filter && t->mag_filter == mag_filter) - return t->texture_id; - } - - source_texture = texture; - } - - t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture)); - - if (gdk_texture_set_render_data (texture, self, t, gsk_gl_driver_release_texture)) - t->user = texture; - - gsk_gl_driver_bind_source_texture (self, t->texture_id); - gsk_gl_driver_init_texture (self, - t->texture_id, - source_texture, - min_filter, - mag_filter); - gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id, - "GdkTexture<%p> %d", texture, t->texture_id); - - if (downloaded_texture) - g_object_unref (downloaded_texture); - - return t->texture_id; -} - -static guint -texture_key_hash (gconstpointer v) -{ - const GskTextureKey *k = (GskTextureKey *)v; - - return GPOINTER_TO_UINT (k->pointer) - + (guint)(k->scale_x * 100) - + (guint)(k->scale_y * 100) - + (guint)k->filter * 2 + - + (guint)k->pointer_is_child; -} - -static gboolean -texture_key_equal (gconstpointer v1, gconstpointer v2) -{ - const GskTextureKey *k1 = (GskTextureKey *)v1; - const GskTextureKey *k2 = (GskTextureKey *)v2; - - return k1->pointer == k2->pointer && - k1->scale_x == k2->scale_x && - k1->scale_y == k2->scale_y && - k1->filter == k2->filter && - k1->pointer_is_child == k2->pointer_is_child && - (!k1->pointer_is_child || graphene_rect_equal (&k1->parent_rect, &k2->parent_rect)); -} - -int -gsk_gl_driver_get_texture_for_key (GskGLDriver *self, - GskTextureKey *key) -{ - int id = 0; - - if (G_UNLIKELY (self->pointer_textures == NULL)) - self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL); - - id = GPOINTER_TO_INT (g_hash_table_lookup (self->pointer_textures, key)); - - if (id != 0) - { - Texture *t; - - t = g_hash_table_lookup (self->textures, GINT_TO_POINTER (id)); - - if (t != NULL) - t->in_use = TRUE; - } - - return id; -} - -void -gsk_gl_driver_set_texture_for_key (GskGLDriver *self, - GskTextureKey *key, - int texture_id) -{ - GskTextureKey *k; - - if (G_UNLIKELY (self->pointer_textures == NULL)) - self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL); - - k = g_new (GskTextureKey, 1); - *k = *key; - - g_hash_table_insert (self->pointer_textures, k, GINT_TO_POINTER (texture_id)); -} - -int -gsk_gl_driver_create_texture (GskGLDriver *self, - float width, - float height) -{ - Texture *t; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), -1); - - t = create_texture (self, width, height); - - return t->texture_id; -} - -void -gsk_gl_driver_create_render_target (GskGLDriver *self, - int width, - int height, - int min_filter, - int mag_filter, - int *out_texture_id, - int *out_render_target_id) -{ - GLuint fbo_id; - Texture *texture; - - g_return_if_fail (self->in_frame); - - texture = create_texture (self, width, height); - gsk_gl_driver_bind_source_texture (self, texture->texture_id); - gsk_gl_driver_init_texture_empty (self, texture->texture_id, min_filter, mag_filter); - - glGenFramebuffers (1, &fbo_id); - glBindFramebuffer (GL_FRAMEBUFFER, fbo_id); - glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture_id, 0); - -#if 0 - if (add_depth_buffer || add_stencil_buffer) - { - glGenRenderbuffersEXT (1, &depth_stencil_buffer_id); - gdk_gl_context_label_object_printf (self->gl_context, GL_RENDERBUFFER, depth_stencil_buffer_id, - "%s buffer for %d", add_depth_buffer ? "Depth" : "Stencil", texture_id); - } - else - depth_stencil_buffer_id = 0; - - glBindRenderbuffer (GL_RENDERBUFFER, depth_stencil_buffer_id); - - if (add_depth_buffer || add_stencil_buffer) - { - if (add_stencil_buffer) - glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, t->width, t->height); - else - glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, t->width, t->height); - - if (add_depth_buffer) - glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, depth_stencil_buffer_id); - - if (add_stencil_buffer) - glFramebufferRenderbufferEXT (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, depth_stencil_buffer_id); - texture->fbo.depth_stencil_id = depth_stencil_buffer_id; - } -#endif - - texture->fbo.fbo_id = fbo_id; - - g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE); - - glBindFramebuffer (GL_FRAMEBUFFER, self->default_fbo.fbo_id); - - *out_texture_id = texture->texture_id; - *out_render_target_id = fbo_id; -} - -/* Mark the texture permanent, meaning it won'e be reused by the GLDriver. - * E.g. to store it in some other cache. */ -void -gsk_gl_driver_mark_texture_permanent (GskGLDriver *self, - int texture_id) -{ - Texture *t = gsk_gl_driver_get_texture (self, texture_id); - - g_assert (t != NULL); - - t->permanent = TRUE; -} - -void -gsk_gl_driver_bind_source_texture (GskGLDriver *self, - int texture_id) -{ - Texture *t; - - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - g_return_if_fail (self->in_frame); - - t = gsk_gl_driver_get_texture (self, texture_id); - if (t == NULL) - { - g_critical ("No texture %d found.", texture_id); - return; - } - - if (self->bound_source_texture != t) - { - glActiveTexture (GL_TEXTURE0); - glBindTexture (GL_TEXTURE_2D, t->texture_id); - - self->bound_source_texture = t; - } -} - -void -gsk_gl_driver_destroy_texture (GskGLDriver *self, - int texture_id) -{ - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - - g_hash_table_remove (self->textures, GINT_TO_POINTER (texture_id)); -} - - -void -gsk_gl_driver_init_texture_empty (GskGLDriver *self, - int texture_id, - int min_filter, - int mag_filter) -{ - Texture *t; - - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - - t = gsk_gl_driver_get_texture (self, texture_id); - if (t == NULL) - { - g_critical ("No texture %d found.", texture_id); - return; - } - - if (self->bound_source_texture != t) - { - g_critical ("You must bind the texture before initializing it."); - return; - } - - t->min_filter = min_filter; - t->mag_filter = mag_filter; - - gsk_gl_driver_set_texture_parameters (self, t->min_filter, t->mag_filter); - - if (gdk_gl_context_get_use_es (self->gl_context)) - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - else - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - - glBindTexture (GL_TEXTURE_2D, 0); -} - -static gboolean -filter_uses_mipmaps (int filter) -{ - return filter != GL_NEAREST && filter != GL_LINEAR; -} - -void -gsk_gl_driver_init_texture (GskGLDriver *self, - int texture_id, - GdkTexture *texture, - int min_filter, - int mag_filter) -{ - Texture *t; - - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - - t = gsk_gl_driver_get_texture (self, texture_id); - if (t == NULL) - { - g_critical ("No texture %d found.", texture_id); - return; - } - - if (self->bound_source_texture != t) - { - g_critical ("You must bind the texture before initializing it."); - return; - } - - gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter); - - upload_gdk_texture (texture, GL_TEXTURE_2D, 0, 0, t->width, t->height); - -#ifdef G_ENABLE_DEBUG - gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads); -#endif - - t->min_filter = min_filter; - t->mag_filter = mag_filter; - - if (filter_uses_mipmaps (t->min_filter)) - glGenerateMipmap (GL_TEXTURE_2D); -} diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h deleted file mode 100644 index 86c5dd93d1..0000000000 --- a/gsk/gl/gskgldriverprivate.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef __GSK_GL_DRIVER_PRIVATE_H__ -#define __GSK_GL_DRIVER_PRIVATE_H__ - -#include -#include -#include - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_DRIVER (gsk_gl_driver_get_type ()) - -G_DECLARE_FINAL_TYPE (GskGLDriver, gsk_gl_driver, GSK, GL_DRIVER, GObject) - -typedef struct { - float position[2]; - float uv[2]; -} GskQuadVertex; - -typedef struct { - cairo_rectangle_int_t rect; - guint texture_id; -} TextureSlice; - -typedef struct { - gpointer pointer; - float scale_x; - float scale_y; - int filter; - int pointer_is_child; - graphene_rect_t parent_rect; /* Only set if pointer_is_child */ -} GskTextureKey; - -GskGLDriver * gsk_gl_driver_new (GdkGLContext *context); -GdkGLContext *gsk_gl_driver_get_gl_context (GskGLDriver *driver); - -int gsk_gl_driver_get_max_texture_size (GskGLDriver *driver); - -void gsk_gl_driver_begin_frame (GskGLDriver *driver); -void gsk_gl_driver_end_frame (GskGLDriver *driver); -gboolean gsk_gl_driver_in_frame (GskGLDriver *driver); -int gsk_gl_driver_get_texture_for_texture (GskGLDriver *driver, - GdkTexture *texture, - int min_filter, - int mag_filter); -int gsk_gl_driver_get_texture_for_key (GskGLDriver *driver, - GskTextureKey *key); -void gsk_gl_driver_set_texture_for_key (GskGLDriver *driver, - GskTextureKey *key, - int texture_id); -int gsk_gl_driver_create_texture (GskGLDriver *driver, - float width, - float height); -void gsk_gl_driver_create_render_target (GskGLDriver *driver, - int width, - int height, - int min_filter, - int mag_filter, - int *out_texture_id, - int *out_render_target_id); -void gsk_gl_driver_mark_texture_permanent (GskGLDriver *self, - int texture_id); -void gsk_gl_driver_bind_source_texture (GskGLDriver *driver, - int texture_id); - -void gsk_gl_driver_init_texture_empty (GskGLDriver *driver, - int texture_id, - int min_filter, - int max_filter); -void gsk_gl_driver_init_texture (GskGLDriver *driver, - int texture_id, - GdkTexture *texture, - int min_filter, - int mag_filter); - -void gsk_gl_driver_destroy_texture (GskGLDriver *driver, - int texture_id); - -int gsk_gl_driver_collect_textures (GskGLDriver *driver); -void gsk_gl_driver_slice_texture (GskGLDriver *self, - GdkTexture *texture, - TextureSlice **out_slices, - guint *out_n_slices); - -G_END_DECLS - -#endif /* __GSK_GL_DRIVER_PRIVATE_H__ */ diff --git a/gsk/gl/gskglglyphcache.c b/gsk/gl/gskglglyphcache.c deleted file mode 100644 index 864c741709..0000000000 --- a/gsk/gl/gskglglyphcache.c +++ /dev/null @@ -1,397 +0,0 @@ -#include "config.h" - -#include "gskglglyphcacheprivate.h" -#include "gskgldriverprivate.h" -#include "gskdebugprivate.h" -#include "gskprivate.h" -#include "gskgltextureatlasprivate.h" - -#include "gdk/gdkglcontextprivate.h" -#include "gdk/gdkmemorytextureprivate.h" - -#include -#include -#include -#include - -/* Cache eviction strategy - * - * We mark glyphs as accessed every time we use them. - * Every few frames, we mark glyphs that haven't been - * accessed since the last check as old. - * - * We keep count of the pixels of each atlas that are - * taken up by old data. When the fraction of old pixels - * gets too high, we drop the atlas and all the items it - * contained. - * - * Big glyphs are not stored in the atlas, they get their - * own texture, but they are still cached. - */ - -#define MAX_FRAME_AGE (60) -#define MAX_GLYPH_SIZE 128 /* Will get its own texture if bigger */ - -static guint glyph_cache_hash (gconstpointer v); -static gboolean glyph_cache_equal (gconstpointer v1, - gconstpointer v2); -static void glyph_cache_key_free (gpointer v); -static void glyph_cache_value_free (gpointer v); - -GskGLGlyphCache * -gsk_gl_glyph_cache_new (GdkDisplay *display, - GskGLTextureAtlases *atlases) -{ - GskGLGlyphCache *glyph_cache; - - glyph_cache = g_new0 (GskGLGlyphCache, 1); - - glyph_cache->display = display; - glyph_cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal, - glyph_cache_key_free, glyph_cache_value_free); - - glyph_cache->atlases = gsk_gl_texture_atlases_ref (atlases); - - glyph_cache->ref_count = 1; - - return glyph_cache; -} - -GskGLGlyphCache * -gsk_gl_glyph_cache_ref (GskGLGlyphCache *self) -{ - self->ref_count++; - - return self; -} - -void -gsk_gl_glyph_cache_unref (GskGLGlyphCache *self) -{ - g_assert (self->ref_count > 0); - - if (self->ref_count == 1) - { - gsk_gl_texture_atlases_unref (self->atlases); - g_hash_table_unref (self->hash_table); - g_free (self); - return; - } - - self->ref_count--; -} - -static gboolean -glyph_cache_equal (gconstpointer v1, gconstpointer v2) -{ - return memcmp (v1, v2, sizeof (CacheKeyData)) == 0; -} - -static guint -glyph_cache_hash (gconstpointer v) -{ - const GlyphCacheKey *key = v; - - return key->hash; -} - -static void -glyph_cache_key_free (gpointer v) -{ - GlyphCacheKey *f = v; - - g_object_unref (f->data.font); - g_free (f); -} - -static void -glyph_cache_value_free (gpointer v) -{ - g_free (v); -} - -static gboolean -render_glyph (GlyphCacheKey *key, - GskGLCachedGlyph *value, - GskImageRegion *region) -{ - cairo_surface_t *surface; - cairo_t *cr; - cairo_scaled_font_t *scaled_font; - PangoGlyphString glyph_string; - PangoGlyphInfo glyph_info; - int surface_width, surface_height; - int stride; - unsigned char *data; - - scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->data.font); - if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS)) - { - g_warning ("Failed to get a font"); - return FALSE; - } - - surface_width = value->draw_width * key->data.scale / 1024; - surface_height = value->draw_height * key->data.scale / 1024; - - stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, surface_width); - data = g_malloc0 (stride * surface_height); - surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, - surface_width, surface_height, - stride); - cairo_surface_set_device_scale (surface, key->data.scale / 1024.0, key->data.scale / 1024.0); - - cr = cairo_create (surface); - - cairo_set_scaled_font (cr, scaled_font); - cairo_set_source_rgba (cr, 1, 1, 1, 1); - - glyph_info.glyph = key->data.glyph; - glyph_info.geometry.width = value->draw_width * 1024; - if (glyph_info.glyph & PANGO_GLYPH_UNKNOWN_FLAG) - glyph_info.geometry.x_offset = 256 * key->data.xshift; - else - glyph_info.geometry.x_offset = 256 * key->data.xshift - value->draw_x * 1024; - glyph_info.geometry.y_offset = 256 * key->data.yshift - value->draw_y * 1024; - - glyph_string.num_glyphs = 1; - glyph_string.glyphs = &glyph_info; - - pango_cairo_show_glyph_string (cr, key->data.font, &glyph_string); - cairo_destroy (cr); - - cairo_surface_flush (surface); - - region->width = cairo_image_surface_get_width (surface); - region->height = cairo_image_surface_get_height (surface); - region->stride = cairo_image_surface_get_stride (surface); - region->data = data; - if (value->atlas) - { - region->x = (gsize)(value->tx * value->atlas->width); - region->y = (gsize)(value->ty * value->atlas->height); - } - else - { - region->x = 0; - region->y = 0; - } - - cairo_surface_destroy (surface); - - return TRUE; -} - -static void -upload_glyph (GlyphCacheKey *key, - GskGLCachedGlyph *value) -{ - GskImageRegion r; - guchar *pixel_data; - guchar *free_data = NULL; - guint gl_format; - guint gl_type; - - gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (), - "Uploading glyph %d", - key->data.glyph); - - if (render_glyph (key, value, &r)) - { - glPixelStorei (GL_UNPACK_ROW_LENGTH, r.stride / 4); - glBindTexture (GL_TEXTURE_2D, value->texture_id); - - if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - { - pixel_data = free_data = g_malloc (r.width * r.height * 4); - gdk_memory_convert (pixel_data, r.width * 4, - GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, - r.data, r.width * 4, - GDK_MEMORY_DEFAULT, r.width, r.height); - gl_format = GL_RGBA; - gl_type = GL_UNSIGNED_BYTE; - } - else - { - pixel_data = r.data; - gl_format = GL_BGRA; - gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - } - - glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height, - gl_format, gl_type, pixel_data); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - g_free (r.data); - g_free (free_data); - } - - gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ()); -} - -static void -add_to_cache (GskGLGlyphCache *self, - GlyphCacheKey *key, - GskGLDriver *driver, - GskGLCachedGlyph *value) -{ - const int width = value->draw_width * key->data.scale / 1024; - const int height = value->draw_height * key->data.scale / 1024; - - if (width < MAX_GLYPH_SIZE && height < MAX_GLYPH_SIZE) - { - GskGLTextureAtlas *atlas = NULL; - int packed_x = 0; - int packed_y = 0; - - gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y); - - value->tx = (float)(packed_x + 1) / atlas->width; - value->ty = (float)(packed_y + 1) / atlas->height; - value->tw = (float)width / atlas->width; - value->th = (float)height / atlas->height; - value->used = TRUE; - - value->atlas = atlas; - value->texture_id = atlas->texture_id; - } - else - { - value->atlas = NULL; - value->texture_id = gsk_gl_driver_create_texture (driver, width, height); - gsk_gl_driver_mark_texture_permanent (driver, value->texture_id); - - gsk_gl_driver_bind_source_texture (driver, value->texture_id); - gsk_gl_driver_init_texture_empty (driver, value->texture_id, GL_LINEAR, GL_LINEAR); - - value->tx = 0.0f; - value->ty = 0.0f; - value->tw = 1.0f; - value->th = 1.0f; - } - - upload_glyph (key, value); -} - -void -gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache *cache, - GlyphCacheKey *lookup, - GskGLDriver *driver, - const GskGLCachedGlyph **cached_glyph_out) -{ - GskGLCachedGlyph *value; - - value = g_hash_table_lookup (cache->hash_table, lookup); - - if (value) - { - if (value->atlas && !value->used) - { - gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height); - value->used = TRUE; - } - value->accessed = TRUE; - - *cached_glyph_out = value; - return; - } - - { - GlyphCacheKey *key; - PangoRectangle ink_rect; - - pango_font_get_glyph_extents (lookup->data.font, lookup->data.glyph, &ink_rect, NULL); - pango_extents_to_pixels (&ink_rect, NULL); - if (lookup->data.xshift != 0) - ink_rect.width += 1; - if (lookup->data.yshift != 0) - ink_rect.height += 1; - - value = g_new0 (GskGLCachedGlyph, 1); - - value->draw_x = ink_rect.x; - value->draw_y = ink_rect.y; - value->draw_width = ink_rect.width; - value->draw_height = ink_rect.height; - value->accessed = TRUE; - value->atlas = NULL; /* For now */ - - key = g_new0 (GlyphCacheKey, 1); - - key->data.font = g_object_ref (lookup->data.font); - key->data.glyph = lookup->data.glyph; - key->data.xshift = lookup->data.xshift; - key->data.yshift = lookup->data.yshift; - key->data.scale = lookup->data.scale; - key->hash = lookup->hash; - - if (key->data.scale > 0 && - value->draw_width * key->data.scale / 1024 > 0 && - value->draw_height * key->data.scale / 1024 > 0) - add_to_cache (cache, key, driver, value); - - *cached_glyph_out = value; - g_hash_table_insert (cache->hash_table, key, value); - } -} - -void -gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self, - GskGLDriver *driver, - GPtrArray *removed_atlases) -{ - GHashTableIter iter; - GlyphCacheKey *key; - GskGLCachedGlyph *value; - guint dropped = 0; - - self->timestamp++; - - if (removed_atlases->len > 0) - { - g_hash_table_iter_init (&iter, self->hash_table); - while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value)) - { - if (g_ptr_array_find (removed_atlases, value->atlas, NULL)) - { - g_hash_table_iter_remove (&iter); - dropped++; - } - } - } - - if (self->timestamp % MAX_FRAME_AGE == 30) - { - g_hash_table_iter_init (&iter, self->hash_table); - while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value)) - { - if (!value->accessed) - { - if (value->atlas) - { - if (value->used) - { - gsk_gl_texture_atlas_mark_unused (value->atlas, value->draw_width, value->draw_height); - value->used = FALSE; - } - } - else - { - gsk_gl_driver_destroy_texture (driver, value->texture_id); - g_hash_table_iter_remove (&iter); - - /* Sadly, if we drop an atlas-less cached glyph, we - * have to treat it like a dropped atlas and purge - * text node render data. - */ - dropped++; - } - } - else - value->accessed = FALSE; - } - - GSK_NOTE(GLYPH_CACHE, g_message ("%d glyphs cached", g_hash_table_size (self->hash_table))); - } - - GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d glyphs", dropped)); -} diff --git a/gsk/gl/gskglglyphcacheprivate.h b/gsk/gl/gskglglyphcacheprivate.h deleted file mode 100644 index d66b251a44..0000000000 --- a/gsk/gl/gskglglyphcacheprivate.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef __GSK_GL_GLYPH_CACHE_PRIVATE_H__ -#define __GSK_GL_GLYPH_CACHE_PRIVATE_H__ - -#include "gskgldriverprivate.h" -#include "gskglimageprivate.h" -#include "gskgltextureatlasprivate.h" -#include -#include - -typedef struct -{ - int ref_count; - - GdkDisplay *display; - GHashTable *hash_table; - GskGLTextureAtlases *atlases; - - int timestamp; -} GskGLGlyphCache; - -struct _CacheKeyData -{ - PangoFont *font; - PangoGlyph glyph; - guint xshift : 3; - guint yshift : 3; - guint scale : 26; /* times 1024 */ -}; - -typedef struct _CacheKeyData CacheKeyData; - -struct _GlyphCacheKey -{ - CacheKeyData data; - guint hash; -}; - -typedef struct _GlyphCacheKey GlyphCacheKey; - -#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125))) - -static inline void -glyph_cache_key_set_glyph_and_shift (GlyphCacheKey *key, - PangoGlyph glyph, - float x, - float y) -{ - key->data.glyph = glyph; - key->data.xshift = PHASE (x); - key->data.yshift = PHASE (y); - key->hash = GPOINTER_TO_UINT (key->data.font) ^ - key->data.glyph ^ - (key->data.xshift << 24) ^ - (key->data.yshift << 26) ^ - key->data.scale; -} - -typedef struct _GskGLCachedGlyph GskGLCachedGlyph; - -struct _GskGLCachedGlyph -{ - GskGLTextureAtlas *atlas; - guint texture_id; - - float tx; - float ty; - float tw; - float th; - - int draw_x; - int draw_y; - int draw_width; - int draw_height; - - guint accessed : 1; /* accessed since last check */ - guint used : 1; /* accounted as used in the atlas */ -}; - - -GskGLGlyphCache * gsk_gl_glyph_cache_new (GdkDisplay *display, - GskGLTextureAtlases *atlases); -GskGLGlyphCache * gsk_gl_glyph_cache_ref (GskGLGlyphCache *self); -void gsk_gl_glyph_cache_unref (GskGLGlyphCache *self); -void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self, - GskGLDriver *driver, - GPtrArray *removed_atlases); -void gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache *self, - GlyphCacheKey *lookup, - GskGLDriver *driver, - const GskGLCachedGlyph **cached_glyph_out); - -#endif diff --git a/gsk/gl/gskgliconcache.c b/gsk/gl/gskgliconcache.c deleted file mode 100644 index 4bbbdbb4da..0000000000 --- a/gsk/gl/gskgliconcache.c +++ /dev/null @@ -1,275 +0,0 @@ -#include "gskgliconcacheprivate.h" -#include "gskgltextureatlasprivate.h" -#include "gdk/gdktextureprivate.h" -#include "gdk/gdkmemorytextureprivate.h" -#include "gdk/gdkglcontextprivate.h" - -#include - -#define MAX_FRAME_AGE 60 - -static void -icon_data_free (gpointer p) -{ - g_object_unref (((IconData *)p)->source_texture); - g_free (p); -} - -GskGLIconCache * -gsk_gl_icon_cache_new (GdkDisplay *display, - GskGLTextureAtlases *atlases) -{ - GskGLIconCache *self; - - self = g_new0 (GskGLIconCache, 1); - - self->display = display; - self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free); - self->atlases = gsk_gl_texture_atlases_ref (atlases); - self->ref_count = 1; - - return self; -} - -GskGLIconCache * -gsk_gl_icon_cache_ref (GskGLIconCache *self) -{ - self->ref_count++; - - return self; -} - -void -gsk_gl_icon_cache_unref (GskGLIconCache *self) -{ - g_assert (self->ref_count > 0); - - if (self->ref_count == 1) - { - gsk_gl_texture_atlases_unref (self->atlases); - g_hash_table_unref (self->icons); - g_free (self); - return; - } - - self->ref_count--; -} - -void -gsk_gl_icon_cache_begin_frame (GskGLIconCache *self, - GPtrArray *removed_atlases) -{ - GHashTableIter iter; - GdkTexture *texture; - IconData *icon_data; - - self->timestamp++; - - /* Drop icons on removed atlases */ - if (removed_atlases->len > 0) - { - guint dropped = 0; - - g_hash_table_iter_init (&iter, self->icons); - while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data)) - { - if (g_ptr_array_find (removed_atlases, icon_data->atlas, NULL)) - { - g_hash_table_iter_remove (&iter); - dropped++; - } - } - - GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d icons", dropped)); - } - - if (self->timestamp % MAX_FRAME_AGE == 0) - { - g_hash_table_iter_init (&iter, self->icons); - while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data)) - { - if (!icon_data->accessed) - { - if (icon_data->used) - { - const int width = icon_data->source_texture->width; - const int height = icon_data->source_texture->height; - gsk_gl_texture_atlas_mark_unused (icon_data->atlas, width + 2, height + 2); - icon_data->used = FALSE; - } - } - - icon_data->accessed = FALSE; - } - - GSK_NOTE(GLYPH_CACHE, g_message ("%d icons cached", g_hash_table_size (self->icons))); - } -} - -void -gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self, - GdkTexture *texture, - const IconData **out_icon_data) -{ - IconData *icon_data = g_hash_table_lookup (self->icons, texture); - - if (icon_data) - { - if (!icon_data->used) - { - gsk_gl_texture_atlas_mark_used (icon_data->atlas, texture->width + 2, texture->height + 2); - icon_data->used = TRUE; - } - icon_data->accessed = TRUE; - - *out_icon_data = icon_data; - return; - } - - /* texture not on any atlas yet. Find a suitable one. */ - { - const int width = texture->width; - const int height = texture->height; - GskGLTextureAtlas *atlas = NULL; - int packed_x = 0; - int packed_y = 0; - cairo_surface_t *surface; - unsigned char *surface_data; - unsigned char *pixel_data; - guchar *free_data = NULL; - guint gl_format; - guint gl_type; - - gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y); - - icon_data = g_new0 (IconData, 1); - icon_data->atlas = atlas; - icon_data->accessed = TRUE; - icon_data->used = TRUE; - icon_data->texture_id = atlas->texture_id; - icon_data->source_texture = g_object_ref (texture); - icon_data->x = (float)(packed_x + 1) / atlas->width; - icon_data->y = (float)(packed_y + 1) / atlas->width; - icon_data->x2 = icon_data->x + (float)width / atlas->width; - icon_data->y2 = icon_data->y + (float)height / atlas->height; - - g_hash_table_insert (self->icons, texture, icon_data); - - /* actually upload the texture */ - surface = gdk_texture_download_surface (texture); - surface_data = cairo_image_surface_get_data (surface); - gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (), - "Uploading texture"); - - if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - { - pixel_data = free_data = g_malloc (width * height * 4); - gdk_memory_convert (pixel_data, width * 4, - GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, - surface_data, cairo_image_surface_get_stride (surface), - GDK_MEMORY_DEFAULT, width, height); - gl_format = GL_RGBA; - gl_type = GL_UNSIGNED_BYTE; - } - else - { - pixel_data = surface_data; - gl_format = GL_BGRA; - gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - } - - glBindTexture (GL_TEXTURE_2D, atlas->texture_id); - - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y + 1, - width, height, - gl_format, gl_type, - pixel_data); - /* Padding top */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y, - width, 1, - gl_format, gl_type, - pixel_data); - /* Padding left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y + 1, - 1, height, - gl_format, gl_type, - pixel_data); - /* Padding top left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y, - 1, 1, - gl_format, gl_type, - pixel_data); - - /* Padding right */ - glPixelStorei (GL_UNPACK_ROW_LENGTH, width); - glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + width + 1, packed_y + 1, - 1, height, - gl_format, gl_type, - pixel_data); - /* Padding top right */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + width + 1, packed_y, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom */ - glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y + 1 + height, - width, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y + 1 + height, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom right */ - glPixelStorei (GL_UNPACK_ROW_LENGTH, width); - glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1 + width, packed_y + 1 + height, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Reset this */ - glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); - - gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ()); - - *out_icon_data = icon_data; - - cairo_surface_destroy (surface); - g_free (free_data); - -#if 0 - { - static int k; - const int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width); - guchar *data = g_malloc (atlas->height * stride); - cairo_surface_t *s; - char *filename = g_strdup_printf ("atlas_%u_%d.png", atlas->texture_id, k++); - - glBindTexture (GL_TEXTURE_2D, atlas->texture_id); - glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); - s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride); - cairo_surface_write_to_png (s, filename); - - cairo_surface_destroy (s); - g_free (data); - g_free (filename); - } -#endif - } -} diff --git a/gsk/gl/gskgliconcacheprivate.h b/gsk/gl/gskgliconcacheprivate.h deleted file mode 100644 index e076278a4e..0000000000 --- a/gsk/gl/gskgliconcacheprivate.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef __GSK_GL_ICON_CACHE_PRIVATE_H__ -#define __GSK_GL_ICON_CACHE_PRIVATE_H__ - -#include "gskgldriverprivate.h" -#include "gskglimageprivate.h" -#include "gskrendererprivate.h" -#include "gskgltextureatlasprivate.h" -#include -#include - -typedef struct -{ - int ref_count; - - GdkDisplay *display; - GskGLDriver *gl_driver; - - GskGLTextureAtlases *atlases; - GHashTable *icons; /* GdkTexture -> IconData */ - - int timestamp; -} GskGLIconCache; - -typedef struct -{ - float x, y, x2, y2; - GskGLTextureAtlas *atlas; - guint used : 1; - guint accessed : 1; - int texture_id; - GdkTexture *source_texture; -} IconData; - -GskGLIconCache * gsk_gl_icon_cache_new (GdkDisplay *display, - GskGLTextureAtlases *atlases); -GskGLIconCache * gsk_gl_icon_cache_ref (GskGLIconCache *self); -void gsk_gl_icon_cache_unref (GskGLIconCache *self); -void gsk_gl_icon_cache_begin_frame (GskGLIconCache *self, - GPtrArray *removed_atlases); -void gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self, - GdkTexture *texture, - const IconData **out_icon_data); - -#endif diff --git a/gsk/gl/gskglimage.c b/gsk/gl/gskglimage.c deleted file mode 100644 index 71d7bce2a3..0000000000 --- a/gsk/gl/gskglimage.c +++ /dev/null @@ -1,64 +0,0 @@ - -#include "gskglimageprivate.h" -#include - -void -gsk_gl_image_create (GskGLImage *self, - GskGLDriver *gl_driver, - int width, - int height, - int min_filter, - int mag_filter) -{ - self->texture_id = gsk_gl_driver_create_texture (gl_driver, width, height); - self->width = width; - self->height = height; - - gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id); - gsk_gl_driver_init_texture_empty (gl_driver, self->texture_id, min_filter, mag_filter); - gsk_gl_driver_mark_texture_permanent (gl_driver, self->texture_id); -} - -void -gsk_gl_image_destroy (GskGLImage *self, - GskGLDriver *gl_driver) -{ - gsk_gl_driver_destroy_texture (gl_driver, self->texture_id); - self->texture_id = 0; -} - -void -gsk_gl_image_write_to_png (const GskGLImage *self, - GskGLDriver *gl_driver, - const char *filename) -{ - int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, self->width); - guchar *data = g_malloc (self->height * stride); - cairo_surface_t *s; - - gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id); - glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); - s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, self->width, self->height, stride); - cairo_surface_write_to_png (s, filename); - - cairo_surface_destroy (s); - g_free (data); -} - -void -gsk_gl_image_upload_region (GskGLImage *self, - GskGLDriver *gl_driver, - const GskImageRegion *region) -{ - gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id); - glBindTexture (GL_TEXTURE_2D, self->texture_id); - - glTexSubImage2D (GL_TEXTURE_2D, 0, region->x, region->y, region->width, region->height, - GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, region->data); - -#ifdef G_ENABLE_DEBUG - /*gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);*/ - /*gsk_gl_image_dump (self, gl_driver, "/home/baedert/atlases/test_dump.png");*/ -#endif -} - diff --git a/gsk/gl/gskglimageprivate.h b/gsk/gl/gskglimageprivate.h deleted file mode 100644 index 3d706723b8..0000000000 --- a/gsk/gl/gskglimageprivate.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __GSK_GL_IMAGE_H__ -#define __GSK_GL_IMAGE_H__ - -#include "gskgldriverprivate.h" -#include - -typedef struct -{ - guint texture_id; - int width; - int height; -} GskGLImage; - -typedef struct -{ - guchar *data; - gsize width; - gsize height; - gsize stride; - gsize x; - gsize y; -} GskImageRegion; - -void gsk_gl_image_create (GskGLImage *self, - GskGLDriver *gl_driver, - int width, - int height, - int min_filter, - int mag_filter); -void gsk_gl_image_destroy (GskGLImage *self, - GskGLDriver *gl_driver); -void gsk_gl_image_write_to_png (const GskGLImage *self, - GskGLDriver *gl_driver, - const char *filename); -void gsk_gl_image_upload_region (GskGLImage *self, - GskGLDriver *gl_driver, - const GskImageRegion *region); - - -#endif - diff --git a/gsk/gl/gskglnodesample.c b/gsk/gl/gskglnodesample.c deleted file mode 100644 index cf1afa5a68..0000000000 --- a/gsk/gl/gskglnodesample.c +++ /dev/null @@ -1,51 +0,0 @@ - -#include -#include "gskglnodesampleprivate.h" -#include "gskrendernodeprivate.h" - -void -node_sample_init (NodeSample *self) -{ - memset (self->nodes, 0, sizeof (self->nodes)); - self->count = 0; -} - -void -node_sample_reset (NodeSample *self) -{ - node_sample_init (self); -} - -void -node_sample_add (NodeSample *self, - GskRenderNode *node) -{ - const guint node_type = gsk_render_node_get_node_type (node); - - g_assert (node_type <= N_NODE_TYPES); - - if (self->nodes[node_type].class_name == NULL) - self->nodes[node_type].class_name = g_type_name_from_instance ((GTypeInstance *) node); - - self->nodes[node_type].count ++; - self->count ++; -} - -void -node_sample_print (const NodeSample *self, - const char *prefix) -{ - guint i; - - g_printf ("%s:\n", prefix); - - for (i = 0; i < N_NODE_TYPES; i ++) - { - if (self->nodes[i].count > 0) - { - double p = (double)self->nodes[i].count / (double)self->count; - - g_printf ("%s: %u (%.2f%%)\n", self->nodes[i].class_name, self->nodes[i].count, p * 100.0); - } - } -} diff --git a/gsk/gl/gskglnodesampleprivate.h b/gsk/gl/gskglnodesampleprivate.h deleted file mode 100644 index a1d09a098f..0000000000 --- a/gsk/gl/gskglnodesampleprivate.h +++ /dev/null @@ -1,28 +0,0 @@ - -#ifndef __GSK_GL_NODE_SAMPLE_PRIVATE_H__ -#define __GSK_GL_NODE_SAMPLE_PRIVATE_H__ - -#include -#include "gskenums.h" -#include "gskrendernode.h" - -/* TODO: We have no other way for this...? */ -#define N_NODE_TYPES (GSK_DEBUG_NODE + 1) - -typedef struct -{ - struct { - const char *class_name; - guint count; - } nodes[N_NODE_TYPES]; - guint count; -} NodeSample; - -void node_sample_init (NodeSample *self); -void node_sample_reset (NodeSample *self); -void node_sample_add (NodeSample *self, - GskRenderNode *node); -void node_sample_print (const NodeSample *self, - const char *prefix); - -#endif diff --git a/gsk/gl/gskglprofiler.c b/gsk/gl/gskglprofiler.c deleted file mode 100644 index 9b834e5fc8..0000000000 --- a/gsk/gl/gskglprofiler.c +++ /dev/null @@ -1,181 +0,0 @@ -#include "config.h" - -#include "gskglprofilerprivate.h" - -#include - -#define N_QUERIES 4 - -struct _GskGLProfiler -{ - GObject parent_instance; - - GdkGLContext *gl_context; - - /* Creating GL queries is kind of expensive, so we pay the - * price upfront and create a circular buffer of queries - */ - GLuint gl_queries[N_QUERIES]; - GLuint active_query; - - gboolean has_queries : 1; - gboolean has_timer : 1; - gboolean first_frame : 1; -}; - -enum { - PROP_GL_CONTEXT = 1, - - N_PROPERTIES -}; - -static GParamSpec *gsk_gl_profiler_properties[N_PROPERTIES]; - -G_DEFINE_TYPE (GskGLProfiler, gsk_gl_profiler, G_TYPE_OBJECT) - -static void -gsk_gl_profiler_finalize (GObject *gobject) -{ - GskGLProfiler *self = GSK_GL_PROFILER (gobject); - - if (self->has_queries) - glDeleteQueries (N_QUERIES, self->gl_queries); - - g_clear_object (&self->gl_context); - - G_OBJECT_CLASS (gsk_gl_profiler_parent_class)->finalize (gobject); -} - -static void -gsk_gl_profiler_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GskGLProfiler *self = GSK_GL_PROFILER (gobject); - - switch (prop_id) - { - case PROP_GL_CONTEXT: - self->gl_context = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - } -} - -static void -gsk_gl_profiler_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GskGLProfiler *self = GSK_GL_PROFILER (gobject); - - switch (prop_id) - { - case PROP_GL_CONTEXT: - g_value_set_object (value, self->gl_context); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - } -} - -static void -gsk_gl_profiler_class_init (GskGLProfilerClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = gsk_gl_profiler_set_property; - gobject_class->get_property = gsk_gl_profiler_get_property; - gobject_class->finalize = gsk_gl_profiler_finalize; - - gsk_gl_profiler_properties[PROP_GL_CONTEXT] = - g_param_spec_object ("gl-context", - "GL Context", - "The GdkGLContext used by the GL profiler", - GDK_TYPE_GL_CONTEXT, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, N_PROPERTIES, gsk_gl_profiler_properties); -} - -static void -gsk_gl_profiler_init (GskGLProfiler *self) -{ - self->has_queries = epoxy_is_desktop_gl(); - self->has_timer = epoxy_is_desktop_gl() && (epoxy_gl_version () >= 33 || epoxy_has_gl_extension ("GL_ARB_timer_query")); - - if (!self->has_queries) - return; - - glGenQueries (N_QUERIES, self->gl_queries); - self->first_frame = TRUE; -} - -GskGLProfiler * -gsk_gl_profiler_new (GdkGLContext *context) -{ - g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); - - return g_object_new (GSK_TYPE_GL_PROFILER, "gl-context", context, NULL); -} - -void -gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler) -{ - GLuint query_id; - - g_return_if_fail (GSK_IS_GL_PROFILER (profiler)); - - if (!profiler->has_timer || !profiler->has_queries) - return; - - query_id = profiler->gl_queries[profiler->active_query]; - glBeginQuery (GL_TIME_ELAPSED, query_id); -} - -guint64 -gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler) -{ - GLuint last_query_id; - GLint res; - GLuint64 elapsed; - - g_return_val_if_fail (GSK_IS_GL_PROFILER (profiler), 0); - - if (!profiler->has_timer || !profiler->has_queries) - return 0; - - glEndQuery (GL_TIME_ELAPSED); - - if (profiler->active_query == 0) - last_query_id = N_QUERIES - 1; - else - last_query_id = profiler->active_query - 1; - - /* Advance iterator */ - profiler->active_query += 1; - if (profiler->active_query == N_QUERIES) - profiler->active_query = 0; - - /* If this is the first frame we already have a result */ - if (profiler->first_frame) - { - profiler->first_frame = FALSE; - return 0; - } - - glGetQueryObjectiv (profiler->gl_queries[last_query_id], GL_QUERY_RESULT_AVAILABLE, &res); - if (res == 1) - glGetQueryObjectui64v (profiler->gl_queries[last_query_id], GL_QUERY_RESULT, &elapsed); - else - elapsed = 0; - - return elapsed / 1000; /* Convert to usec to match other profiler APIs */ -} diff --git a/gsk/gl/gskglprofilerprivate.h b/gsk/gl/gskglprofilerprivate.h deleted file mode 100644 index 5b2a24b09f..0000000000 --- a/gsk/gl/gskglprofilerprivate.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __GSK_GL_PROFILER_PRIVATE_H__ -#define __GSK_GL_PROFILER_PRIVATE_H__ - -#include - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_PROFILER (gsk_gl_profiler_get_type ()) -G_DECLARE_FINAL_TYPE (GskGLProfiler, gsk_gl_profiler, GSK, GL_PROFILER, GObject) - -GskGLProfiler * gsk_gl_profiler_new (GdkGLContext *context); - -void gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler); -guint64 gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler); - -G_END_DECLS - -#endif /* __GSK_GL_PROFILER_PRIVATE_H__ */ diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c deleted file mode 100644 index a801f55ab3..0000000000 --- a/gsk/gl/gskglrenderer.c +++ /dev/null @@ -1,4559 +0,0 @@ -#include "config.h" - -#include "gskglrendererprivate.h" - -#include "gskdebugprivate.h" -#include "gskenums.h" -#include "gskgldriverprivate.h" -#include "gskglprofilerprivate.h" -#include "gskprofilerprivate.h" -#include "gskrendererprivate.h" -#include "gskrendernodeprivate.h" -#include "gsktransformprivate.h" -#include "gskglshaderbuilderprivate.h" -#include "gskglglyphcacheprivate.h" -#include "gskgliconcacheprivate.h" -#include "gskglrenderopsprivate.h" -#include "gskcairoblurprivate.h" -#include "gskglshadowcacheprivate.h" -#include "gskglnodesampleprivate.h" -#include "gsktransform.h" -#include "glutilsprivate.h" -#include "gskglshaderprivate.h" - -#include "gskprivate.h" - -#include "gdk/gdkgltextureprivate.h" -#include "gdk/gdkglcontextprivate.h" -#include "gdk/gdkprofilerprivate.h" -#include "gdk/gdkrgbaprivate.h" - -#include - -#define SHADER_VERSION_GLES 100 -#define SHADER_VERSION_GL2_LEGACY 110 -#define SHADER_VERSION_GL3_LEGACY 130 -#define SHADER_VERSION_GL3 150 - -#define ORTHO_NEAR_PLANE -10000 -#define ORTHO_FAR_PLANE 10000 - -#define DEBUG_OPS 0 - -#define SHADOW_EXTRA_SIZE 4 - -#if DEBUG_OPS -#define OP_PRINT(format, ...) g_print(format, ## __VA_ARGS__) -#else -#define OP_PRINT(format, ...) -#endif - -#define INIT_PROGRAM_UNIFORM_LOCATION(program_name, uniform_basename) \ - G_STMT_START{\ - programs->program_name ## _program.program_name.uniform_basename ## _location = \ - glGetUniformLocation(programs->program_name ## _program.id, "u_" #uniform_basename);\ - if (programs->program_name ## _program.program_name.uniform_basename ## _location == -1) \ - { \ - g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED, \ - "Failed to find variable \"u_%s\" in shader program \"%s\"", #uniform_basename, #program_name); \ - g_clear_pointer (&programs, gsk_gl_renderer_programs_unref); \ - goto out; \ - } \ - }G_STMT_END - -#define INIT_COMMON_UNIFORM_LOCATION(program_ptr, uniform_basename) \ - G_STMT_START{\ - program_ptr->uniform_basename ## _location = \ - glGetUniformLocation(program_ptr->id, "u_" #uniform_basename);\ - }G_STMT_END - -static Program *gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self, - GskGLShader *shader); -static Program *gsk_gl_renderer_create_custom_program (GskGLRenderer *self, - GskGLShader *shader); - -typedef enum -{ - FORCE_OFFSCREEN = 1 << 0, - RESET_CLIP = 1 << 1, - DUMP_FRAMEBUFFER = 1 << 3, - NO_CACHE_PLZ = 1 << 5, - LINEAR_FILTER = 1 << 6, -} OffscreenFlags; - -static inline void -init_full_texture_region (TextureRegion *r, - int texture_id) -{ - r->texture_id = texture_id; - r->x = 0; - r->y = 0; - r->x2 = 1; - r->y2 = 1; -} - -static void G_GNUC_UNUSED -print_render_node_tree (GskRenderNode *root, int level) -{ -#define INDENT 4 - const guint type = gsk_render_node_get_node_type (root); - guint i; - - switch (type) - { - case GSK_CONTAINER_NODE: - g_print ("%*s Container\n", level * INDENT, " "); - for (i = 0; i < gsk_container_node_get_n_children (root); i++) - print_render_node_tree (gsk_container_node_get_child (root, i), level + 1); - break; - - case GSK_TRANSFORM_NODE: - g_print ("%*s Transform\n", level * INDENT, " "); - print_render_node_tree (gsk_transform_node_get_child (root), level + 1); - break; - - case GSK_COLOR_MATRIX_NODE: - g_print ("%*s Color Matrix\n", level * INDENT, " "); - print_render_node_tree (gsk_color_matrix_node_get_child (root), level + 1); - break; - - case GSK_CROSS_FADE_NODE: - g_print ("%*s Crossfade(%.2f)\n", level * INDENT, " ", - gsk_cross_fade_node_get_progress (root)); - print_render_node_tree (gsk_cross_fade_node_get_start_child (root), level + 1); - print_render_node_tree (gsk_cross_fade_node_get_end_child (root), level + 1); - break; - - case GSK_TEXT_NODE: - g_print ("%*s Text\n", level * INDENT, " "); - break; - - case GSK_COLOR_NODE: - g_print ("%*s Color %s\n", level * INDENT, " ", gdk_rgba_to_string (gsk_color_node_get_color (root))); - break; - - case GSK_SHADOW_NODE: - g_print ("%*s Shadow\n", level * INDENT, " "); - print_render_node_tree (gsk_shadow_node_get_child (root), level + 1); - break; - - case GSK_GL_SHADER_NODE: - g_print ("%*s GL Shader\n", level * INDENT, " "); - for (i = 0; i < gsk_gl_shader_node_get_n_children (root); i++) - print_render_node_tree (gsk_gl_shader_node_get_child (root, i), level + 1); - break; - - case GSK_TEXTURE_NODE: - g_print ("%*s Texture %p\n", level * INDENT, " ", gsk_texture_node_get_texture (root)); - break; - - case GSK_DEBUG_NODE: - g_print ("%*s Debug: %s\n", level * INDENT, " ", gsk_debug_node_get_message (root)); - print_render_node_tree (gsk_debug_node_get_child (root), level + 1); - break; - - case GSK_CLIP_NODE: - g_print ("%*s Clip (%f, %f, %f, %f):\n", level * INDENT, " ", - root->bounds.origin.x, root->bounds.origin.y, root->bounds.size.width, root->bounds.size.height); - print_render_node_tree (gsk_clip_node_get_child (root), level + 1); - break; - - default: - g_print ("%*s %s\n", level * INDENT, " ", g_type_name_from_instance ((GTypeInstance *) root)); - } - -#undef INDENT -} - - -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_ARGB32, w, h, stride); - cairo_surface_write_to_png (s, filename); - - cairo_surface_destroy (s); - g_free (data); -} - -static void G_GNUC_UNUSED -dump_node (GskRenderNode *node, - const char *filename) -{ - const int surface_width = ceilf (node->bounds.size.width); - const int surface_height = ceilf (node->bounds.size.height); - cairo_surface_t *surface; - cairo_t *cr; - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - surface_width, - surface_height); - - cr = cairo_create (surface); - cairo_save (cr); - cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y); - gsk_render_node_draw (node, cr); - cairo_restore (cr); - cairo_destroy (cr); - - cairo_surface_write_to_png (surface, filename); - cairo_surface_destroy (surface); -} - -static inline bool G_GNUC_PURE -node_is_invisible (const GskRenderNode *node) -{ - return node->bounds.size.width == 0.0f || - node->bounds.size.height == 0.0f || - isnan (node->bounds.size.width) || - isnan (node->bounds.size.height); -} - -static inline bool G_GNUC_PURE -graphene_rect_intersects (const graphene_rect_t *r1, - const graphene_rect_t *r2) -{ - /* Assume both rects are already normalized, as they usually are */ - if (r1->origin.x > (r2->origin.x + r2->size.width) || - (r1->origin.x + r1->size.width) < r2->origin.x) - return false; - - if (r1->origin.y > (r2->origin.y + r2->size.height) || - (r1->origin.y + r1->size.height) < r2->origin.y) - return false; - - return true; -} - -static inline bool G_GNUC_PURE -_graphene_rect_contains_rect (const graphene_rect_t *r1, - const graphene_rect_t *r2) -{ - if (r2->origin.x >= r1->origin.x && - (r2->origin.x + r2->size.width) <= (r1->origin.x + r1->size.width) && - r2->origin.y >= r1->origin.y && - (r2->origin.y + r2->size.height) <= (r1->origin.y + r1->size.height)) - return true; - - return false; -} - -static inline bool G_GNUC_PURE -equal_texture_nodes (GskRenderNode *node1, - GskRenderNode *node2) -{ - if (gsk_render_node_get_node_type (node1) != GSK_TEXTURE_NODE || - gsk_render_node_get_node_type (node2) != GSK_TEXTURE_NODE) - return false; - - if (gsk_texture_node_get_texture (node1) != - gsk_texture_node_get_texture (node2)) - return false; - - return graphene_rect_equal (&node1->bounds, &node2->bounds); -} - -static inline void -sort_border_sides (const GdkRGBA *colors, - int *indices) -{ - gboolean done[4] = {0, 0, 0, 0}; - int i, k; - int cur = 0; - - for (i = 0; i < 3; i ++) - { - if (done[i]) - continue; - - indices[cur] = i; - done[i] = TRUE; - cur ++; - - for (k = i + 1; k < 4; k ++) - { - if (gdk_rgba_equal (&colors[k], &colors[i])) - { - indices[cur] = k; - done[k] = TRUE; - cur ++; - } - } - - if (cur >= 4) - break; - } -} - -static inline void -init_projection_matrix (graphene_matrix_t *out_proj, - const graphene_rect_t *viewport) -{ - graphene_matrix_init_ortho (out_proj, - viewport->origin.x, - viewport->origin.x + viewport->size.width, - viewport->origin.y, - viewport->origin.y + viewport->size.height, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE); - graphene_matrix_scale (out_proj, 1, -1, 1); -} - -static inline gboolean G_GNUC_PURE -color_matrix_modifies_alpha (GskRenderNode *node) -{ - const graphene_matrix_t *matrix = gsk_color_matrix_node_get_color_matrix (node); - const graphene_vec4_t *offset = gsk_color_matrix_node_get_color_offset (node); - graphene_vec4_t row3; - - if (graphene_vec4_get_w (offset) != 0.0f) - return TRUE; - - graphene_matrix_get_row (matrix, 3, &row3); - - return !graphene_vec4_equal (graphene_vec4_w_axis (), &row3); -} - -static inline void -gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self) -{ - self->bounds.size.width = MAX (self->corner[0].width + self->corner[1].width, - self->corner[3].width + self->corner[2].width); - self->bounds.size.height = MAX (self->corner[0].height + self->corner[3].height, - self->corner[1].height + self->corner[2].height); -} - -static inline gboolean G_GNUC_PURE -node_supports_transform (GskRenderNode *node) -{ - /* Some nodes can't handle non-trivial transforms without being - * rendered to a texture (e.g. rotated clips, etc.). Some however - * work just fine, mostly because they already draw their child - * to a texture and just render the texture manipulated in some - * way, think opacity or color matrix. */ - const guint node_type = gsk_render_node_get_node_type (node); - - switch (node_type) - { - case GSK_COLOR_NODE: - case GSK_OPACITY_NODE: - case GSK_COLOR_MATRIX_NODE: - case GSK_TEXTURE_NODE: - case GSK_CROSS_FADE_NODE: - case GSK_LINEAR_GRADIENT_NODE: - case GSK_DEBUG_NODE: - case GSK_TEXT_NODE: - return TRUE; - - case GSK_TRANSFORM_NODE: - return node_supports_transform (gsk_transform_node_get_child (node)); - - default: - return FALSE; - } - return FALSE; -} - -static inline void -load_vertex_data_with_region (GskQuadVertex vertex_data[GL_N_VERTICES], - const graphene_rect_t *bounds, - RenderOpBuilder *builder, - const TextureRegion *r, - gboolean flip_y) -{ - const float min_x = builder->dx + bounds->origin.x; - const float min_y = builder->dy + bounds->origin.y; - const float max_x = min_x + bounds->size.width; - const float max_y = min_y + bounds->size.height; - const float y1 = flip_y ? r->y2 : r->y; - const float y2 = flip_y ? r->y : r->y2; - - vertex_data[0].position[0] = min_x; - vertex_data[0].position[1] = min_y; - vertex_data[0].uv[0] = r->x; - vertex_data[0].uv[1] = y1; - - vertex_data[1].position[0] = min_x; - vertex_data[1].position[1] = max_y; - vertex_data[1].uv[0] = r->x; - vertex_data[1].uv[1] = y2; - - vertex_data[2].position[0] = max_x; - vertex_data[2].position[1] = min_y; - vertex_data[2].uv[0] = r->x2; - vertex_data[2].uv[1] = y1; - - vertex_data[3].position[0] = max_x; - vertex_data[3].position[1] = max_y; - vertex_data[3].uv[0] = r->x2; - vertex_data[3].uv[1] = y2; - - vertex_data[4].position[0] = min_x; - vertex_data[4].position[1] = max_y; - vertex_data[4].uv[0] = r->x; - vertex_data[4].uv[1] = y2; - - vertex_data[5].position[0] = max_x; - vertex_data[5].position[1] = min_y; - vertex_data[5].uv[0] = r->x2; - vertex_data[5].uv[1] = y1; -} - -static inline void -load_float_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES], - RenderOpBuilder *builder, - float x, - float y, - float width, - float height) -{ - const float min_x = builder->dx + x; - const float min_y = builder->dy + y; - const float max_x = min_x + width; - const float max_y = min_y + height; - - vertex_data[0].position[0] = min_x; - vertex_data[0].position[1] = min_y; - vertex_data[0].uv[0] = 0; - vertex_data[0].uv[1] = 0; - - vertex_data[1].position[0] = min_x; - vertex_data[1].position[1] = max_y; - vertex_data[1].uv[0] = 0; - vertex_data[1].uv[1] = 1; - - vertex_data[2].position[0] = max_x; - vertex_data[2].position[1] = min_y; - vertex_data[2].uv[0] = 1; - vertex_data[2].uv[1] = 0; - - vertex_data[3].position[0] = max_x; - vertex_data[3].position[1] = max_y; - vertex_data[3].uv[0] = 1; - vertex_data[3].uv[1] = 1; - - vertex_data[4].position[0] = min_x; - vertex_data[4].position[1] = max_y; - vertex_data[4].uv[0] = 0; - vertex_data[4].uv[1] = 1; - - vertex_data[5].position[0] = max_x; - vertex_data[5].position[1] = min_y; - vertex_data[5].uv[0] = 1; - vertex_data[5].uv[1] = 0; -} - -static void -load_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES], - const graphene_rect_t *bounds, - RenderOpBuilder *builder) -{ - load_float_vertex_data (vertex_data, builder, - bounds->origin.x, bounds->origin.y, - bounds->size.width, bounds->size.height); -} - -static void -fill_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES], - const float min_x, - const float min_y, - const float max_x, - const float max_y) -{ - vertex_data[0].position[0] = min_x; - vertex_data[0].position[1] = min_y; - vertex_data[0].uv[0] = 0; - vertex_data[0].uv[1] = 1; - - vertex_data[1].position[0] = min_x; - vertex_data[1].position[1] = max_y; - vertex_data[1].uv[0] = 0; - vertex_data[1].uv[1] = 0; - - vertex_data[2].position[0] = max_x; - vertex_data[2].position[1] = min_y; - vertex_data[2].uv[0] = 1; - vertex_data[2].uv[1] = 1; - - vertex_data[3].position[0] = max_x; - vertex_data[3].position[1] = max_y; - vertex_data[3].uv[0] = 1; - vertex_data[3].uv[1] = 0; - - vertex_data[4].position[0] = min_x; - vertex_data[4].position[1] = max_y; - vertex_data[4].uv[0] = 0; - vertex_data[4].uv[1] = 0; - - vertex_data[5].position[0] = max_x; - vertex_data[5].position[1] = min_y; - vertex_data[5].uv[0] = 1; - vertex_data[5].uv[1] = 1; -} - -static void -load_offscreen_vertex_data (GskQuadVertex vertex_data[GL_N_VERTICES], - 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; - - fill_vertex_data (vertex_data, - min_x, min_y, - max_x, max_y); -} - - -static void gsk_gl_renderer_setup_render_mode (GskGLRenderer *self); -static gboolean add_offscreen_ops (GskGLRenderer *self, - RenderOpBuilder *builder, - const graphene_rect_t *bounds, - GskRenderNode *child_node, - TextureRegion *region_out, - gboolean *is_offscreen, - guint flags) G_GNUC_WARN_UNUSED_RESULT; -static void gsk_gl_renderer_add_render_ops (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder); - -struct _GskGLRenderer -{ - GskRenderer parent_instance; - - int scale_factor; - - GdkGLContext *gl_context; - GskGLDriver *gl_driver; - GskGLProfiler *gl_profiler; - - GskGLRendererPrograms *programs; - - RenderOpBuilder op_builder; - - GskGLTextureAtlases *atlases; - GskGLGlyphCache *glyph_cache; - GskGLIconCache *icon_cache; - GskGLShadowCache shadow_cache; - -#ifdef G_ENABLE_DEBUG - struct { - GQuark frames; - } profile_counters; - struct { - GQuark cpu_time; - GQuark gpu_time; - } profile_timers; -#endif - - cairo_region_t *render_region; -}; - -struct _GskGLRendererClass -{ - GskRendererClass parent_class; -}; - -G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER) - -static void -init_shader_builder (GskGLRenderer *self, - GskGLShaderBuilder *shader_builder) -{ -#ifdef G_ENABLE_DEBUG - if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS)) - shader_builder->debugging = TRUE; -#endif - - if (gdk_gl_context_get_use_es (self->gl_context)) - { - gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GLES); - shader_builder->gles = TRUE; - } - else if (gdk_gl_context_is_legacy (self->gl_context)) - { - int maj, min; - - gdk_gl_context_get_version (self->gl_context, &maj, &min); - - if (maj == 3) - gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3_LEGACY); - else - gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL2_LEGACY); - - shader_builder->legacy = TRUE; - } - else - { - gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3); - shader_builder->gl3 = TRUE; - } -} - -static GdkRGBA BLACK = {0, 0, 0, 1}; -static void G_GNUC_UNUSED -add_rect_outline_ops (GskGLRenderer *self, - RenderOpBuilder *builder, - const graphene_rect_t *rect) -{ - ops_set_program (builder, &self->programs->color_program); - ops_set_color (builder, &BLACK); - - load_vertex_data (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT (rect->origin.x, rect->origin.y, 1, rect->size.height), - builder); - load_vertex_data (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT (rect->origin.x, rect->origin.y, rect->size.width, 1), - builder); - load_vertex_data (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT (rect->origin.x + rect->size.width - 1,rect->origin.y, - 1, rect->size.height), - builder); - load_vertex_data (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT (rect->origin.x, rect->origin.y + rect->size.height - 1, - rect->size.width, 1), - builder); -} - -static inline GskRoundedRect -transform_rect (GskGLRenderer *self, - RenderOpBuilder *builder, - const GskRoundedRect *rect) -{ - GskRoundedRect r; - - r.bounds.origin.x = builder->dx + rect->bounds.origin.x; - r.bounds.origin.y = builder->dy + rect->bounds.origin.y; - r.bounds.size = rect->bounds.size; - - r.corner[0] = rect->corner[0]; - r.corner[1] = rect->corner[1]; - r.corner[2] = rect->corner[2]; - r.corner[3] = rect->corner[3]; - - return r; -} - -static inline void -render_fallback_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const float scale_x = builder->scale_x; - const float scale_y = builder->scale_y; - const int surface_width = ceilf (node->bounds.size.width * scale_x); - const int surface_height = ceilf (node->bounds.size.height * scale_y); - GdkTexture *texture; - cairo_surface_t *surface; - cairo_surface_t *rendered_surface; - cairo_t *cr; - int cached_id; - int texture_id; - GskTextureKey key; - - if (surface_width <= 0 || - surface_height <= 0) - return; - - key.pointer = node; - key.pointer_is_child = FALSE; - key.scale_x = scale_x; - key.scale_y = scale_y; - key.filter = GL_NEAREST; - - cached_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key); - - if (cached_id != 0) - { - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, cached_id); - load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); - return; - } - - - /* We first draw the recording surface on an image surface, - * just because the scaleY(-1) later otherwise screws up the - * rendering... */ - { - rendered_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - surface_width, - surface_height); - - cairo_surface_set_device_scale (rendered_surface, scale_x, scale_y); - cr = cairo_create (rendered_surface); - - cairo_save (cr); - cairo_translate (cr, - floorf (node->bounds.origin.x), - floorf (node->bounds.origin.y)); - gsk_render_node_draw (node, cr); - cairo_restore (cr); - cairo_destroy (cr); - } - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - surface_width, - surface_height); - cairo_surface_set_device_scale (surface, scale_x, scale_y); - cr = cairo_create (surface); - - /* We draw upside down here, so it matches what GL does. */ - cairo_save (cr); - cairo_scale (cr, 1, -1); - cairo_translate (cr, 0, - surface_height / scale_y); - cairo_set_source_surface (cr, rendered_surface, 0, 0); - cairo_rectangle (cr, 0, 0, surface_width / scale_x, surface_height / scale_y); - cairo_fill (cr); - cairo_restore (cr); - -#ifdef G_ENABLE_DEBUG - if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK)) - { - cairo_move_to (cr, 0, 0); - cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height); - if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE) - cairo_set_source_rgba (cr, 0.3, 0, 1, 0.25); - else - cairo_set_source_rgba (cr, 1, 0, 0, 0.25); - cairo_fill_preserve (cr); - if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE) - cairo_set_source_rgba (cr, 0.3, 0, 1, 1); - else - cairo_set_source_rgba (cr, 1, 0, 0, 1); - cairo_stroke (cr); - } -#endif - cairo_destroy (cr); - - - /* Upload the Cairo surface to a GL texture */ - texture_id = gsk_gl_driver_create_texture (self->gl_driver, - surface_width, - surface_height); - gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id); - - texture = gdk_texture_new_for_surface (surface); - gsk_gl_driver_init_texture (self->gl_driver, - texture_id, - texture, - GL_NEAREST, GL_NEAREST); - - if (gdk_gl_context_has_debug (self->gl_context)) - gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id, - "Fallback %s %d", - g_type_name_from_instance ((GTypeInstance *) node), - texture_id); - - g_object_unref (texture); - cairo_surface_destroy (surface); - cairo_surface_destroy (rendered_surface); - - gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, texture_id); - - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, texture_id); - load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); -} - -static inline void -render_text_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder, - const GdkRGBA *color, - gboolean force_color) -{ - const PangoFont *font = gsk_text_node_get_font (node); - const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL); - const float text_scale = MAX (builder->scale_x, builder->scale_y); /* TODO: Fix for uneven scales? */ - const graphene_point_t *offset = gsk_text_node_get_offset (node); - const guint num_glyphs = gsk_text_node_get_num_glyphs (node); - const float x = offset->x + builder->dx; - const float y = offset->y + builder->dy; - int i; - int x_position = 0; - GlyphCacheKey lookup; - - /* If the font has color glyphs, we don't need to recolor anything */ - if (!force_color && gsk_text_node_has_color_glyphs (node)) - { - ops_set_program (builder, &self->programs->blit_program); - } - else - { - ops_set_program (builder, &self->programs->coloring_program); - ops_set_color (builder, color); - } - - memset (&lookup, 0, sizeof (CacheKeyData)); - lookup.data.font = (PangoFont *)font; - lookup.data.scale = (guint) (text_scale * 1024); - - /* We use one quad per character */ - for (i = 0; i < num_glyphs; i++) - { - const PangoGlyphInfo *gi = &glyphs[i]; - const GskGLCachedGlyph *glyph; - float glyph_x, glyph_y, glyph_x2, glyph_y2; - float tx, ty, tx2, ty2; - float cx; - float cy; - - if (gi->glyph == PANGO_GLYPH_EMPTY) - continue; - - cx = (float)(x_position + gi->geometry.x_offset) / PANGO_SCALE; - cy = (float)(gi->geometry.y_offset) / PANGO_SCALE; - - glyph_cache_key_set_glyph_and_shift (&lookup, gi->glyph, x + cx, y + cy); - - gsk_gl_glyph_cache_lookup_or_add (self->glyph_cache, - &lookup, - self->gl_driver, - &glyph); - - if (glyph->texture_id == 0) - goto next; - - ops_set_texture (builder, glyph->texture_id); - - tx = glyph->tx; - ty = glyph->ty; - tx2 = tx + glyph->tw; - ty2 = ty + glyph->th; - - glyph_x = floor (x + cx + 0.125) + glyph->draw_x; - glyph_y = floor (y + cy + 0.125) + glyph->draw_y; - glyph_x2 = glyph_x + glyph->draw_width; - glyph_y2 = glyph_y + glyph->draw_height; - - ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) { - { { glyph_x, glyph_y }, { tx, ty }, }, - { { glyph_x, glyph_y2 }, { tx, ty2 }, }, - { { glyph_x2, glyph_y }, { tx2, ty }, }, - - { { glyph_x2, glyph_y2 }, { tx2, ty2 }, }, - { { glyph_x, glyph_y2 }, { tx, ty2 }, }, - { { glyph_x2, glyph_y }, { tx2, ty }, }, - }); - -next: - x_position += gi->geometry.width; - } -} - -static inline void -render_border_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const GdkRGBA *colors = gsk_border_node_get_colors (node); - const GskRoundedRect *rounded_outline = gsk_border_node_get_outline (node); - const float *widths = gsk_border_node_get_widths (node); - int i; - struct { - float w; - float h; - } sizes[4]; - - if (gsk_border_node_get_uniform (node)) - { - ops_set_program (builder, &self->programs->inset_shadow_program); - ops_set_inset_shadow (builder, transform_rect (self, builder, rounded_outline), - widths[0], &colors[0], 0, 0); - - load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); - return; - } - - /* Top left */ - if (widths[3] > 0) - sizes[0].w = MAX (widths[3], rounded_outline->corner[0].width); - else - sizes[0].w = 0; - - if (widths[0] > 0) - sizes[0].h = MAX (widths[0], rounded_outline->corner[0].height); - else - sizes[0].h = 0; - - /* Top right */ - if (widths[1] > 0) - sizes[1].w = MAX (widths[1], rounded_outline->corner[1].width); - else - sizes[1].w = 0; - - if (widths[0] > 0) - sizes[1].h = MAX (widths[0], rounded_outline->corner[1].height); - else - sizes[1].h = 0; - - /* Bottom right */ - if (widths[1] > 0) - sizes[2].w = MAX (widths[1], rounded_outline->corner[2].width); - else - sizes[2].w = 0; - - if (widths[2] > 0) - sizes[2].h = MAX (widths[2], rounded_outline->corner[2].height); - else - sizes[2].h = 0; - - - /* Bottom left */ - if (widths[3] > 0) - sizes[3].w = MAX (widths[3], rounded_outline->corner[3].width); - else - sizes[3].w = 0; - - if (widths[2] > 0) - sizes[3].h = MAX (widths[2], rounded_outline->corner[3].height); - else - sizes[3].h = 0; - - { - 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; - const GskQuadVertex side_data[4][6] = { - /* Top */ - { - { { min_x, min_y }, { 0, 1 }, }, /* Upper left */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ - - { { max_x - sizes[1].w, min_y + sizes[1].h }, { 1, 0 }, }, /* Lower right */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ - }, - /* Right */ - { - { { max_x - sizes[1].w, min_y + sizes[1].h }, { 0, 1 }, }, /* Upper left */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ - - { { max_x, max_y }, { 1, 0 }, }, /* Lower right */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ - }, - /* Bottom */ - { - { { min_x + sizes[3].w, max_y - sizes[3].h }, { 0, 1 }, }, /* Upper left */ - { { min_x, max_y }, { 0, 0 }, }, /* Lower left */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */ - - { { max_x, max_y }, { 1, 0 }, }, /* Lower right */ - { { min_x , max_y }, { 0, 0 }, }, /* Lower left */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */ - }, - /* Left */ - { - { { min_x, min_y }, { 0, 1 }, }, /* Upper left */ - { { min_x, max_y }, { 0, 0 }, }, /* Lower left */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */ - - { { min_x + sizes[3].w, max_y - sizes[3].h }, { 1, 0 }, }, /* Lower right */ - { { min_x, max_y }, { 0, 0 }, }, /* Lower left */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */ - } - }; - int indices[4] = { 0, 1, 2, 3 }; - GskRoundedRect outline; - - /* We sort them by color */ - sort_border_sides (colors, indices); - - /* Prepare outline */ - outline = transform_rect (self, builder, rounded_outline); - - ops_set_program (builder, &self->programs->border_program); - ops_set_border_width (builder, widths); - ops_set_border (builder, &outline); - - for (i = 0; i < 4; i ++) - { - if (widths[indices[i]] > 0) - { - ops_set_border_color (builder, &colors[indices[i]]); - ops_draw (builder, side_data[indices[i]]); - } - } - } -} - -static inline void -render_color_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - ops_set_program (builder, &self->programs->color_program); - ops_set_color (builder, gsk_color_node_get_color (node)); - load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); -} - -static inline void -upload_texture (GskGLRenderer *self, - GdkTexture *texture, - TextureRegion *out_region) -{ - if (texture->width <= 128 && - texture->height <= 128 && - !GDK_IS_GL_TEXTURE (texture)) - { - const IconData *icon_data; - - gsk_gl_icon_cache_lookup_or_add (self->icon_cache, - texture, - &icon_data); - - out_region->texture_id = icon_data->texture_id; - out_region->x = icon_data->x; - out_region->y = icon_data->y; - out_region->x2 = icon_data->x2; - out_region->y2 = icon_data->y2; - } - else - { - - out_region->texture_id = - gsk_gl_driver_get_texture_for_texture (self->gl_driver, - texture, - GL_LINEAR, - GL_LINEAR); - - out_region->x = 0; - out_region->y = 0; - out_region->x2 = 1; - out_region->y2 = 1; - } -} - -static inline void -render_texture_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GdkTexture *texture = gsk_texture_node_get_texture (node); - const int max_texture_size = gsk_gl_driver_get_max_texture_size (self->gl_driver); - - if (texture->width > max_texture_size || texture->height > max_texture_size) - { - 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; - const float scale_x = (max_x - min_x) / texture->width; - const float scale_y = (max_y - min_y) / texture->height; - TextureSlice *slices; - guint n_slices; - guint i; - - gsk_gl_driver_slice_texture (self->gl_driver, texture, &slices, &n_slices); - - ops_set_program (builder, &self->programs->blit_program); - for (i = 0; i < n_slices; i ++) - { - const TextureSlice *slice = &slices[i]; - float x1, x2, y1, y2; - - x1 = min_x + (scale_x * slice->rect.x); - x2 = x1 + (slice->rect.width * scale_x); - y1 = min_y + (scale_y * slice->rect.y); - y2 = y1 + (slice->rect.height * scale_y); - - ops_set_texture (builder, slice->texture_id); - ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) { - { { x1, y1 }, { 0, 0 }, }, - { { x1, y2 }, { 0, 1 }, }, - { { x2, y1 }, { 1, 0 }, }, - - { { x2, y2 }, { 1, 1 }, }, - { { x1, y2 }, { 0, 1 }, }, - { { x2, y1 }, { 1, 0 }, }, - }); - } - } - else - { - TextureRegion r; - - upload_texture (self, texture, &r); - - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, r.texture_id); - - load_vertex_data_with_region (ops_draw (builder, NULL), - &node->bounds, builder, - &r, - FALSE); - } -} - -static Program * -compile_glshader (GskGLRenderer *self, - GskGLShader *shader, - GError **error) -{ - GskGLShaderBuilder shader_builder; - const char *shader_source; - gsize shader_source_len; - int n_uniforms; - const GskGLUniform *uniforms; - GBytes *bytes; - int n_required_textures = gsk_gl_shader_get_n_textures (shader); - int program_id; - Program *program; - - bytes = gsk_gl_shader_get_source (shader); - shader_source = g_bytes_get_data (bytes, &shader_source_len); - uniforms = gsk_gl_shader_get_uniforms (shader, &n_uniforms); - - if (n_uniforms > G_N_ELEMENTS (program->glshader.args_locations)) - { - g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT, - "GLShaderNode supports max %d custom uniforms", (int)G_N_ELEMENTS (program->glshader.args_locations)); - return NULL; - } - - if (n_required_textures > G_N_ELEMENTS (program->glshader.texture_locations)) - { - g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT, - "GLShaderNode supports max %d texture sources", (int)(G_N_ELEMENTS (program->glshader.texture_locations))); - return NULL; - } - - gsk_gl_shader_builder_init (&shader_builder, - "/org/gtk/libgsk/gl/preamble.glsl", - "/org/gtk/libgsk/gl/preamble.vs.glsl", - "/org/gtk/libgsk/gl/preamble.fs.glsl"); - - init_shader_builder (self, &shader_builder); - program_id = gsk_gl_shader_builder_create_program (&shader_builder, - "/org/gtk/libgsk/gl/custom.glsl", - shader_source, shader_source_len, - error); - gsk_gl_shader_builder_finish (&shader_builder); - - if (program_id < 0) - return NULL; - - program = gsk_gl_renderer_create_custom_program (self, shader); - - program->id = program_id; - INIT_COMMON_UNIFORM_LOCATION (program, alpha); - INIT_COMMON_UNIFORM_LOCATION (program, clip_rect); - INIT_COMMON_UNIFORM_LOCATION (program, viewport); - INIT_COMMON_UNIFORM_LOCATION (program, projection); - INIT_COMMON_UNIFORM_LOCATION (program, modelview); - program->glshader.size_location = glGetUniformLocation(program->id, "u_size"); - program->glshader.texture_locations[0] = glGetUniformLocation(program->id, "u_texture1"); - program->glshader.texture_locations[1] = glGetUniformLocation(program->id, "u_texture2"); - program->glshader.texture_locations[2] = glGetUniformLocation(program->id, "u_texture3"); - program->glshader.texture_locations[3] = glGetUniformLocation(program->id, "u_texture4"); - - /* We use u_texture1 for the texture 0 in the glshaders, so alias it here so we can use the regular setters */ - program->source_location = program->glshader.texture_locations[0]; - - for (int i = 0; i < G_N_ELEMENTS (program->glshader.args_locations); i++) - { - if (i < n_uniforms) - { - program->glshader.args_locations[i] = glGetUniformLocation(program->id, uniforms[i].name); - /* This isn't necessary a hard error, you might declare uniforms that are not actually - always used, for instance if you have an "API" in uniforms for multiple shaders. */ - if (program->glshader.args_locations[i] == -1) - g_debug ("Declared uniform `%s` not found in GskGLShader", uniforms[i].name); - } - else - program->glshader.args_locations[i] = -1; - } - - return program; -} - -gboolean -gsk_gl_renderer_try_compile_gl_shader (GskGLRenderer *self, - GskGLShader *shader, - GError **error) -{ - Program *program; - - gdk_gl_context_make_current (self->gl_context); - - /* Maybe we tried to compile it already? */ - program = gsk_gl_renderer_lookup_custom_program (self, shader); - if (program != NULL) - { - if (program->id > 0) - return TRUE; - else - { - g_propagate_error (error, g_error_copy (program->glshader.compile_error)); - return FALSE; - } - } - - program = compile_glshader (self, shader, error); - return program != NULL; -} - -static inline void -render_gl_shader_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GskGLShader *shader = gsk_gl_shader_node_get_shader (node); - Program *program = gsk_gl_renderer_lookup_custom_program (self, shader); - int n_children = gsk_gl_shader_node_get_n_children (node); - - if (program == NULL) - { - GError *error = NULL; - - program = compile_glshader (self, shader, &error); - if (program == NULL) - { - /* We create the program anyway (in a failed state), so that any compiler warnings or other are only reported once */ - program = gsk_gl_renderer_create_custom_program (self, shader); - program->id = -1; - program->glshader.compile_error = error; - - g_warning ("Failed to compile gl shader: %s", error->message); - } - } - - if (program->id >= 0 && n_children <= G_N_ELEMENTS (program->glshader.texture_locations)) - { - GBytes *args; - TextureRegion regions[4]; - gboolean is_offscreen[4]; - for (guint i = 0; i < n_children; i++) - { - GskRenderNode *child = gsk_gl_shader_node_get_child (node, i); - if (!add_offscreen_ops (self, builder, - &node->bounds, - child, - ®ions[i], &is_offscreen[i], - FORCE_OFFSCREEN | RESET_CLIP)) - return; - } - - args = gsk_gl_shader_node_get_args (node); - ops_set_program (builder, program); - - ops_set_gl_shader_args (builder, shader, node->bounds.size.width, node->bounds.size.height, g_bytes_get_data (args, NULL)); - for (guint i = 0; i < n_children; i++) - { - if (i == 0) - ops_set_texture (builder, regions[i].texture_id); - else - ops_set_extra_texture (builder, regions[i].texture_id, i); - } - - load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); - } - else - { - static GdkRGBA pink = { 255 / 255., 105 / 255., 180 / 255., 1.0 }; - ops_set_program (builder, &self->programs->color_program); - ops_set_color (builder, &pink); - load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); - } -} - -/* Returns TRUE if applying @transform to @bounds - * yields an axis-aligned rectangle - */ -static gboolean -result_is_axis_aligned (GskTransform *transform, - const graphene_rect_t *bounds) -{ - graphene_matrix_t m; - graphene_quad_t q; - graphene_rect_t b; - graphene_point_t b1, b2; - const graphene_point_t *p; - int i; - - gsk_transform_to_matrix (transform, &m); - gsk_matrix_transform_rect (&m, bounds, &q); - graphene_quad_bounds (&q, &b); - graphene_rect_get_top_left (&b, &b1); - graphene_rect_get_bottom_right (&b, &b2); - - for (i = 0; i < 4; i++) - { - p = graphene_quad_get_point (&q, i); - if (fabs (p->x - b1.x) > FLT_EPSILON && fabs (p->x - b2.x) > FLT_EPSILON) - return FALSE; - if (fabs (p->y - b1.y) > FLT_EPSILON && fabs (p->y - b2.y) > FLT_EPSILON) - return FALSE; - } - - return TRUE; -} - -static inline void -render_transform_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GskTransform *node_transform = gsk_transform_node_get_transform (node); - const GskTransformCategory category = gsk_transform_get_category (node_transform); - GskRenderNode *child = gsk_transform_node_get_child (node); - - switch (category) - { - case GSK_TRANSFORM_CATEGORY_IDENTITY: - gsk_gl_renderer_add_render_ops (self, child, builder); - break; - - case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE: - { - float dx, dy; - - gsk_transform_to_translate (node_transform, &dx, &dy); - - ops_offset (builder, dx, dy); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_offset (builder, -dx, -dy); - } - break; - - case GSK_TRANSFORM_CATEGORY_2D_AFFINE: - { - ops_push_modelview (builder, node_transform); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_pop_modelview (builder); - } - break; - - case GSK_TRANSFORM_CATEGORY_2D: - case GSK_TRANSFORM_CATEGORY_3D: - case GSK_TRANSFORM_CATEGORY_ANY: - case GSK_TRANSFORM_CATEGORY_UNKNOWN: - { - TextureRegion region; - gboolean is_offscreen; - - if (node_supports_transform (child)) - { - ops_push_modelview (builder, node_transform); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_pop_modelview (builder); - } - else - { - int filter_flag = 0; - - if (!result_is_axis_aligned (node_transform, &child->bounds)) - filter_flag = LINEAR_FILTER; - - if (add_offscreen_ops (self, builder, - &child->bounds, - child, - ®ion, &is_offscreen, - RESET_CLIP | filter_flag)) - { - /* For non-trivial transforms, we draw everything on a texture and then - * draw the texture transformed. */ - /* TODO: We should compute a modelview containing only the "non-trivial" - * part (e.g. the rotation) and use that. We want to keep the scale - * for the texture. - */ - ops_push_modelview (builder, node_transform); - ops_set_texture (builder, region.texture_id); - ops_set_program (builder, &self->programs->blit_program); - - load_vertex_data_with_region (ops_draw (builder, NULL), - &child->bounds, builder, - ®ion, - is_offscreen); - ops_pop_modelview (builder); - } - } - } - break; - - default: - g_assert_not_reached (); - break; - } -} - -static inline void -render_opacity_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GskRenderNode *child = gsk_opacity_node_get_child (node); - const float opacity = gsk_opacity_node_get_opacity (node); - float prev_opacity; - - if (gsk_render_node_get_node_type (child) == GSK_CONTAINER_NODE) - { - gboolean is_offscreen; - TextureRegion region; - - /* The semantics of an opacity node mandate that when, e.g., two color nodes overlap, - * there may not be any blending between them */ - if (!add_offscreen_ops (self, builder, &child->bounds, - child, - ®ion, &is_offscreen, - FORCE_OFFSCREEN | RESET_CLIP)) - return; - - prev_opacity = ops_set_opacity (builder, - builder->current_opacity * opacity); - - if (builder->current_opacity >= ((float) 0x00ff / (float) 0xffff)) - { - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, region.texture_id); - - load_vertex_data_with_region (ops_draw (builder, NULL), - &node->bounds, builder, - ®ion, - is_offscreen); - } - } - else - { - prev_opacity = ops_set_opacity (builder, - builder->current_opacity * opacity); - - if (builder->current_opacity >= ((float) 0x00ff / (float) 0xffff)) - gsk_gl_renderer_add_render_ops (self, child, builder); - } - - ops_set_opacity (builder, prev_opacity); -} - -static inline void -render_linear_gradient_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const int n_color_stops = gsk_linear_gradient_node_get_n_color_stops (node); - - if (n_color_stops < GL_MAX_GRADIENT_STOPS) - { - 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); - - ops_set_program (builder, &self->programs->linear_gradient_program); - ops_set_linear_gradient (builder, - n_color_stops, - stops, - gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE, - builder->dx + start->x, - builder->dy + start->y, - builder->dx + end->x, - builder->dy + end->y); - - load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); - } - else - { - render_fallback_node (self, node, builder); - } -} - -static inline void -render_radial_gradient_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const int n_color_stops = gsk_radial_gradient_node_get_n_color_stops (node); - - if (n_color_stops < GL_MAX_GRADIENT_STOPS) - { - const GskColorStop *stops = gsk_radial_gradient_node_get_color_stops (node, NULL); - const graphene_point_t *center = gsk_radial_gradient_node_get_center (node); - const float start = gsk_radial_gradient_node_get_start (node); - const float end = gsk_radial_gradient_node_get_end (node); - const float hradius = gsk_radial_gradient_node_get_hradius (node); - const float vradius = gsk_radial_gradient_node_get_vradius (node); - - ops_set_program (builder, &self->programs->radial_gradient_program); - ops_set_radial_gradient (builder, - n_color_stops, - stops, - gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE, - builder->dx + center->x, - builder->dy + center->y, - start, end, - hradius * builder->scale_x, - vradius * builder->scale_y); - - load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); - } - else - { - render_fallback_node (self, node, builder); - } -} - -static inline void -render_conic_gradient_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const int n_color_stops = gsk_conic_gradient_node_get_n_color_stops (node); - - if (n_color_stops < GL_MAX_GRADIENT_STOPS) - { - const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL); - const graphene_point_t *center = gsk_conic_gradient_node_get_center (node); - const float angle = gsk_conic_gradient_node_get_angle (node); - - ops_set_program (builder, &self->programs->conic_gradient_program); - ops_set_conic_gradient (builder, - n_color_stops, - stops, - builder->dx + center->x, - builder->dy + center->y, - angle); - - load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); - } - else - { - render_fallback_node (self, node, builder); - } -} - -static inline gboolean -rounded_inner_rect_contains_rect (const GskRoundedRect *rounded, - const graphene_rect_t *rect) -{ - const graphene_rect_t *rounded_bounds = &rounded->bounds; - graphene_rect_t inner; - float offset_x, offset_y; - - /* TODO: This is pretty conservative and we could to further, more - * fine-grained checks to avoid offscreen drawing. */ - - offset_x = MAX (rounded->corner[GSK_CORNER_TOP_LEFT].width, - rounded->corner[GSK_CORNER_BOTTOM_LEFT].width); - offset_y = MAX (rounded->corner[GSK_CORNER_TOP_LEFT].height, - rounded->corner[GSK_CORNER_TOP_RIGHT].height); - - - inner.origin.x = rounded_bounds->origin.x + offset_x; - inner.origin.y = rounded_bounds->origin.y + offset_y; - inner.size.width = rounded_bounds->size.width - offset_x - - MAX (rounded->corner[GSK_CORNER_TOP_RIGHT].width, - rounded->corner[GSK_CORNER_BOTTOM_RIGHT].width); - inner.size.height = rounded_bounds->size.height - offset_y - - MAX (rounded->corner[GSK_CORNER_BOTTOM_LEFT].height, - rounded->corner[GSK_CORNER_BOTTOM_RIGHT].height); - - return _graphene_rect_contains_rect (&inner, rect); -} - -/* Current clip is NOT rounded but new one is definitely! */ -static inline bool -intersect_rounded_rectilinear (const graphene_rect_t *non_rounded, - const GskRoundedRect *rounded, - GskRoundedRect *result) -{ - bool corners[4]; - - /* Intersects with top left corner? */ - corners[0] = rounded_rect_has_corner (rounded, 0) && - graphene_rect_intersects (non_rounded, - &rounded_rect_corner (rounded, 0)); - /* top right? */ - corners[1] = rounded_rect_has_corner (rounded, 1) && - graphene_rect_intersects (non_rounded, - &rounded_rect_corner (rounded, 1)); - /* bottom right? */ - corners[2] = rounded_rect_has_corner (rounded, 2) && - graphene_rect_intersects (non_rounded, - &rounded_rect_corner (rounded, 2)); - /* bottom left */ - corners[3] = rounded_rect_has_corner (rounded, 3) && - graphene_rect_intersects (non_rounded, - &rounded_rect_corner (rounded, 3)); - - if (corners[0] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 0))) - return false; - if (corners[1] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 1))) - return false; - if (corners[2] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 2))) - return false; - if (corners[3] && !_graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 3))) - return false; - - /* We do intersect with at least one of the corners, but in such a way that the - * intersection between the two clips can still be represented by a single rounded - * rect in a trivial way. do that. */ - graphene_rect_intersection (non_rounded, &rounded->bounds, &result->bounds); - - for (int i = 0; i < 4; i++) - { - if (corners[i]) - result->corner[i] = rounded->corner[i]; - else - result->corner[i].width = result->corner[i].height = 0; - } - - return true; -} - -/* This code intersects the current (maybe rounded) clip with the new - * non-rounded clip */ -static inline void -render_clipped_child (GskGLRenderer *self, - RenderOpBuilder *builder, - const graphene_rect_t *clip, - GskRenderNode *child) -{ - graphene_rect_t transformed_clip; - GskRoundedRect intersection; - - ops_transform_bounds_modelview (builder, clip, &transformed_clip); - - if (builder->clip_is_rectilinear) - { - memset (&intersection, 0, sizeof (GskRoundedRect)); - graphene_rect_intersection (&transformed_clip, - &builder->current_clip->bounds, - &intersection.bounds); - - ops_push_clip (builder, &intersection); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_pop_clip (builder); - } - else if (intersect_rounded_rectilinear (&transformed_clip, - builder->current_clip, - &intersection)) - { - ops_push_clip (builder, &intersection); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_pop_clip (builder); - } - else - { - /* well fuck */ - const float scale_x = builder->scale_x; - const float scale_y = builder->scale_y; - const GskRoundedRect scaled_clip = GSK_ROUNDED_RECT_INIT ((builder->dx + clip->origin.x) * scale_x, - (builder->dy + clip->origin.y) * scale_y, - clip->size.width * scale_x, - clip->size.height * scale_y); - gboolean is_offscreen; - TextureRegion region; - - ops_push_clip (builder, &scaled_clip); - if (!add_offscreen_ops (self, builder, &child->bounds, - child, - ®ion, &is_offscreen, - FORCE_OFFSCREEN)) - g_assert_not_reached (); - ops_pop_clip (builder); - - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, region.texture_id); - - load_offscreen_vertex_data (ops_draw (builder, NULL), child, builder); - } -} - -static inline void -render_clip_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const graphene_rect_t *clip = gsk_clip_node_get_clip (node); - GskRenderNode *child = gsk_clip_node_get_child (node); - - render_clipped_child (self, builder, clip, child); -} - -static inline void -render_rounded_clip_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const float scale_x = builder->scale_x; - const float scale_y = builder->scale_y; - const GskRoundedRect *clip = gsk_rounded_clip_node_get_clip (node); - GskRenderNode *child = gsk_rounded_clip_node_get_child (node); - GskRoundedRect transformed_clip; - gboolean need_offscreen; - int i; - - if (node_is_invisible (child)) - return; - - ops_transform_bounds_modelview (builder, &clip->bounds, &transformed_clip.bounds); - for (i = 0; i < 4; i ++) - { - transformed_clip.corner[i].width = clip->corner[i].width * scale_x; - transformed_clip.corner[i].height = clip->corner[i].height * scale_y; - } - - if (builder->clip_is_rectilinear) - { - GskRoundedRect intersected_clip; - - if (intersect_rounded_rectilinear (&builder->current_clip->bounds, - &transformed_clip, - &intersected_clip)) - { - ops_push_clip (builder, &intersected_clip); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_pop_clip (builder); - return; - } - } - - /* After this point we are really working with a new and a current clip - * which both have rounded corners. */ - - if (!ops_has_clip (builder)) - need_offscreen = FALSE; - else if (rounded_inner_rect_contains_rect (builder->current_clip, - &transformed_clip.bounds)) - need_offscreen = FALSE; - else - need_offscreen = TRUE; - - if (!need_offscreen) - { - /* If they don't intersect at all, we can simply set - * the new clip and add the render ops */ - - /* If the new clip entirely contains the current clip, the intersection is simply - * the current clip, so we can ignore the new one */ - if (rounded_inner_rect_contains_rect (&transformed_clip, &builder->current_clip->bounds)) - { - gsk_gl_renderer_add_render_ops (self, child, builder); - return; - } - - ops_push_clip (builder, &transformed_clip); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_pop_clip (builder); - } - else - { - gboolean is_offscreen; - TextureRegion region; - - ops_push_clip (builder, &transformed_clip); - if (!add_offscreen_ops (self, builder, &node->bounds, - child, - ®ion, &is_offscreen, - FORCE_OFFSCREEN)) - g_assert_not_reached (); - ops_pop_clip (builder); - - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, region.texture_id); - - load_vertex_data_with_region (ops_draw (builder, NULL), &node->bounds, builder, - ®ion, is_offscreen); - } -} - -static inline void -render_color_matrix_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GskRenderNode *child = gsk_color_matrix_node_get_child (node); - TextureRegion region; - gboolean is_offscreen; - - if (node_is_invisible (child)) - return; - - if (!add_offscreen_ops (self, builder, - &node->bounds, - child, - ®ion, &is_offscreen, - RESET_CLIP)) - g_assert_not_reached (); - - ops_set_program (builder, &self->programs->color_matrix_program); - ops_set_color_matrix (builder, - gsk_color_matrix_node_get_color_matrix (node), - gsk_color_matrix_node_get_color_offset (node)); - - ops_set_texture (builder, region.texture_id); - - load_vertex_data_with_region (ops_draw (builder, NULL), - &node->bounds, builder, - ®ion, - is_offscreen); -} - -static inline int -blur_texture (GskGLRenderer *self, - RenderOpBuilder *builder, - const TextureRegion *region, - const int texture_to_blur_width, - const int texture_to_blur_height, - float blur_radius_x, - float blur_radius_y) -{ - const GskRoundedRect new_clip = GSK_ROUNDED_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height); - int pass1_texture_id, pass1_render_target; - int pass2_texture_id, pass2_render_target; - int prev_render_target; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - graphene_matrix_t item_proj; - OpBlur *op; - - g_assert (blur_radius_x > 0); - g_assert (blur_radius_y > 0); - - gsk_gl_driver_create_render_target (self->gl_driver, - MAX (texture_to_blur_width, 1), MAX (texture_to_blur_height, 1), - GL_NEAREST, GL_NEAREST, - &pass1_texture_id, &pass1_render_target); - - if (texture_to_blur_width <= 0 || texture_to_blur_height <= 0) - { - return pass1_texture_id; - } - - gsk_gl_driver_create_render_target (self->gl_driver, - texture_to_blur_width, texture_to_blur_height, - GL_NEAREST, GL_NEAREST, - &pass2_texture_id, &pass2_render_target); - - init_projection_matrix (&item_proj, &new_clip.bounds); - - ops_set_program (builder, &self->programs->blur_program); - prev_projection = ops_set_projection (builder, &item_proj); - ops_set_modelview (builder, NULL); - prev_viewport = ops_set_viewport (builder, &new_clip.bounds); - ops_push_clip (builder, &new_clip); - - prev_render_target = ops_set_render_target (builder, pass1_render_target); - ops_begin (builder, OP_CLEAR); - - op = ops_begin (builder, OP_CHANGE_BLUR); - op->size.width = texture_to_blur_width; - op->size.height = texture_to_blur_height; - op->radius = blur_radius_x; - op->dir[0] = 1; - op->dir[1] = 0; - ops_set_texture (builder, region->texture_id); - - load_vertex_data_with_region (ops_draw (builder, NULL), - &new_clip.bounds, - builder, region, - FALSE); -#if 0 - { - static int k; - ops_dump_framebuffer (builder, - g_strdup_printf ("pass1_%d.png", k++), - texture_to_blur_width, - texture_to_blur_height); - } -#endif - op = ops_begin (builder, OP_CHANGE_BLUR); - op->size.width = texture_to_blur_width; - op->size.height = texture_to_blur_height; - op->radius = blur_radius_y; - op->dir[0] = 0; - op->dir[1] = 1; - ops_set_texture (builder, pass1_texture_id); - ops_set_render_target (builder, pass2_render_target); - ops_begin (builder, OP_CLEAR); - load_vertex_data_with_region (ops_draw (builder, NULL), /* render pass 2 */ - &new_clip.bounds, - builder, region, - FALSE); - -#if 0 - { - static int k; - ops_dump_framebuffer (builder, - g_strdup_printf ("blurred%d.png", k++), - texture_to_blur_width, - texture_to_blur_height); - } -#endif - - ops_set_render_target (builder, prev_render_target); - ops_set_viewport (builder, &prev_viewport); - ops_set_projection (builder, &prev_projection); - ops_pop_modelview (builder); - ops_pop_clip (builder); - - return pass2_texture_id; -} - -static inline void -blur_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder, - float blur_radius, - guint extra_flags, - TextureRegion *out_region, - float *out_vertex_data[4]) /* min, max, min, max */ -{ - const float scale_x = builder->scale_x; - const float scale_y = builder->scale_y; - const float blur_extra = blur_radius * 2.0; /* 2.0 = shader radius_multiplier */ - float texture_width, texture_height; - gboolean is_offscreen; - TextureRegion region; - int blurred_texture_id; - - g_assert (blur_radius > 0); - - /* Increase texture size for the given blur radius and scale it */ - texture_width = ceilf ((node->bounds.size.width + blur_extra)); - texture_height = ceilf ((node->bounds.size.height + blur_extra)); - - /* Only blur this if the out region has no texture id yet */ - if (out_region->texture_id == 0) - { - if (!add_offscreen_ops (self, builder, - &GRAPHENE_RECT_INIT (node->bounds.origin.x - (blur_extra / 2.0), - node->bounds.origin.y - (blur_extra / 2.0), - texture_width, texture_height), - node, - ®ion, &is_offscreen, - RESET_CLIP | FORCE_OFFSCREEN | extra_flags)) - g_assert_not_reached (); - - blurred_texture_id = blur_texture (self, builder, - ®ion, - texture_width * scale_x, texture_height * scale_y, - blur_radius * scale_x, - blur_radius * scale_y); - init_full_texture_region (out_region, blurred_texture_id); - } - - if (out_vertex_data) - { - *out_vertex_data[0] = builder->dx + node->bounds.origin.x - (blur_extra / 2.0); - *out_vertex_data[1] = builder->dx + node->bounds.origin.x + node->bounds.size.width + (blur_extra / 2.0); - *out_vertex_data[2] = builder->dy + node->bounds.origin.y - (blur_extra / 2.0); - *out_vertex_data[3] = builder->dy + node->bounds.origin.y + node->bounds.size.height + (blur_extra / 2.0); - } -} - -static inline void -render_blur_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const float blur_radius = gsk_blur_node_get_radius (node); - GskRenderNode *child = gsk_blur_node_get_child (node); - TextureRegion blurred_region; - GskTextureKey key; - float min_x, max_x, min_y, max_y; - - if (node_is_invisible (child)) - return; - - if (blur_radius <= 0) - { - gsk_gl_renderer_add_render_ops (self, child, builder); - return; - } - - key.pointer = node; - key.pointer_is_child = FALSE; - key.scale_x = builder->scale_x; - key.scale_y = builder->scale_y; - key.filter = GL_NEAREST; - blurred_region.texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key); - blur_node (self, child, builder, blur_radius, 0, &blurred_region, - (float*[4]){&min_x, &max_x, &min_y, &max_y}); - - g_assert (blurred_region.texture_id != 0); - - /* Draw the result */ - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, blurred_region.texture_id); - fill_vertex_data (ops_draw (builder, NULL), min_x, min_y, max_x, max_y); - - - /* Add to cache for the blur node */ - gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, blurred_region.texture_id); -} - -static inline void -render_unblurred_inset_shadow_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const float dx = gsk_inset_shadow_node_get_dx (node); - const float dy = gsk_inset_shadow_node_get_dy (node); - const float spread = gsk_inset_shadow_node_get_spread (node); - - g_assert (gsk_inset_shadow_node_get_blur_radius (node) == 0); - - ops_set_program (builder, &self->programs->inset_shadow_program); - ops_set_inset_shadow (builder, transform_rect (self, builder, gsk_inset_shadow_node_get_outline (node)), - spread, - gsk_inset_shadow_node_get_color (node), - dx, dy); - - load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); -} - -static inline void -render_inset_shadow_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const float scale_x = builder->scale_x; - const float scale_y = builder->scale_y; - const float blur_radius = gsk_inset_shadow_node_get_blur_radius (node); - const float blur_extra = blur_radius * 2.0; /* 2.0 = shader radius_multiplier */ - const float dx = gsk_inset_shadow_node_get_dx (node); - const float dy = gsk_inset_shadow_node_get_dy (node); - const GskRoundedRect *node_outline = gsk_inset_shadow_node_get_outline (node); - float texture_width; - float texture_height; - int blurred_texture_id; - GskTextureKey key; - - g_assert (blur_radius > 0); - - texture_width = ceilf ((node_outline->bounds.size.width + blur_extra) * scale_x); - texture_height = ceilf ((node_outline->bounds.size.height + blur_extra) * scale_y); - - key.pointer = node; - key.pointer_is_child = FALSE; - key.scale_x = scale_x; - key.scale_y = scale_y; - key.filter = GL_NEAREST; - blurred_texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key); - if (blurred_texture_id == 0) - { - const float spread = gsk_inset_shadow_node_get_spread (node) + (blur_extra / 2.0); - GskRoundedRect outline_to_blur; - int render_target, texture_id; - int prev_render_target; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - graphene_matrix_t item_proj; - int i; - - /* TODO: In the following code, we have to be careful about where we apply the scale. - * We're manually scaling stuff (e.g. the outline) so we can later use texture_width - * and texture_height (which are already scaled) as the geometry and keep the modelview - * at a scale of 1. That's kinda complicated though... */ - - /* Outline of what we actually want to blur later. - * Spread grows inside, so we don't need to account for that. But the blur will need - * to read outside of the inset shadow, so we need to draw some color in there. */ - outline_to_blur = *node_outline; - gsk_rounded_rect_shrink (&outline_to_blur, - - blur_extra / 2.0, - blur_extra / 2.0, - - blur_extra / 2.0, - blur_extra / 2.0); - - /* Fit to our texture */ - outline_to_blur.bounds.origin.x = 0; - outline_to_blur.bounds.origin.y = 0; - outline_to_blur.bounds.size.width *= scale_x; - outline_to_blur.bounds.size.height *= scale_y; - - for (i = 0; i < 4; i ++) - { - outline_to_blur.corner[i].width *= scale_x; - outline_to_blur.corner[i].height *= scale_y; - } - - gsk_gl_driver_create_render_target (self->gl_driver, - texture_width, texture_height, - GL_NEAREST, GL_NEAREST, - &texture_id, &render_target); - - init_projection_matrix (&item_proj, - &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height)); - - prev_projection = ops_set_projection (builder, &item_proj); - ops_set_modelview (builder, NULL); - prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height)); - ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT (0, 0, texture_width, texture_height)); - - prev_render_target = ops_set_render_target (builder, render_target); - ops_begin (builder, OP_CLEAR); - - /* Actual inset shadow outline drawing */ - ops_set_program (builder, &self->programs->inset_shadow_program); - ops_set_inset_shadow (builder, transform_rect (self, builder, &outline_to_blur), - spread * MAX (scale_x, scale_y), - gsk_inset_shadow_node_get_color (node), - dx * scale_x, dy * scale_y); - - load_float_vertex_data (ops_draw (builder, NULL), builder, - 0, 0, texture_width, texture_height); - - ops_set_render_target (builder, prev_render_target); - ops_set_viewport (builder, &prev_viewport); - ops_set_projection (builder, &prev_projection); - ops_pop_modelview (builder); - ops_pop_clip (builder); - - blurred_texture_id = blur_texture (self, builder, - &(TextureRegion) { texture_id, 0, 0, 1, 1 }, - texture_width, - texture_height, - blur_radius * scale_x, - blur_radius * scale_y); - } - - g_assert (blurred_texture_id != 0); - - /* Blur the rendered unblurred inset shadow */ - /* Use a clip to cut away the unwanted parts outside of the original outline */ - { - const gboolean needs_clip = !gsk_rounded_rect_is_rectilinear (node_outline); - const float tx1 = blur_extra / 2.0 * scale_x / texture_width; - const float tx2 = 1.0 - tx1; - const float ty1 = blur_extra / 2.0 * scale_y / texture_height; - const float ty2 = 1.0 - ty1; - - gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, blurred_texture_id); - - if (needs_clip) - { - GskRoundedRect node_clip; - - ops_transform_bounds_modelview (builder, &node_outline->bounds, &node_clip.bounds); - for (int i = 0; i < 4; i ++) - { - node_clip.corner[i].width = node_outline->corner[i].width * scale_x; - node_clip.corner[i].height = node_outline->corner[i].height * scale_y; - } - - ops_push_clip (builder, &node_clip); - } - - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, blurred_texture_id); - - load_vertex_data_with_region (ops_draw (builder, NULL), - &node->bounds, builder, - &(TextureRegion) { 0, tx1, ty1, tx2, ty2 }, - TRUE); - - if (needs_clip) - ops_pop_clip (builder); - } - -} - -/* Spread *grows* the outline. The offset moves the shadow and leaves the - * inner rect where it was */ -static inline void -render_unblurred_outset_shadow_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node); - const float x = node->bounds.origin.x; - const float y = node->bounds.origin.y; - const float w = node->bounds.size.width; - const float h = node->bounds.size.height; - const float spread = gsk_outset_shadow_node_get_spread (node); - const float dx = gsk_outset_shadow_node_get_dx (node); - const float dy = gsk_outset_shadow_node_get_dy (node); - const float edge_sizes[] = { // Top, right, bottom, left - spread - dy, spread + dx, spread + dy, spread - dx - }; - const float corner_sizes[][2] = { // top left, top right, bottom right, bottom left - { outline->corner[0].width + spread - dx, outline->corner[0].height + spread - dy }, - { outline->corner[1].width + spread + dx, outline->corner[1].height + spread - dy }, - { outline->corner[2].width + spread + dx, outline->corner[2].height + spread + dy }, - { outline->corner[3].width + spread - dx, outline->corner[3].height + spread + dy }, - }; - - ops_set_program (builder, &self->programs->unblurred_outset_shadow_program); - ops_set_unblurred_outset_shadow (builder, transform_rect (self, builder, outline), - spread, - gsk_outset_shadow_node_get_color (node), - dx, dy); - - /* Corners... */ - if (corner_sizes[0][0] > 0 && corner_sizes[0][1] > 0) /* Top left */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x, y, - corner_sizes[0][0], corner_sizes[0][1]); - if (corner_sizes[1][0] > 0 && corner_sizes[1][1] > 0) /* Top right */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x + w - corner_sizes[1][0], y, - corner_sizes[1][0], corner_sizes[1][1]); - if (corner_sizes[2][0] > 0 && corner_sizes[2][1] > 0) /* Bottom right */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x + w - corner_sizes[2][0], y + h - corner_sizes[2][1], - corner_sizes[2][0], corner_sizes[2][1]); - if (corner_sizes[3][0] > 0 && corner_sizes[3][1] > 0) /* Bottom left */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x, y + h - corner_sizes[3][1], - corner_sizes[3][0], corner_sizes[3][1]); - /* Edges... */; - if (edge_sizes[0] > 0) /* Top */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x + corner_sizes[0][0], y, - w - corner_sizes[0][0] - corner_sizes[1][0], edge_sizes[0]); - if (edge_sizes[1] > 0) /* Right */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x + w - edge_sizes[1], y + corner_sizes[1][1], - edge_sizes[1], h - corner_sizes[1][1] - corner_sizes[2][1]); - if (edge_sizes[2] > 0) /* Bottom */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x + corner_sizes[3][0], y + h - edge_sizes[2], - w - corner_sizes[3][0] - corner_sizes[2][0], edge_sizes[2]); - if (edge_sizes[3] > 0) /* Left */ - load_float_vertex_data (ops_draw (builder, NULL), builder, - x, y + corner_sizes[0][1], - edge_sizes[3], h - corner_sizes[0][1] - corner_sizes[3][1]); -} - - -static GdkRGBA COLOR_WHITE = { 1, 1, 1, 1 }; -static inline void -render_outset_shadow_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const float scale_x = builder->scale_x; - const float scale_y = builder->scale_y; - const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node); - const GdkRGBA *color = gsk_outset_shadow_node_get_color (node); - const float blur_radius = gsk_outset_shadow_node_get_blur_radius (node); - const float blur_extra = blur_radius * 2.0f; /* 2.0 = shader radius_multiplier */ - const int extra_blur_pixels = (int) ceilf(blur_extra / 2.0 * MAX (scale_x, scale_y)); /* TODO: No need to MAX() her actually */ - const float spread = gsk_outset_shadow_node_get_spread (node); - const float dx = gsk_outset_shadow_node_get_dx (node); - const float dy = gsk_outset_shadow_node_get_dy (node); - GskRoundedRect scaled_outline; - int texture_width, texture_height; - OpOutsetShadow *shadow; - int blurred_texture_id; - int cached_tid; - bool do_slicing; - - /* scaled_outline is the minimal outline we need to draw the given drop shadow, - * enlarged by the spread and offset by the blur radius. */ - scaled_outline = *outline; - - if (outline->bounds.size.width < blur_extra || - outline->bounds.size.height < blur_extra) - { - do_slicing = false; - gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread); - } - else - { - /* Shrink our outline to the minimum size that can still hold all the border radii */ - gsk_rounded_rect_shrink_to_minimum (&scaled_outline); - /* Increase by the spread */ - gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread); - /* Grow bounds but don't grow corners */ - graphene_rect_inset (&scaled_outline.bounds, - blur_extra / 2.0, - blur_extra / 2.0); - /* For the center part, we add a few pixels */ - scaled_outline.bounds.size.width += SHADOW_EXTRA_SIZE; - scaled_outline.bounds.size.height += SHADOW_EXTRA_SIZE; - - do_slicing = true; - } - - texture_width = (int)ceil ((scaled_outline.bounds.size.width + blur_extra) * scale_x); - texture_height = (int)ceil ((scaled_outline.bounds.size.height + blur_extra) * scale_y); - - scaled_outline.bounds.origin.x = extra_blur_pixels; - scaled_outline.bounds.origin.y = extra_blur_pixels; - scaled_outline.bounds.size.width = texture_width - (extra_blur_pixels * 2); - scaled_outline.bounds.size.height = texture_height - (extra_blur_pixels * 2); - - for (int i = 0; i < 4; i ++) - { - scaled_outline.corner[i].width *= scale_x; - scaled_outline.corner[i].height *= scale_y; - } - - cached_tid = gsk_gl_shadow_cache_get_texture_id (&self->shadow_cache, - self->gl_driver, - &scaled_outline, - blur_radius); - - if (cached_tid == 0) - { - int texture_id, render_target; - int prev_render_target; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - graphene_matrix_t item_proj; - - gsk_gl_driver_create_render_target (self->gl_driver, - texture_width, texture_height, - GL_NEAREST, GL_NEAREST, - &texture_id, &render_target); - if (gdk_gl_context_has_debug (self->gl_context)) - { - gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id, - "Outset Shadow Temp %d", texture_id); - gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, render_target, - "Outset Shadow FB Temp %d", render_target); - } - - ops_set_program (builder, &self->programs->color_program); - init_projection_matrix (&item_proj, - &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height)); - - prev_render_target = ops_set_render_target (builder, render_target); - ops_begin (builder, OP_CLEAR); - prev_projection = ops_set_projection (builder, &item_proj); - ops_set_modelview (builder, NULL); - prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height)); - - /* Draw outline */ - ops_push_clip (builder, &scaled_outline); - ops_set_color (builder, &COLOR_WHITE); - load_float_vertex_data (ops_draw (builder, NULL), builder, - 0, 0, texture_width, texture_height); - - ops_pop_clip (builder); - ops_set_viewport (builder, &prev_viewport); - ops_pop_modelview (builder); - ops_set_projection (builder, &prev_projection); - ops_set_render_target (builder, prev_render_target); - - /* Now blur the outline */ - blurred_texture_id = blur_texture (self, builder, - &(TextureRegion) { texture_id, 0, 0, 1, 1 }, - texture_width, - texture_height, - blur_radius * scale_x, - blur_radius * scale_y); - - gsk_gl_driver_mark_texture_permanent (self->gl_driver, blurred_texture_id); - gsk_gl_shadow_cache_commit (&self->shadow_cache, - &scaled_outline, - blur_radius, - blurred_texture_id); - } - else - { - blurred_texture_id = cached_tid; - } - - - if (!do_slicing) - { - const float min_x = floorf (outline->bounds.origin.x - spread - (blur_extra / 2.0) + dx); - const float min_y = floorf (outline->bounds.origin.y - spread - (blur_extra / 2.0) + dy); - - ops_set_program (builder, &self->programs->outset_shadow_program); - ops_set_color (builder, color); - ops_set_texture (builder, blurred_texture_id); - - shadow = ops_begin (builder, OP_CHANGE_OUTSET_SHADOW); - shadow->outline.value = transform_rect (self, builder, outline); - shadow->outline.send = TRUE; - - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - min_x, min_y, - texture_width / scale_x, texture_height / scale_y - ), builder, - &(TextureRegion) { 0, 0, 0, 1, 1 }, - FALSE); - return; - } - - - ops_set_program (builder, &self->programs->outset_shadow_program); - ops_set_color (builder, color); - ops_set_texture (builder, blurred_texture_id); - - shadow = ops_begin (builder, OP_CHANGE_OUTSET_SHADOW); - shadow->outline.value = transform_rect (self, builder, outline); - shadow->outline.send = TRUE; - - { - const float min_x = floorf (outline->bounds.origin.x - spread - (blur_extra / 2.0) + dx); - const float min_y = floorf (outline->bounds.origin.y - spread - (blur_extra / 2.0) + dy); - const float max_x = ceilf (outline->bounds.origin.x + outline->bounds.size.width + - (blur_extra / 2.0) + dx + spread); - const float max_y = ceilf (outline->bounds.origin.y + outline->bounds.size.height + - (blur_extra / 2.0) + dy + spread); - cairo_rectangle_int_t slices[9]; - TextureRegion tregs[9]; - - /* TODO: The slicing never changes and could just go into the cache */ - nine_slice_rounded_rect (&scaled_outline, slices); - nine_slice_grow (slices, extra_blur_pixels); - nine_slice_to_texture_coords (slices, texture_width, texture_height, tregs); - - /* Our texture coordinates MUST be scaled, while the actual vertex coords - * MUST NOT be scaled. */ - - /* Top left */ - if (slice_is_visible (&slices[NINE_SLICE_TOP_LEFT])) - { - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - min_x, min_y, - slices[NINE_SLICE_TOP_LEFT].width / scale_x, - slices[NINE_SLICE_TOP_LEFT].height / scale_y - ), - builder, - &tregs[NINE_SLICE_TOP_LEFT], TRUE); - } - - /* Top center */ - if (slice_is_visible (&slices[NINE_SLICE_TOP_CENTER])) - { - const float width = (max_x - min_x) - (slices[NINE_SLICE_TOP_LEFT].width / scale_x + - slices[NINE_SLICE_TOP_RIGHT].width / scale_x); - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - min_x + (slices[NINE_SLICE_TOP_LEFT].width / scale_x), - min_y, - width, - slices[NINE_SLICE_TOP_CENTER].height / scale_y - ), - builder, - &tregs[NINE_SLICE_TOP_CENTER], TRUE); - } - /* Top right */ - if (slice_is_visible (&slices[NINE_SLICE_TOP_RIGHT])) - { - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - max_x - (slices[NINE_SLICE_TOP_RIGHT].width / scale_x), - min_y, - slices[NINE_SLICE_TOP_RIGHT].width / scale_x, - slices[NINE_SLICE_TOP_RIGHT].height / scale_y - ), - builder, - &tregs[NINE_SLICE_TOP_RIGHT], TRUE); - } - - /* Bottom right */ - if (slice_is_visible (&slices[NINE_SLICE_BOTTOM_RIGHT])) - { - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - max_x - (slices[NINE_SLICE_BOTTOM_RIGHT].width / scale_x), - max_y - (slices[NINE_SLICE_BOTTOM_RIGHT].height / scale_y), - slices[NINE_SLICE_BOTTOM_RIGHT].width / scale_x, - slices[NINE_SLICE_BOTTOM_RIGHT].height / scale_y - ), - builder, - &tregs[NINE_SLICE_BOTTOM_RIGHT], TRUE); - } - - /* Bottom left */ - if (slice_is_visible (&slices[NINE_SLICE_BOTTOM_LEFT])) - { - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - min_x, - max_y - (slices[NINE_SLICE_BOTTOM_LEFT].height / scale_y), - slices[NINE_SLICE_BOTTOM_LEFT].width / scale_x, - slices[NINE_SLICE_BOTTOM_LEFT].height / scale_y - ), - builder, - &tregs[NINE_SLICE_BOTTOM_LEFT], TRUE); - } - - /* Left side */ - if (slice_is_visible (&slices[NINE_SLICE_LEFT_CENTER])) - { - const float height = (max_y - min_y) - (slices[NINE_SLICE_TOP_LEFT].height / scale_y + - slices[NINE_SLICE_BOTTOM_LEFT].height / scale_y); - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - min_x, - min_y + (slices[NINE_SLICE_TOP_LEFT].height / scale_y), - slices[NINE_SLICE_LEFT_CENTER].width / scale_x, - height - ), - builder, - &tregs[NINE_SLICE_LEFT_CENTER], TRUE); - } - - /* Right side */ - if (slice_is_visible (&slices[NINE_SLICE_RIGHT_CENTER])) - { - const float height = (max_y - min_y) - (slices[NINE_SLICE_TOP_RIGHT].height / scale_y + - slices[NINE_SLICE_BOTTOM_RIGHT].height / scale_y); - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - max_x - (slices[NINE_SLICE_RIGHT_CENTER].width / scale_x), - min_y + (slices[NINE_SLICE_TOP_LEFT].height / scale_y), - slices[NINE_SLICE_RIGHT_CENTER].width / scale_x, - height - ), - builder, - &tregs[NINE_SLICE_RIGHT_CENTER], TRUE); - } - - /* Bottom side */ - if (slice_is_visible (&slices[NINE_SLICE_BOTTOM_CENTER])) - { - const float width = (max_x - min_x) - (slices[NINE_SLICE_BOTTOM_LEFT].width / scale_x + - slices[NINE_SLICE_BOTTOM_RIGHT].width / scale_x); - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - min_x + (slices[NINE_SLICE_BOTTOM_LEFT].width / scale_x), - max_y - (slices[NINE_SLICE_BOTTOM_CENTER].height / scale_y), - width, - slices[NINE_SLICE_BOTTOM_CENTER].height / scale_y - ), - builder, - &tregs[NINE_SLICE_BOTTOM_CENTER], TRUE); - } - - /* Middle */ - if (slice_is_visible (&slices[NINE_SLICE_CENTER])) - { - const float width = (max_x - min_x) - (slices[NINE_SLICE_LEFT_CENTER].width / scale_x + - slices[NINE_SLICE_RIGHT_CENTER].width / scale_x); - const float height = (max_y - min_y) - (slices[NINE_SLICE_TOP_CENTER].height / scale_y + - slices[NINE_SLICE_BOTTOM_CENTER].height / scale_y); - - load_vertex_data_with_region (ops_draw (builder, NULL), - &GRAPHENE_RECT_INIT ( - min_x + (slices[NINE_SLICE_LEFT_CENTER].width / scale_x), - min_y + (slices[NINE_SLICE_TOP_CENTER].height / scale_y), - width, height - ), - builder, - &tregs[NINE_SLICE_CENTER], TRUE); - } - } -} - -static inline void -render_shadow_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - const gsize n_shadows = gsk_shadow_node_get_n_shadows (node); - GskRenderNode *original_child = gsk_shadow_node_get_child (node); - GskRenderNode *shadow_child = original_child; - guint i; - - /* Shadow nodes recolor every pixel of the source texture, but leave the alpha in tact. - * If the child is a color matrix node that doesn't touch the alpha, we can throw that away. */ - if (gsk_render_node_get_node_type (shadow_child) == GSK_COLOR_MATRIX_NODE && - !color_matrix_modifies_alpha (shadow_child)) - { - shadow_child = gsk_color_matrix_node_get_child (shadow_child); - } - - for (i = 0; i < n_shadows; i ++) - { - const GskShadow *shadow = gsk_shadow_node_get_shadow (node, i); - const float dx = shadow->dx; - const float dy = shadow->dy; - TextureRegion region; - gboolean is_offscreen; - graphene_rect_t bounds; - - if (shadow->radius == 0 && - gsk_render_node_get_node_type (shadow_child) == GSK_TEXT_NODE) - { - ops_offset (builder, dx, dy); - render_text_node (self, shadow_child, builder, &shadow->color, TRUE); - ops_offset (builder, - dx, - dy); - continue; - } - - if (gdk_rgba_is_clear (&shadow->color)) - continue; - - if (node_is_invisible (shadow_child)) - continue; - - if (shadow->radius > 0) - { - float min_x; - float min_y; - float max_x; - float max_y; - - region.texture_id = 0; - blur_node (self, shadow_child, builder, shadow->radius, NO_CACHE_PLZ, ®ion, - (float*[4]){&min_x, &max_x, &min_y, &max_y}); - bounds.origin.x = min_x - builder->dx; - bounds.origin.y = min_y - builder->dy; - bounds.size.width = max_x - min_x; - bounds.size.height = max_y - min_y; - is_offscreen = TRUE; - } - else if (dx == 0 && dy == 0) - { - continue; /* Invisible anyway */ - } - else - { - if (!add_offscreen_ops (self, builder, - &shadow_child->bounds, - shadow_child, ®ion, &is_offscreen, - RESET_CLIP | NO_CACHE_PLZ)) - g_assert_not_reached (); - - bounds = shadow_child->bounds; - } - - ops_set_program (builder, &self->programs->coloring_program); - ops_set_color (builder, &shadow->color); - ops_set_texture (builder, region.texture_id); - - ops_offset (builder, dx, dy); - load_vertex_data_with_region (ops_draw (builder, NULL), - &bounds, builder, - ®ion, - is_offscreen); - ops_offset (builder, -dx, -dy); - } - - /* Now draw the child normally */ - gsk_gl_renderer_add_render_ops (self, original_child, builder); -} - -static inline void -render_cross_fade_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GskRenderNode *start_node = gsk_cross_fade_node_get_start_child (node); - GskRenderNode *end_node = gsk_cross_fade_node_get_end_child (node); - const float progress = gsk_cross_fade_node_get_progress (node); - TextureRegion start_region; - TextureRegion end_region; - gboolean is_offscreen1, is_offscreen2; - OpCrossFade *op; - - if (progress <= 0) - { - gsk_gl_renderer_add_render_ops (self, start_node, builder); - return; - } - else if (progress >= 1) - { - gsk_gl_renderer_add_render_ops (self, end_node, builder); - return; - } - - if (equal_texture_nodes (start_node, end_node)) - { - gsk_gl_renderer_add_render_ops (self, end_node, builder); - return; - } - - /* TODO: We create 2 textures here as big as the cross-fade node, but both the - * start and the end node might be a lot smaller than that. */ - - if (!add_offscreen_ops (self, builder, - &node->bounds, - start_node, - &start_region, &is_offscreen1, - FORCE_OFFSCREEN | RESET_CLIP)) - { - gsk_gl_renderer_add_render_ops (self, end_node, builder); - return; - } - - if (!add_offscreen_ops (self, builder, - &node->bounds, - end_node, - &end_region, &is_offscreen2, - FORCE_OFFSCREEN | RESET_CLIP)) - { - const float prev_opacity = ops_set_opacity (builder, builder->current_opacity * progress); - gsk_gl_renderer_add_render_ops (self, start_node, builder); - ops_set_opacity (builder, prev_opacity); - - return; - } - - ops_set_program (builder, &self->programs->cross_fade_program); - - op = ops_begin (builder, OP_CHANGE_CROSS_FADE); - op->progress = progress; - op->source2 = end_region.texture_id; - - ops_set_texture (builder, start_region.texture_id); - - load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); -} - -static inline void -render_blend_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GskRenderNode *top_child = gsk_blend_node_get_top_child (node); - GskRenderNode *bottom_child = gsk_blend_node_get_bottom_child (node); - TextureRegion top_region; - TextureRegion bottom_region; - gboolean is_offscreen1, is_offscreen2; - OpBlend *op; - - /* TODO: We create 2 textures here as big as the blend node, but both the - * start and the end node might be a lot smaller than that. */ - if (!add_offscreen_ops (self, builder, - &node->bounds, - bottom_child, - &bottom_region, &is_offscreen1, - FORCE_OFFSCREEN | RESET_CLIP)) - { - gsk_gl_renderer_add_render_ops (self, top_child, builder); - return; - } - - if (!add_offscreen_ops (self, builder, - &node->bounds, - top_child, - &top_region, &is_offscreen2, - FORCE_OFFSCREEN | RESET_CLIP)) - { - load_vertex_data_with_region (ops_draw (builder, NULL), - &node->bounds, - builder, - &bottom_region, - TRUE); - return; - } - - ops_set_program (builder, &self->programs->blend_program); - ops_set_texture (builder, bottom_region.texture_id); - - op = ops_begin (builder, OP_CHANGE_BLEND); - op->source2 = top_region.texture_id; - op->mode = gsk_blend_node_get_blend_mode (node); - - load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); -} - -static inline void -render_repeat_node (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - GskRenderNode *child = gsk_repeat_node_get_child (node); - const graphene_rect_t *child_bounds = gsk_repeat_node_get_child_bounds (node); - TextureRegion region; - gboolean is_offscreen; - OpRepeat *op; - - if (node_is_invisible (child)) - return; - - if (!graphene_rect_equal (child_bounds, &child->bounds)) - { - /* TODO: Implement these repeat nodes. */ - render_fallback_node (self, node, builder); - return; - } - - /* If the size of the repeat node is smaller than the size of the - * child node, we don't repeat at all and can just draw that part - * of the child texture... */ - if (graphene_rect_contains_rect (child_bounds, &node->bounds)) - { - render_clipped_child (self, builder, &node->bounds, child); - return; - } - - /* Draw the entire child on a texture */ - if (!add_offscreen_ops (self, builder, - &child->bounds, - child, - ®ion, &is_offscreen, - RESET_CLIP)) - g_assert_not_reached (); - - ops_set_program (builder, &self->programs->repeat_program); - ops_set_texture (builder, region.texture_id); - - op = ops_begin (builder, OP_CHANGE_REPEAT); - op->child_bounds[0] = (node->bounds.origin.x - child_bounds->origin.x) / child_bounds->size.width; - op->child_bounds[1] = (node->bounds.origin.y - child_bounds->origin.y) / child_bounds->size.height; - op->child_bounds[2] = node->bounds.size.width / child_bounds->size.width; - op->child_bounds[3] = node->bounds.size.height / child_bounds->size.height; - - op->texture_rect[0] = region.x; - op->texture_rect[2] = region.x2; - - if (is_offscreen) - { - op->texture_rect[1] = region.y2; - op->texture_rect[3] = region.y; - } - else - { - op->texture_rect[1] = region.y; - op->texture_rect[3] = region.y2; - } - - load_vertex_data_with_region (ops_draw (builder, NULL), - &node->bounds, builder, - ®ion, - is_offscreen); -} - -static inline void -apply_viewport_op (const Program *program, - const OpViewport *op) -{ - OP_PRINT (" -> New Viewport: %f, %f, %f, %f", - op->viewport.origin.x, op->viewport.origin.y, - op->viewport.size.width, op->viewport.size.height); - glUniform4f (program->viewport_location, - op->viewport.origin.x, op->viewport.origin.y, - op->viewport.size.width, op->viewport.size.height); - glViewport (0, 0, op->viewport.size.width, op->viewport.size.height); -} - -static inline void -apply_modelview_op (const Program *program, - const OpMatrix *op) -{ - float mat[16]; - - graphene_matrix_to_float (&op->matrix, mat); - OP_PRINT (" -> Modelview { { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }", - mat[0], mat[1], mat[2], mat[3], - mat[4], mat[5], mat[6], mat[7], - mat[8], mat[9], mat[10], mat[11], - mat[12], mat[13], mat[14], mat[15]); - glUniformMatrix4fv (program->modelview_location, 1, GL_FALSE, mat); -} - -static inline void -apply_projection_op (const Program *program, - const OpMatrix *op) -{ - float mat[16]; - - graphene_matrix_to_float (&op->matrix, mat); - OP_PRINT (" -> Projection { { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }", - mat[0], mat[1], mat[2], mat[3], - mat[4], mat[5], mat[6], mat[7], - mat[8], mat[9], mat[10], mat[11], - mat[12], mat[13], mat[14], mat[15]); - glUniformMatrix4fv (program->projection_location, 1, GL_FALSE, mat); -} - -static inline void -apply_program_op (const Program *program, - const OpProgram *op) -{ - OP_PRINT (" -> Program: %d", op->program->index); - glUseProgram (op->program->id); -} - -static inline void -apply_render_target_op (GskGLRenderer *self, - const OpRenderTarget *op) -{ - OP_PRINT (" -> Render Target: %d", op->render_target_id); - - glBindFramebuffer (GL_FRAMEBUFFER, op->render_target_id); - - if (op->render_target_id != 0) - glDisable (GL_SCISSOR_TEST); - else - gsk_gl_renderer_setup_render_mode (self); /* Reset glScissor etc. */ -} - -static inline void -apply_color_op (const Program *program, - const OpColor *op) -{ - OP_PRINT (" -> Color: (%f, %f, %f, %f)", - op->rgba->red, op->rgba->green, op->rgba->blue, op->rgba->alpha); - glUniform4fv (program->color.color_location, 1, (float *)op->rgba); -} - -static inline void -apply_opacity_op (const Program *program, - const OpOpacity *op) -{ - OP_PRINT (" -> Opacity %f", op->opacity); - glUniform1f (program->alpha_location, op->opacity); -} - -static inline void -apply_source_texture_op (const Program *program, - const OpTexture *op) -{ - g_assert(op->texture_id != 0); - OP_PRINT (" -> New texture: %d", op->texture_id); - /* Use texture unit 0 for the source */ - glUniform1i (program->source_location, 0); - glActiveTexture (GL_TEXTURE0); - glBindTexture (GL_TEXTURE_2D, op->texture_id); -} - -static inline void -apply_source_extra_texture_op (const Program *program, - const OpExtraTexture *op) -{ - g_assert(op->texture_id != 0); - OP_PRINT (" -> New extra texture %d: %d", op->idx, op->texture_id); - glUniform1i (program->glshader.texture_locations[op->idx], op->idx); - glActiveTexture (GL_TEXTURE0 + op->idx); - glBindTexture (GL_TEXTURE_2D, op->texture_id); -} - -static inline void -apply_color_matrix_op (const Program *program, - const OpColorMatrix *op) -{ - OP_PRINT (" -> Color matrix. Send matrix: %d. Send offset: %d.", - op->matrix.send, op->offset.send); - - if (op->matrix.send) - { - float mat[16]; - graphene_matrix_to_float (op->matrix.value, mat); - glUniformMatrix4fv (program->color_matrix.color_matrix_location, 1, GL_FALSE, mat); - } - - if (op->offset.send) - { - float vec[4]; - graphene_vec4_to_float (op->offset.value, vec); - glUniform4fv (program->color_matrix.color_offset_location, 1, vec); - } -} - -static inline void -apply_clip_op (const Program *program, - const OpClip *op) -{ - int count; - - if (op->send_corners) - { - OP_PRINT (" -> Clip: %s", gsk_rounded_rect_to_string (&op->clip)); - count = 3; - } - else - { - OP_PRINT (" -> clip: %f, %f, %f, %f", - op->clip.bounds.origin.x, op->clip.bounds.origin.y, - op->clip.bounds.size.width, op->clip.bounds.size.height); - count = 1; - } - - glUniform4fv (program->clip_rect_location, count, (float *)&op->clip.bounds); -} - -static inline void -apply_inset_shadow_op (const Program *program, - const OpShadow *op) -{ - OP_PRINT (" -> inset shadow. Color: %s, Offset: (%f, %f), Spread: %f, Outline: %s", - op->color.send ? gdk_rgba_to_string (op->color.value) : "don't send", - op->offset.send ? op->offset.value[0] : -1337.0, - op->offset.send ? op->offset.value[1] : -1337.0, - op->spread.send ? op->spread.value : -1337.0, - op->outline.send ? gsk_rounded_rect_to_string (&op->outline.value) : "don't send"); - if (op->outline.send) - { - if (op->outline.send_corners) - glUniform4fv (program->inset_shadow.outline_rect_location, 3, (float *)&op->outline.value); - else - glUniform4fv (program->inset_shadow.outline_rect_location, 1, (float *)&op->outline.value); - } - - if (op->color.send) - glUniform4fv (program->inset_shadow.color_location, 1, (float *)op->color.value); - - if (op->spread.send) - glUniform1f (program->inset_shadow.spread_location, op->spread.value); - - if (op->offset.send) - glUniform2fv (program->inset_shadow.offset_location, 1, op->offset.value); -} - -static inline void -apply_unblurred_outset_shadow_op (const Program *program, - const OpShadow *op) -{ - OP_PRINT (" -> unblurred outset shadow"); - - if (op->outline.send) - { - if (op->outline.send_corners) - glUniform4fv (program->unblurred_outset_shadow.outline_rect_location, 3, (float *)&op->outline.value); - else - glUniform4fv (program->unblurred_outset_shadow.outline_rect_location, 1, (float *)&op->outline.value); - } - - if (op->color.send) - glUniform4fv (program->unblurred_outset_shadow.color_location, 1, (float *)op->color.value); - - if (op->spread.send) - glUniform1f (program->unblurred_outset_shadow.spread_location, op->spread.value); - - if (op->offset.send) - glUniform2fv (program->unblurred_outset_shadow.offset_location, 1, op->offset.value); -} - -static inline void -apply_outset_shadow_op (const Program *program, - const OpOutsetShadow *op) -{ - OP_PRINT (" -> outset shadow"); - glUniform4fv (program->outset_shadow.outline_rect_location, 3, (float *)&op->outline.value.bounds); -} - -static inline void -apply_linear_gradient_op (const Program *program, - const OpLinearGradient *op) -{ - OP_PRINT (" -> Linear gradient"); - if (op->n_color_stops.send) - glUniform1i (program->linear_gradient.num_color_stops_location, op->n_color_stops.value); - - if (op->color_stops.send) - glUniform1fv (program->linear_gradient.color_stops_location, - op->n_color_stops.value * 5, - (float *)op->color_stops.value); - - glUniform4f (program->linear_gradient.points_location, - op->start_point[0], op->start_point[1], - op->end_point[0] - op->start_point[0], op->end_point[1] - op->start_point[1]); - glUniform1i (program->linear_gradient.repeat_location, op->repeat); -} - -static inline void -apply_radial_gradient_op (const Program *program, - const OpRadialGradient *op) -{ - float scale; - float bias; - - OP_PRINT (" -> Radial gradient"); - if (op->n_color_stops.send) - glUniform1i (program->radial_gradient.num_color_stops_location, op->n_color_stops.value); - - if (op->color_stops.send) - glUniform1fv (program->radial_gradient.color_stops_location, - op->n_color_stops.value * 5, - (float *)op->color_stops.value); - - scale = 1.0f / (op->end - op->start); - bias = -op->start * scale; - - glUniform1i (program->radial_gradient.repeat_location, op->repeat); - glUniform2f (program->radial_gradient.range_location, scale, bias); - glUniform4f (program->radial_gradient.geometry_location, - op->center[0], op->center[1], - 1.0f / op->radius[0], 1.0f / op->radius[1]); -} - -static inline void -apply_conic_gradient_op (const Program *program, - const OpConicGradient *op) -{ - float bias; - float scale; - - OP_PRINT (" -> Conic gradient"); - if (op->n_color_stops.send) - glUniform1i (program->conic_gradient.num_color_stops_location, op->n_color_stops.value); - - if (op->color_stops.send) - glUniform1fv (program->conic_gradient.color_stops_location, - op->n_color_stops.value * 5, - (float *)op->color_stops.value); - - scale = 0.5f * M_1_PI; - bias = op->angle * scale + 2.0f; - glUniform4f (program->conic_gradient.geometry_location, op->center[0], op->center[1], scale, bias); -} - -static inline void -apply_border_op (const Program *program, - const OpBorder *op) -{ - OP_PRINT (" -> Border Outline"); - - glUniform4fv (program->border.outline_rect_location, 3, (float *)&op->outline.bounds); -} - -static inline void -apply_gl_shader_args_op (const Program *program, - const OpGLShader *op) -{ - int n_uniforms, i; - const GskGLUniform *uniforms; - - OP_PRINT (" -> GL Shader Args"); - - glUniform2fv (program->glshader.size_location, 1, op->size); - - uniforms = gsk_gl_shader_get_uniforms (op->shader, &n_uniforms); - for (i = 0; i < n_uniforms; i++) - { - const GskGLUniform *u = &uniforms[i]; - const guchar *data = op->uniform_data + u->offset; - - switch (u->type) - { - default: - case GSK_GL_UNIFORM_TYPE_NONE: - break; - case GSK_GL_UNIFORM_TYPE_FLOAT: - glUniform1fv (program->glshader.args_locations[i], 1, (const float *)data); - break; - case GSK_GL_UNIFORM_TYPE_INT: - glUniform1iv (program->glshader.args_locations[i], 1, (const gint32 *)data); - break; - case GSK_GL_UNIFORM_TYPE_UINT: - case GSK_GL_UNIFORM_TYPE_BOOL: - glUniform1uiv (program->glshader.args_locations[i], 1, (const guint32 *) data); - break; - case GSK_GL_UNIFORM_TYPE_VEC2: - glUniform2fv (program->glshader.args_locations[i], 1, (const float *)data); - break; - case GSK_GL_UNIFORM_TYPE_VEC3: - glUniform3fv (program->glshader.args_locations[i], 1, (const float *)data); - break; - case GSK_GL_UNIFORM_TYPE_VEC4: - glUniform4fv (program->glshader.args_locations[i], 1, (const float *)data); - break; - } - } -} - -static inline void -apply_border_width_op (const Program *program, - const OpBorder *op) -{ - OP_PRINT (" -> Border width (%f, %f, %f, %f)", - op->widths[0], op->widths[1], op->widths[2], op->widths[3]); - - glUniform4fv (program->border.widths_location, 1, op->widths); -} - -static inline void -apply_border_color_op (const Program *program, - const OpBorder *op) -{ - OP_PRINT (" -> Border color: %s", gdk_rgba_to_string (op->color)); - glUniform4fv (program->border.color_location, 1, (float *)op->color); -} - -static inline void -apply_blur_op (const Program *program, - const OpBlur *op) -{ - OP_PRINT (" -> Blur"); - glUniform1f (program->blur.blur_radius_location, op->radius); - glUniform2f (program->blur.blur_size_location, op->size.width, op->size.height); - glUniform2f (program->blur.blur_dir_location, op->dir[0], op->dir[1]); -} - -static inline void -apply_cross_fade_op (const Program *program, - const OpCrossFade *op) -{ - OP_PRINT (" -> Cross fade"); - /* End texture id */ - glUniform1i (program->cross_fade.source2_location, 1); - glActiveTexture (GL_TEXTURE0 + 1); - glBindTexture (GL_TEXTURE_2D, op->source2); - /* progress */ - glUniform1f (program->cross_fade.progress_location, op->progress); -} - -static inline void -apply_blend_op (const Program *program, - const OpBlend *op) -{ - /* End texture id */ - glUniform1i (program->blend.source2_location, 1); - glActiveTexture (GL_TEXTURE0 + 1); - glBindTexture (GL_TEXTURE_2D, op->source2); - /* progress */ - glUniform1i (program->blend.mode_location, op->mode); -} - -static inline void -apply_repeat_op (const Program *program, - const OpRepeat *op) -{ - OP_PRINT (" -> Repeat"); - glUniform4fv (program->repeat.child_bounds_location, 1, op->child_bounds); - glUniform4fv (program->repeat.texture_rect_location, 1, op->texture_rect); -} - -static void -gsk_gl_renderer_dispose (GObject *gobject) -{ - GskGLRenderer *self = GSK_GL_RENDERER (gobject); - - ops_free (&self->op_builder); - - G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject); -} - -static void -program_init (Program *program) -{ - program->index = -1; - program->state.opacity = 1.0f; -} - -static void -program_finalize (Program *program) -{ - if (program->id > 0) - glDeleteProgram (program->id); - if (program->index == -1 && - program->glshader.compile_error != NULL) - g_error_free (program->glshader.compile_error); - - gsk_transform_unref (program->state.modelview); -} - -static void -program_free (Program *program) -{ - program_finalize (program); - g_free (program); -} - -static GskGLRendererPrograms * -gsk_gl_renderer_programs_new (void) -{ - GskGLRendererPrograms *programs; - int i; - - programs = g_new0 (GskGLRendererPrograms, 1); - programs->ref_count = 1; - for (i = 0; i < GL_N_PROGRAMS; i ++) - program_init (&programs->programs[i]); - - /* We use direct hash for performance, not string hash on the source, because we assume each caller - * reuses a single GskGLShader for all uses and different callers will use different source content. */ - programs->custom_programs = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify)g_object_unref, (GDestroyNotify)program_free); - - return programs; -} - -static GskGLRendererPrograms * -gsk_gl_renderer_programs_ref (GskGLRendererPrograms *programs) -{ - programs->ref_count++; - return programs; -} - -/* Must be called with the context current */ -static void -gsk_gl_renderer_programs_unref (GskGLRendererPrograms *programs) -{ - int i; - programs->ref_count--; - if (programs->ref_count == 0) - { - for (i = 0; i < GL_N_PROGRAMS; i ++) - program_finalize (&programs->programs[i]); - - g_hash_table_destroy (programs->custom_programs); - g_free (programs); - } -} - -static Program * -gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self, - GskGLShader *shader) -{ - return g_hash_table_lookup (self->programs->custom_programs, shader); -} - -static Program * -gsk_gl_renderer_create_custom_program (GskGLRenderer *self, - GskGLShader *shader) -{ - Program *program = g_new0 (Program, 1); - - program_init (program); - - g_hash_table_insert (self->programs->custom_programs, g_object_ref (shader), program); - - return program; -} - -static GskGLRendererPrograms * -gsk_gl_renderer_create_programs (GskGLRenderer *self, - GError **error) -{ - GskGLShaderBuilder shader_builder; - GskGLRendererPrograms *programs = NULL; - int i; - static const struct { - const char *resource_path; - const char *name; - } program_definitions[] = { - { "/org/gtk/libgsk/gl/blend.glsl", "blend" }, - { "/org/gtk/libgsk/gl/blit.glsl", "blit" }, - { "/org/gtk/libgsk/gl/blur.glsl", "blur" }, - { "/org/gtk/libgsk/gl/border.glsl", "border" }, - { "/org/gtk/libgsk/gl/color_matrix.glsl", "color matrix" }, - { "/org/gtk/libgsk/gl/color.glsl", "color" }, - { "/org/gtk/libgsk/gl/coloring.glsl", "coloring" }, - { "/org/gtk/libgsk/gl/cross_fade.glsl", "cross fade" }, - { "/org/gtk/libgsk/gl/inset_shadow.glsl", "inset shadow" }, - { "/org/gtk/libgsk/gl/linear_gradient.glsl", "linear gradient" }, - { "/org/gtk/libgsk/gl/radial_gradient.glsl", "radial gradient" }, - { "/org/gtk/libgsk/gl/conic_gradient.glsl", "conic gradient" }, - { "/org/gtk/libgsk/gl/outset_shadow.glsl", "outset shadow" }, - { "/org/gtk/libgsk/gl/repeat.glsl", "repeat" }, - { "/org/gtk/libgsk/gl/unblurred_outset_shadow.glsl", "unblurred_outset shadow" }, - }; - - gsk_gl_shader_builder_init (&shader_builder, - "/org/gtk/libgsk/gl/preamble.glsl", - "/org/gtk/libgsk/gl/preamble.vs.glsl", - "/org/gtk/libgsk/gl/preamble.fs.glsl"); - - g_assert (G_N_ELEMENTS (program_definitions) == GL_N_PROGRAMS); - - init_shader_builder (self, &shader_builder); - - programs = gsk_gl_renderer_programs_new (); - - for (i = 0; i < GL_N_PROGRAMS; i ++) - { - Program *prog = &programs->programs[i]; - - prog->name = program_definitions[i].name; - prog->index = i; - prog->id = gsk_gl_shader_builder_create_program (&shader_builder, - program_definitions[i].resource_path, - NULL, 0, error); - if (prog->id < 0) - { - g_clear_pointer (&programs, gsk_gl_renderer_programs_unref); - goto out; - } - - INIT_COMMON_UNIFORM_LOCATION (prog, alpha); - INIT_COMMON_UNIFORM_LOCATION (prog, source); - INIT_COMMON_UNIFORM_LOCATION (prog, clip_rect); - INIT_COMMON_UNIFORM_LOCATION (prog, viewport); - INIT_COMMON_UNIFORM_LOCATION (prog, projection); - INIT_COMMON_UNIFORM_LOCATION (prog, modelview); - } - /* color */ - INIT_PROGRAM_UNIFORM_LOCATION (color, color); - - /* coloring */ - INIT_PROGRAM_UNIFORM_LOCATION (coloring, color); - - /* color matrix */ - INIT_PROGRAM_UNIFORM_LOCATION (color_matrix, color_matrix); - INIT_PROGRAM_UNIFORM_LOCATION (color_matrix, color_offset); - - /* linear gradient */ - INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, color_stops); - INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, num_color_stops); - INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, repeat); - INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, points); - - /* radial gradient */ - INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, color_stops); - INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, num_color_stops); - INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, repeat); - INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, geometry); - INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, range); - - /* conic gradient */ - INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, color_stops); - INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, num_color_stops); - INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, geometry); - - /* blur */ - INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius); - INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_size); - INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_dir); - - /* inset shadow */ - INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, color); - INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, spread); - INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, offset); - INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, outline_rect); - - /* outset shadow */ - INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, color); - INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, outline_rect); - - /* unblurred outset shadow */ - INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, color); - INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, spread); - INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, offset); - INIT_PROGRAM_UNIFORM_LOCATION (unblurred_outset_shadow, outline_rect); - - /* border */ - INIT_PROGRAM_UNIFORM_LOCATION (border, color); - INIT_PROGRAM_UNIFORM_LOCATION (border, widths); - INIT_PROGRAM_UNIFORM_LOCATION (border, outline_rect); - - /* cross fade */ - INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, progress); - INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, source2); - - /* blend */ - 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); - - - /* We initialize the alpha uniform here, since the default value is important. - * We can't do it in the shader like a reasonable person would because that doesn't - * work in gles. */ - for (i = 0; i < GL_N_PROGRAMS; i++) - { - glUseProgram(programs->programs[i].id); - glUniform1f (programs->programs[i].alpha_location, 1.0); - } - -out: - gsk_gl_shader_builder_finish (&shader_builder); - - /* Check we indeed emitted an error if there was one */ - g_assert (programs || !error || *error); - - return programs; -} - -static GskGLRendererPrograms * -get_programs_for_display (GskGLRenderer *self, - GdkDisplay *display, - GError **error) -{ - GskGLRendererPrograms *programs; - - if (g_getenv ("GSK_NO_SHARED_PROGRAMS")) - return gsk_gl_renderer_create_programs (self, error); - - programs = (GskGLRendererPrograms *)g_object_get_data (G_OBJECT (display), "gsk-gl-programs"); - if (programs == NULL) - { - programs = gsk_gl_renderer_create_programs (self, error); - if (programs) - g_object_set_data_full (G_OBJECT (display), "gsk-gl-programs", - programs, - (GDestroyNotify) gsk_gl_renderer_programs_unref); - } - - if (programs) - return gsk_gl_renderer_programs_ref (programs); - return NULL; -} - - -static GskGLTextureAtlases * -get_texture_atlases_for_display (GdkDisplay *display) -{ - GskGLTextureAtlases *atlases; - - if (g_getenv ("GSK_NO_SHARED_CACHES")) - return gsk_gl_texture_atlases_new (); - - atlases = (GskGLTextureAtlases*)g_object_get_data (G_OBJECT (display), "gsk-gl-texture-atlases"); - if (atlases == NULL) - { - atlases = gsk_gl_texture_atlases_new (); - g_object_set_data_full (G_OBJECT (display), "gsk-gl-texture-atlases", - atlases, - (GDestroyNotify) gsk_gl_texture_atlases_unref); - } - - return gsk_gl_texture_atlases_ref (atlases); -} - -static GskGLGlyphCache * -get_glyph_cache_for_display (GdkDisplay *display, - GskGLTextureAtlases *atlases) -{ - GskGLGlyphCache *glyph_cache; - - if (g_getenv ("GSK_NO_SHARED_CACHES")) - return gsk_gl_glyph_cache_new (display, atlases); - - glyph_cache = (GskGLGlyphCache*)g_object_get_data (G_OBJECT (display), "gsk-gl-glyph-cache"); - if (glyph_cache == NULL) - { - glyph_cache = gsk_gl_glyph_cache_new (display, atlases); - g_object_set_data_full (G_OBJECT (display), "gsk-gl-glyph-cache", - glyph_cache, - (GDestroyNotify) gsk_gl_glyph_cache_unref); - } - - return gsk_gl_glyph_cache_ref (glyph_cache); -} - -static GskGLIconCache * -get_icon_cache_for_display (GdkDisplay *display, - GskGLTextureAtlases *atlases) -{ - GskGLIconCache *icon_cache; - - if (g_getenv ("GSK_NO_SHARED_CACHES")) - return gsk_gl_icon_cache_new (display, atlases); - - icon_cache = (GskGLIconCache*)g_object_get_data (G_OBJECT (display), "gsk-gl-icon-cache"); - if (icon_cache == NULL) - { - icon_cache = gsk_gl_icon_cache_new (display, atlases); - g_object_set_data_full (G_OBJECT (display), "gsk-gl-icon-cache", - icon_cache, - (GDestroyNotify) gsk_gl_icon_cache_unref); - } - - return gsk_gl_icon_cache_ref (icon_cache); -} - -static gboolean -gsk_gl_renderer_realize (GskRenderer *renderer, - GdkSurface *surface, - GError **error) -{ - GskGLRenderer *self = GSK_GL_RENDERER (renderer); - gint64 before G_GNUC_UNUSED; - - before = GDK_PROFILER_CURRENT_TIME; - /* If we didn't get a GdkGLContext before realization, try creating - * one now, for our exclusive use. - */ - if (self->gl_context == NULL) - { - self->gl_context = gdk_surface_create_gl_context (surface, error); - if (self->gl_context == NULL) - return FALSE; - } - - if (!gdk_gl_context_realize (self->gl_context, error)) - return FALSE; - - gdk_gl_context_make_current (self->gl_context); - - g_assert (self->gl_driver == NULL); - self->gl_profiler = gsk_gl_profiler_new (self->gl_context); - self->gl_driver = gsk_gl_driver_new (self->gl_context); - - GSK_RENDERER_NOTE (renderer, OPENGL, g_message ("Creating buffers and programs")); - self->programs = get_programs_for_display (self, gdk_surface_get_display (surface), error); - if (self->programs == NULL) - return FALSE; - self->op_builder.programs = self->programs; - - self->atlases = get_texture_atlases_for_display (gdk_surface_get_display (surface)); - self->glyph_cache = get_glyph_cache_for_display (gdk_surface_get_display (surface), self->atlases); - self->icon_cache = get_icon_cache_for_display (gdk_surface_get_display (surface), self->atlases); - gsk_gl_shadow_cache_init (&self->shadow_cache); - - gdk_profiler_end_mark (before, "gl renderer realize", NULL); - - return TRUE; -} - -static void -gsk_gl_renderer_unrealize (GskRenderer *renderer) -{ - GskGLRenderer *self = GSK_GL_RENDERER (renderer); - - if (self->gl_context == NULL) - return; - - gdk_gl_context_make_current (self->gl_context); - - /* We don't need to iterate to destroy the associated GL resources, - * as they will be dropped when we finalize the GskGLDriver - */ - ops_reset (&self->op_builder); - self->op_builder.programs = NULL; - - g_clear_pointer (&self->programs, gsk_gl_renderer_programs_unref); - g_clear_pointer (&self->glyph_cache, gsk_gl_glyph_cache_unref); - g_clear_pointer (&self->icon_cache, gsk_gl_icon_cache_unref); - g_clear_pointer (&self->atlases, gsk_gl_texture_atlases_unref); - gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver); - - g_clear_object (&self->gl_profiler); - g_clear_object (&self->gl_driver); - - if (self->gl_context == gdk_gl_context_get_current ()) - gdk_gl_context_clear_current (); - - g_clear_object (&self->gl_context); -} - -static void -gsk_gl_renderer_clear_tree (GskGLRenderer *self) -{ - if (self->gl_context == NULL) - return; - - gdk_gl_context_make_current (self->gl_context); - - ops_reset (&self->op_builder); - -#ifdef G_ENABLE_DEBUG - int removed_textures = gsk_gl_driver_collect_textures (self->gl_driver); - GSK_RENDERER_NOTE (GSK_RENDERER (self), OPENGL, g_message ("Collected: %d textures", removed_textures)); -#else - gsk_gl_driver_collect_textures (self->gl_driver); -#endif -} - -static void -gsk_gl_renderer_clear (GskGLRenderer *self) -{ - GSK_RENDERER_NOTE (GSK_RENDERER (self), OPENGL, g_message ("Clearing viewport")); - glClearColor (0, 0, 0, 0); - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); -} - -static void -gsk_gl_renderer_setup_render_mode (GskGLRenderer *self) -{ - if (self->render_region == NULL) - { - glDisable (GL_SCISSOR_TEST); - } - else - { - GdkSurface *surface = gsk_renderer_get_surface (GSK_RENDERER (self)); - cairo_rectangle_int_t extents; - int surface_height; - - g_assert (cairo_region_num_rectangles (self->render_region) == 1); - - surface_height = gdk_surface_get_height (surface) * self->scale_factor; - cairo_region_get_rectangle (self->render_region, 0, &extents); - - glEnable (GL_SCISSOR_TEST); - glScissor (extents.x * self->scale_factor, - surface_height - (extents.height * self->scale_factor) - (extents.y * self->scale_factor), - extents.width * self->scale_factor, - extents.height * self->scale_factor); - } -} - -static void -gsk_gl_renderer_add_render_ops (GskGLRenderer *self, - GskRenderNode *node, - RenderOpBuilder *builder) -{ - /* This can still happen, even if the render nodes are created using - * GtkSnapshot, so let's just be safe. */ - if (node_is_invisible (node)) - return; - - /* Check whether the render node is entirely out of the current - * already transformed clip region */ - { - graphene_rect_t transformed_node_bounds; - - ops_transform_bounds_modelview (builder, - &node->bounds, - &transformed_node_bounds); - - if (!graphene_rect_intersects (&builder->current_clip->bounds, - &transformed_node_bounds)) - return; - } - - switch (gsk_render_node_get_node_type (node)) - { - case GSK_NOT_A_RENDER_NODE: - g_assert_not_reached (); - - case 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_ops (self, child, builder); - } - } - break; - - case GSK_DEBUG_NODE: - { - const char *message = gsk_debug_node_get_message (node); - if (message) - ops_push_debug_group (builder, message); - gsk_gl_renderer_add_render_ops (self, - gsk_debug_node_get_child (node), - builder); - if (message) - ops_pop_debug_group (builder); - } - break; - - case GSK_COLOR_NODE: - render_color_node (self, node, builder); - break; - - case GSK_TEXTURE_NODE: - render_texture_node (self, node, builder); - break; - - case GSK_TRANSFORM_NODE: - render_transform_node (self, node, builder); - break; - - case GSK_OPACITY_NODE: - render_opacity_node (self, node, builder); - break; - - case GSK_LINEAR_GRADIENT_NODE: - case GSK_REPEATING_LINEAR_GRADIENT_NODE: - render_linear_gradient_node (self, node, builder); - break; - - case GSK_RADIAL_GRADIENT_NODE: - case GSK_REPEATING_RADIAL_GRADIENT_NODE: - render_radial_gradient_node (self, node, builder); - break; - - case GSK_CONIC_GRADIENT_NODE: - render_conic_gradient_node (self, node, builder); - break; - - case GSK_CLIP_NODE: - render_clip_node (self, node, builder); - break; - - case GSK_ROUNDED_CLIP_NODE: - render_rounded_clip_node (self, node, builder); - break; - - case GSK_TEXT_NODE: - render_text_node (self, node, builder, - gsk_text_node_get_color (node), FALSE); - break; - - case GSK_COLOR_MATRIX_NODE: - render_color_matrix_node (self, node, builder); - break; - - case GSK_BLUR_NODE: - render_blur_node (self, node, builder); - break; - - case GSK_INSET_SHADOW_NODE: - if (gsk_inset_shadow_node_get_blur_radius (node) > 0) - render_inset_shadow_node (self, node, builder); - else - render_unblurred_inset_shadow_node (self, node, builder); - break; - - case GSK_OUTSET_SHADOW_NODE: - if (gsk_outset_shadow_node_get_blur_radius (node) > 0) - render_outset_shadow_node (self, node, builder); - else - render_unblurred_outset_shadow_node (self, node, builder); - break; - - case GSK_SHADOW_NODE: - render_shadow_node (self, node, builder); - break; - - case GSK_BORDER_NODE: - render_border_node (self, node, builder); - break; - - case GSK_CROSS_FADE_NODE: - render_cross_fade_node (self, node, builder); - break; - - case GSK_BLEND_NODE: - render_blend_node (self, node, builder); - break; - - case GSK_REPEAT_NODE: - render_repeat_node (self, node, builder); - break; - - case GSK_GL_SHADER_NODE: - render_gl_shader_node (self, node, builder); - break; - - case GSK_CAIRO_NODE: - default: - { - render_fallback_node (self, node, builder); - } - } -} - -static gboolean -add_offscreen_ops (GskGLRenderer *self, - RenderOpBuilder *builder, - const graphene_rect_t *bounds, - GskRenderNode *child_node, - TextureRegion *texture_region_out, - gboolean *is_offscreen, - guint flags) -{ - const float dx = builder->dx; - const float dy = builder->dy; - float scaled_width, scaled_height; - float scale_x; - float scale_y; - int render_target; - int prev_render_target; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - graphene_matrix_t item_proj; - float prev_opacity = 1.0; - int texture_id = 0; - int filter; - GskTextureKey key; - int cached_id; - graphene_rect_t viewport; - - if (node_is_invisible (child_node)) - { - /* Just to be safe */ - *is_offscreen = FALSE; - init_full_texture_region (texture_region_out, 0); - return FALSE; - } - - /* We need the child node as a texture. If it already is one, we don't need to draw - * it on a framebuffer of course. */ - if (gsk_render_node_get_node_type (child_node) == GSK_TEXTURE_NODE && - (flags & FORCE_OFFSCREEN) == 0) - { - GdkTexture *texture = gsk_texture_node_get_texture (child_node); - upload_texture (self, texture, texture_region_out); - *is_offscreen = FALSE; - return TRUE; - } - - if (flags & LINEAR_FILTER) - filter = GL_LINEAR; - else - filter = GL_NEAREST; - - /* Check if we've already cached the drawn texture. */ - key.pointer = child_node; - key.pointer_is_child = TRUE; /* Don't conflict with the child using the cache too */ - key.parent_rect = *bounds; - key.scale_x = builder->scale_x; - key.scale_y = builder->scale_y; - key.filter = filter; - cached_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key); - - if (cached_id != 0) - { - init_full_texture_region (texture_region_out, cached_id); - /* We didn't render it offscreen, but hand out an offscreen texture id */ - *is_offscreen = TRUE; - return TRUE; - } - - scale_x = builder->scale_x; - scale_y = builder->scale_y; - - /* Tweak the scale factor so that the required texture doesn't - * exceed the max texture limit. This will render with a lower - * resolution, but this is better than clipping. - */ - { - const int max_texture_size = gsk_gl_driver_get_max_texture_size (self->gl_driver); - - scaled_width = ceilf (bounds->size.width * scale_x); - if (scaled_width > max_texture_size) - { - scale_x *= (float)max_texture_size / scaled_width; - scaled_width = max_texture_size; - } - - scaled_height = ceilf (bounds->size.height * scale_y); - if (scaled_height > max_texture_size) - { - scale_y *= (float)max_texture_size / scaled_height; - scaled_height = max_texture_size; - } - } - - gsk_gl_driver_create_render_target (self->gl_driver, - scaled_width, scaled_height, - filter, filter, - &texture_id, &render_target); - if (gdk_gl_context_has_debug (self->gl_context)) - { - gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id, - "Offscreen<%s> %d", - g_type_name_from_instance ((GTypeInstance *) child_node), - texture_id); - gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, render_target, - "Offscreen<%s> FB %d", - g_type_name_from_instance ((GTypeInstance *) child_node), - render_target); - } - - ops_transform_bounds_modelview (builder, bounds, &viewport); - /* Code above will scale the size with the scale we use in the render ops, - * but for the viewport size, we need our own size limited by the texture size */ - viewport.size.width = scaled_width; - viewport.size.height = scaled_height; - - init_projection_matrix (&item_proj, &viewport); - prev_render_target = ops_set_render_target (builder, render_target); - /* Clear since we use this rendertarget for the first time */ - ops_begin (builder, OP_CLEAR); - prev_projection = ops_set_projection (builder, &item_proj); - ops_set_modelview (builder, gsk_transform_scale (NULL, scale_x, scale_y)); - prev_viewport = ops_set_viewport (builder, &viewport); - if (flags & RESET_CLIP) - ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport)); - - builder->dx = dx; - builder->dy = dy; - - prev_opacity = ops_set_opacity (builder, 1.0); - - gsk_gl_renderer_add_render_ops (self, child_node, builder); - -#ifdef G_ENABLE_DEBUG - if (G_UNLIKELY (flags & DUMP_FRAMEBUFFER)) - { - static int k; - ops_dump_framebuffer (builder, - g_strdup_printf ("%s_%p_%d.png", - g_type_name_from_instance ((GTypeInstance *) child_node), - child_node, - k ++), - scaled_width, scaled_height); - } -#endif - - ops_set_opacity (builder, prev_opacity); - - builder->dx = dx; - builder->dy = dy; - - if (flags & RESET_CLIP) - ops_pop_clip (builder); - - ops_set_viewport (builder, &prev_viewport); - ops_pop_modelview (builder); - ops_set_projection (builder, &prev_projection); - ops_set_render_target (builder, prev_render_target); - - *is_offscreen = TRUE; - init_full_texture_region (texture_region_out, texture_id); - - if ((flags & NO_CACHE_PLZ) == 0) - gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, texture_id); - - return TRUE; -} - -static void -gsk_gl_renderer_render_ops (GskGLRenderer *self) -{ - const Program *program = NULL; - const gsize vertex_data_size = self->op_builder.vertices->len * sizeof (GskQuadVertex); - const float *vertex_data = (float *)self->op_builder.vertices->data; - OpBufferIter iter; - OpKind kind; - gpointer ptr; - GLuint buffer_id, vao_id; - -#if DEBUG_OPS - g_print ("============================================\n"); -#endif - - glGenVertexArrays (1, &vao_id); - glBindVertexArray (vao_id); - - glGenBuffers (1, &buffer_id); - glBindBuffer (GL_ARRAY_BUFFER, buffer_id); - - glBufferData (GL_ARRAY_BUFFER, vertex_data_size, vertex_data, GL_STATIC_DRAW); - - /* 0 = position location */ - glEnableVertexAttribArray (0); - glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE, - sizeof (GskQuadVertex), - (void *) G_STRUCT_OFFSET (GskQuadVertex, position)); - /* 1 = texture coord location */ - glEnableVertexAttribArray (1); - glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, - sizeof (GskQuadVertex), - (void *) G_STRUCT_OFFSET (GskQuadVertex, uv)); - - op_buffer_iter_init (&iter, ops_get_buffer (&self->op_builder)); - while ((ptr = op_buffer_iter_next (&iter, &kind))) - { - if (kind == OP_NONE) - continue; - - if (program == NULL && - kind != OP_PUSH_DEBUG_GROUP && - kind != OP_POP_DEBUG_GROUP && - kind != OP_CHANGE_PROGRAM && - kind != OP_CHANGE_RENDER_TARGET && - kind != OP_CLEAR) - continue; - - OP_PRINT ("Op %u: %u", iter.pos - 2, kind); - - switch (kind) - { - case OP_CHANGE_PROJECTION: - apply_projection_op (program, ptr); - break; - - case OP_CHANGE_MODELVIEW: - apply_modelview_op (program, ptr); - break; - - case OP_CHANGE_PROGRAM: - { - const OpProgram *op = ptr; - apply_program_op (program, op); - program = op->program; - break; - } - - case OP_CHANGE_RENDER_TARGET: - apply_render_target_op (self, ptr); - break; - - case OP_CLEAR: - OP_PRINT ("-> CLEAR"); - glClearColor (0, 0, 0, 0); - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - break; - - case OP_CHANGE_VIEWPORT: - apply_viewport_op (program, ptr); - break; - - case OP_CHANGE_OPACITY: - apply_opacity_op (program, ptr); - break; - - case OP_CHANGE_COLOR_MATRIX: - apply_color_matrix_op (program, ptr); - break; - - case OP_CHANGE_COLOR: - /*g_assert (program == &self->color_program || program == &self->coloring_program ||*/ - /*program == &self->shadow_program);*/ - apply_color_op (program, ptr); - break; - - case OP_CHANGE_BORDER_COLOR: - apply_border_color_op (program, ptr); - break; - - case OP_CHANGE_CLIP: - apply_clip_op (program, ptr); - break; - - case OP_CHANGE_SOURCE_TEXTURE: - apply_source_texture_op (program, ptr); - break; - - case OP_CHANGE_EXTRA_SOURCE_TEXTURE: - apply_source_extra_texture_op (program, ptr); - break; - - case OP_CHANGE_CROSS_FADE: - g_assert (program == &self->programs->cross_fade_program); - apply_cross_fade_op (program, ptr); - break; - - case OP_CHANGE_BLEND: - g_assert (program == &self->programs->blend_program); - apply_blend_op (program, ptr); - break; - - case OP_CHANGE_LINEAR_GRADIENT: - apply_linear_gradient_op (program, ptr); - break; - - case OP_CHANGE_RADIAL_GRADIENT: - apply_radial_gradient_op (program, ptr); - break; - - case OP_CHANGE_CONIC_GRADIENT: - apply_conic_gradient_op (program, ptr); - break; - - case OP_CHANGE_BLUR: - apply_blur_op (program, ptr); - break; - - case OP_CHANGE_INSET_SHADOW: - apply_inset_shadow_op (program, ptr); - break; - - case OP_CHANGE_OUTSET_SHADOW: - apply_outset_shadow_op (program, ptr); - break; - - case OP_CHANGE_BORDER: - apply_border_op (program, ptr); - break; - - case OP_CHANGE_BORDER_WIDTH: - apply_border_width_op (program, ptr); - break; - - case OP_CHANGE_UNBLURRED_OUTSET_SHADOW: - apply_unblurred_outset_shadow_op (program, ptr); - break; - - case OP_CHANGE_REPEAT: - apply_repeat_op (program, ptr); - break; - - case OP_CHANGE_GL_SHADER_ARGS: - apply_gl_shader_args_op (program, ptr); - break; - - case OP_DRAW: - { - const OpDraw *op = ptr; - - OP_PRINT (" -> draw %ld, size %ld and program %d: %s", - op->vao_offset, op->vao_size, program->index, - program->name ?: ""); - glDrawArrays (GL_TRIANGLES, op->vao_offset, op->vao_size); - break; - } - - case OP_DUMP_FRAMEBUFFER: - { - const OpDumpFrameBuffer *op = ptr; - - dump_framebuffer (op->filename, op->width, op->height); - break; - } - - case OP_PUSH_DEBUG_GROUP: - { - const OpDebugGroup *op = ptr; - gdk_gl_context_push_debug_group (self->gl_context, op->text); - OP_PRINT (" Debug: %s", op->text); - break; - } - - case OP_POP_DEBUG_GROUP: - gdk_gl_context_pop_debug_group (self->gl_context); - break; - - case OP_NONE: - case OP_LAST: - default: - g_warn_if_reached (); - } - - OP_PRINT ("\n"); - } - - glDeleteVertexArrays (1, &vao_id); - glDeleteBuffers (1, &buffer_id); -} - -static void -gsk_gl_renderer_do_render (GskRenderer *renderer, - GskRenderNode *root, - const graphene_rect_t *viewport, - int fbo_id, - int scale_factor) -{ - GskGLRenderer *self = GSK_GL_RENDERER (renderer); - graphene_matrix_t projection; -#ifdef G_ENABLE_DEBUG - GskProfiler *profiler; - gint64 gpu_time, cpu_time; - gint64 start_time G_GNUC_UNUSED; -#endif - GPtrArray *removed; - -#ifdef G_ENABLE_DEBUG - profiler = gsk_renderer_get_profiler (renderer); -#endif - - if (self->gl_context == NULL) - { - GSK_RENDERER_NOTE (renderer, OPENGL, g_message ("No valid GL context associated to the renderer")); - return; - } - - g_assert (gsk_gl_driver_in_frame (self->gl_driver)); - - removed = g_ptr_array_new (); - gsk_gl_texture_atlases_begin_frame (self->atlases, removed); - gsk_gl_glyph_cache_begin_frame (self->glyph_cache, self->gl_driver, removed); - gsk_gl_icon_cache_begin_frame (self->icon_cache, removed); - gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver); - g_ptr_array_unref (removed); - - /* Set up the modelview and projection matrices to fit our viewport */ - init_projection_matrix (&projection, viewport); - ops_set_projection (&self->op_builder, &projection); - ops_set_viewport (&self->op_builder, viewport); - ops_set_modelview (&self->op_builder, gsk_transform_scale (NULL, scale_factor, scale_factor)); - - /* Initial clip is self->render_region! */ - if (self->render_region != NULL) - { - graphene_rect_t transformed_render_region; - cairo_rectangle_int_t render_extents; - - cairo_region_get_extents (self->render_region, &render_extents); - - ops_transform_bounds_modelview (&self->op_builder, - &GRAPHENE_RECT_INIT (render_extents.x, - render_extents.y, - render_extents.width, - render_extents.height), - &transformed_render_region); - ops_push_clip (&self->op_builder, - &GSK_ROUNDED_RECT_INIT (transformed_render_region.origin.x, - transformed_render_region.origin.y, - transformed_render_region.size.width, - transformed_render_region.size.height)); - } - else - { - ops_push_clip (&self->op_builder, - &GSK_ROUNDED_RECT_INIT (viewport->origin.x, - viewport->origin.y, - viewport->size.width, - viewport->size.height)); - } - - if (fbo_id != 0) - ops_set_render_target (&self->op_builder, fbo_id); - - gdk_gl_context_push_debug_group (self->gl_context, "Adding render ops"); - gsk_gl_renderer_add_render_ops (self, root, &self->op_builder); - gdk_gl_context_pop_debug_group (self->gl_context); - - /* We correctly reset the state everywhere */ - g_assert_cmpint (self->op_builder.current_render_target, ==, fbo_id); - ops_pop_modelview (&self->op_builder); - ops_pop_clip (&self->op_builder); - ops_finish (&self->op_builder); - - /*g_message ("Ops: %u", self->render_ops->len);*/ - - /* Now actually draw things... */ -#ifdef G_ENABLE_DEBUG - gsk_gl_profiler_begin_gpu_region (self->gl_profiler); - gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time); -#endif - - /* Actually do the rendering */ - if (fbo_id != 0) - glBindFramebuffer (GL_FRAMEBUFFER, fbo_id); - - glViewport (0, 0, ceilf (viewport->size.width), ceilf (viewport->size.height)); - gsk_gl_renderer_setup_render_mode (self); - gsk_gl_renderer_clear (self); - - glEnable (GL_DEPTH_TEST); - glDepthFunc (GL_LEQUAL); - - /* Pre-multiplied alpha! */ - glEnable (GL_BLEND); - glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation (GL_FUNC_ADD); - - gdk_gl_context_push_debug_group (self->gl_context, "Rendering ops"); - gsk_gl_renderer_render_ops (self); - gdk_gl_context_pop_debug_group (self->gl_context); - -#ifdef G_ENABLE_DEBUG - gsk_profiler_counter_inc (profiler, self->profile_counters.frames); - - start_time = gsk_profiler_timer_get_start (profiler, self->profile_timers.cpu_time); - cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time); - gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time); - - gpu_time = gsk_gl_profiler_end_gpu_region (self->gl_profiler); - gsk_profiler_timer_set (profiler, self->profile_timers.gpu_time, gpu_time); - - gsk_profiler_push_samples (profiler); - - gdk_profiler_add_mark (start_time * 1000, cpu_time * 1000, "GL render", ""); -#endif -} - -static GdkTexture * -gsk_gl_renderer_render_texture (GskRenderer *renderer, - GskRenderNode *root, - const graphene_rect_t *viewport) -{ - GskGLRenderer *self = GSK_GL_RENDERER (renderer); - GdkTexture *texture; - int width, height; - guint texture_id; - guint fbo_id; - - g_return_val_if_fail (self->gl_context != NULL, NULL); - - gdk_gl_context_make_current (self->gl_context); - gdk_gl_context_push_debug_group_printf (self->gl_context, - "Render %s<%p> to texture", - g_type_name_from_instance ((GTypeInstance *) root), - root); - - width = ceilf (viewport->size.width); - height = ceilf (viewport->size.height); - - self->scale_factor = gdk_surface_get_scale_factor (gsk_renderer_get_surface (renderer)); - - /* Prepare our framebuffer */ - gsk_gl_driver_begin_frame (self->gl_driver); - glGenTextures (1, &texture_id); - glBindTexture (GL_TEXTURE_2D, texture_id); - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - if (gdk_gl_context_has_debug (self->gl_context)) - gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id, - "Texture %s<%p> %d", - g_type_name_from_instance ((GTypeInstance *) root), - root, - texture_id); - - if (gdk_gl_context_get_use_es (self->gl_context)) - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - else - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - - glGenFramebuffers (1, &fbo_id); - glBindFramebuffer (GL_FRAMEBUFFER, fbo_id); - - if (gdk_gl_context_has_debug (self->gl_context)) - gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, fbo_id, - "FB %s<%p> %d", - g_type_name_from_instance ((GTypeInstance *) root), - root, - fbo_id); - glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); - - /* Render the actual scene */ - gsk_gl_renderer_do_render (renderer, root, viewport, fbo_id, 1); - - glDeleteFramebuffers (1, &fbo_id); - - - /* Render the now drawn framebuffer y-flipped so it's as GdkGLTexture expects it to be */ - { - guint final_texture_id, final_fbo_id; - graphene_matrix_t m; - - ops_reset (&self->op_builder); - - glGenFramebuffers (1, &final_fbo_id); - glBindFramebuffer (GL_FRAMEBUFFER, final_fbo_id); - glGenTextures (1, &final_texture_id); - glBindTexture (GL_TEXTURE_2D, final_texture_id); - - if (gdk_gl_context_get_use_es (self->gl_context)) - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - else - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, final_texture_id, 0); - g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE); - - ops_set_render_target (&self->op_builder, final_fbo_id); - ops_push_clip (&self->op_builder, &GSK_ROUNDED_RECT_INIT (0, 0, width, height)); - ops_set_program (&self->op_builder, &self->programs->blit_program); - - ops_begin (&self->op_builder, OP_CLEAR); - ops_set_texture (&self->op_builder, texture_id); - ops_set_modelview (&self->op_builder, NULL); - ops_set_viewport (&self->op_builder, &GRAPHENE_RECT_INIT (0, 0, width, height)); - init_projection_matrix (&m, &GRAPHENE_RECT_INIT (0, 0, width, height)); - graphene_matrix_scale (&m, 1, -1, 1); /* Undo the scale init_projection_matrix() does again */ - ops_set_projection (&self->op_builder, &m); - - fill_vertex_data (ops_draw (&self->op_builder, NULL), - 0, 0, width, height); - ops_pop_clip (&self->op_builder); - gsk_gl_renderer_render_ops (self); - - ops_finish (&self->op_builder); - - glDeleteTextures (1, &texture_id); - - texture_id = final_texture_id; - } - - texture = gdk_gl_texture_new (self->gl_context, - texture_id, - width, height, - NULL, NULL); - - gsk_gl_driver_end_frame (self->gl_driver); - - gdk_gl_context_pop_debug_group (self->gl_context); - - gsk_gl_renderer_clear_tree (self); - return texture; -} - -static void -gsk_gl_renderer_render (GskRenderer *renderer, - GskRenderNode *root, - const cairo_region_t *update_area) -{ - GskGLRenderer *self = GSK_GL_RENDERER (renderer); - graphene_rect_t viewport; - const cairo_region_t *damage; - GdkRectangle whole_surface; - GdkSurface *surface; - - if (self->gl_context == NULL) - return; - - surface = gsk_renderer_get_surface (renderer); - self->scale_factor = gdk_surface_get_scale_factor (surface); - - gdk_gl_context_make_current (self->gl_context); - gdk_gl_context_push_debug_group_printf (self->gl_context, - "Render root node %p", root); - - whole_surface = (GdkRectangle) { - 0, 0, - gdk_surface_get_width (surface) * self->scale_factor, - gdk_surface_get_height (surface) * self->scale_factor - }; - - gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->gl_context), - update_area); - - damage = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->gl_context)); - - if (cairo_region_contains_rectangle (damage, &whole_surface) == CAIRO_REGION_OVERLAP_IN) - { - self->render_region = NULL; - } - else - { - GdkRectangle extents; - - cairo_region_get_extents (damage, &extents); - - if (gdk_rectangle_equal (&extents, &whole_surface)) - self->render_region = NULL; - else - self->render_region = cairo_region_create_rectangle (&extents); - } - - gdk_gl_context_make_current (self->gl_context); - - viewport.origin.x = 0; - viewport.origin.y = 0; - viewport.size.width = whole_surface.width; - viewport.size.height = whole_surface.height; - - gsk_gl_driver_begin_frame (self->gl_driver); - gsk_gl_renderer_do_render (renderer, root, &viewport, 0, self->scale_factor); - gsk_gl_driver_end_frame (self->gl_driver); - - gsk_gl_renderer_clear_tree (self); - - gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->gl_context)); - gdk_gl_context_make_current (self->gl_context); - - gdk_gl_context_pop_debug_group (self->gl_context); - - g_clear_pointer (&self->render_region, cairo_region_destroy); -} - -static void -gsk_gl_renderer_class_init (GskGLRendererClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass); - - gobject_class->dispose = gsk_gl_renderer_dispose; - - renderer_class->realize = gsk_gl_renderer_realize; - renderer_class->unrealize = gsk_gl_renderer_unrealize; - renderer_class->render = gsk_gl_renderer_render; - renderer_class->render_texture = gsk_gl_renderer_render_texture; -} - -static void -gsk_gl_renderer_init (GskGLRenderer *self) -{ - gsk_ensure_resources (); - - ops_init (&self->op_builder); - self->op_builder.renderer = self; - -#ifdef G_ENABLE_DEBUG - { - GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self)); - - self->profile_counters.frames = gsk_profiler_add_counter (profiler, "frames", "Frames", FALSE); - - self->profile_timers.cpu_time = gsk_profiler_add_timer (profiler, "cpu-time", "CPU time", FALSE, TRUE); - self->profile_timers.gpu_time = gsk_profiler_add_timer (profiler, "gpu-time", "GPU time", FALSE, TRUE); - } -#endif -} - -/** - * gsk_gl_renderer_new: - * - * Creates a new `GskRenderer` using OpenGL. - * - * Returns: a new GL renderer - */ -GskRenderer * -gsk_gl_renderer_new (void) -{ - return g_object_new (GSK_TYPE_GL_RENDERER, NULL); -} diff --git a/gsk/gl/gskglrenderer.h b/gsk/gl/gskglrenderer.h deleted file mode 100644 index 13466fc56d..0000000000 --- a/gsk/gl/gskglrenderer.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2016 Endless - * 2018 Timm Bäder - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - * Authors: Timm Bäder - */ - -#ifndef __GSK_GL_RENDERER_H__ -#define __GSK_GL_RENDERER_H__ - -#include - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type ()) - -#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER, GskGLRenderer)) -#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER)) -#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER, GskGLRendererClass)) -#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER)) -#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER, GskGLRendererClass)) - -/** - * GskGLRenderer: - * - * A GSK renderer that is using OpenGL. - */ -typedef struct _GskGLRenderer GskGLRenderer; -typedef struct _GskGLRendererClass GskGLRendererClass; - -GDK_AVAILABLE_IN_ALL -GType gsk_gl_renderer_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GskRenderer * gsk_gl_renderer_new (void); - -G_END_DECLS - -#endif /* __GSK_GL_RENDERER_H__ */ diff --git a/gsk/gl/gskglrendererprivate.h b/gsk/gl/gskglrendererprivate.h deleted file mode 100644 index 924ee18ca3..0000000000 --- a/gsk/gl/gskglrendererprivate.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __GSK_GL_RENDERER_PRIVATE_H__ -#define __GSK_GL_RENDERER_PRIVATE_H__ - -#include "gskglrenderer.h" - -G_BEGIN_DECLS - -gboolean gsk_gl_renderer_try_compile_gl_shader (GskGLRenderer *self, - GskGLShader *shader, - GError **error); - -G_END_DECLS - -#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */ diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c deleted file mode 100644 index 36f7e37e35..0000000000 --- a/gsk/gl/gskglrenderops.c +++ /dev/null @@ -1,976 +0,0 @@ -#include "gskglrenderopsprivate.h" -#include "gsktransform.h" - -typedef struct -{ - GskRoundedRect rect; - bool is_rectilinear; -} ClipStackEntry; - -static inline gboolean -rect_equal (const graphene_rect_t *a, - const graphene_rect_t *b) -{ - return memcmp (a, b, sizeof (graphene_rect_t)) == 0; -} - -static inline bool G_GNUC_PURE -rounded_rect_equal (const GskRoundedRect *r1, - const GskRoundedRect *r2) -{ - if (r1 == r2) - return true; - - if (!r1) - return false; - - if (r1->bounds.origin.x != r2->bounds.origin.x || - r1->bounds.origin.y != r2->bounds.origin.y || - r1->bounds.size.width != r2->bounds.size.width || - r1->bounds.size.height != r2->bounds.size.height) - return false; - - for (int i = 0; i < 4; i ++) - if (r1->corner[i].width != r2->corner[i].width || - r1->corner[i].height != r2->corner[i].height) - return false; - - return true; -} - -static inline gboolean G_GNUC_PURE -rounded_rect_corners_equal (const GskRoundedRect *r1, - const GskRoundedRect *r2) -{ - int i; - - if (!r1) - return FALSE; - - for (i = 0; i < 4; i ++) - if (r1->corner[i].width != r2->corner[i].width || - r1->corner[i].height != r2->corner[i].height) - return FALSE; - - return TRUE; -} - -static inline ProgramState * -get_current_program_state (RenderOpBuilder *builder) -{ - if (!builder->current_program) - return NULL; - - return &builder->current_program->state; -} - -void -ops_finish (RenderOpBuilder *builder) -{ - if (builder->mv_stack) - g_array_free (builder->mv_stack, TRUE); - builder->mv_stack = NULL; - - if (builder->clip_stack) - g_array_free (builder->clip_stack, TRUE); - builder->clip_stack = NULL; - - builder->dx = 0; - builder->dy = 0; - builder->scale_x = 1; - builder->scale_y = 1; - builder->current_modelview = NULL; - builder->current_clip = NULL; - builder->clip_is_rectilinear = TRUE; - builder->current_render_target = 0; - builder->current_texture = 0; - builder->current_program = NULL; - graphene_matrix_init_identity (&builder->current_projection); - builder->current_viewport = GRAPHENE_RECT_INIT (0, 0, 0, 0); -} - -/* Debugging only! */ -void -ops_dump_framebuffer (RenderOpBuilder *builder, - const char *filename, - int width, - int height) -{ - OpDumpFrameBuffer *op; - - op = ops_begin (builder, OP_DUMP_FRAMEBUFFER); - op->filename = g_strdup (filename); - op->width = width; - op->height = height; -} - -void -ops_push_debug_group (RenderOpBuilder *builder, - const char *text) -{ - OpDebugGroup *op; - - op = ops_begin (builder, OP_PUSH_DEBUG_GROUP); - strncpy (op->text, text, sizeof(op->text) - 1); - op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */ -} - -void -ops_pop_debug_group (RenderOpBuilder *builder) -{ - ops_begin (builder, OP_POP_DEBUG_GROUP); -} - -static void -extract_matrix_metadata (GskTransform *transform, - OpsMatrixMetadata *md) -{ - float dummy; - - switch (gsk_transform_get_category (transform)) - { - case GSK_TRANSFORM_CATEGORY_IDENTITY: - case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE: - md->scale_x = 1; - md->scale_y = 1; - break; - - case GSK_TRANSFORM_CATEGORY_2D_AFFINE: - gsk_transform_to_affine (transform, - &md->scale_x, &md->scale_y, - &dummy, &dummy); - break; - - case GSK_TRANSFORM_CATEGORY_UNKNOWN: - case GSK_TRANSFORM_CATEGORY_ANY: - case GSK_TRANSFORM_CATEGORY_3D: - case GSK_TRANSFORM_CATEGORY_2D: - { - graphene_vec3_t col1; - graphene_vec3_t col2; - graphene_matrix_t m; - - gsk_transform_to_matrix (transform, &m); - - /* TODO: 90% sure this is incorrect. But we should never hit this code - * path anyway. */ - graphene_vec3_init (&col1, - graphene_matrix_get_value (&m, 0, 0), - graphene_matrix_get_value (&m, 1, 0), - graphene_matrix_get_value (&m, 2, 0)); - - graphene_vec3_init (&col2, - graphene_matrix_get_value (&m, 0, 1), - graphene_matrix_get_value (&m, 1, 1), - graphene_matrix_get_value (&m, 2, 1)); - - md->scale_x = graphene_vec3_length (&col1); - md->scale_y = graphene_vec3_length (&col2); - } - break; - default: - {} - } -} - -void -ops_transform_bounds_modelview (const RenderOpBuilder *builder, - const graphene_rect_t *src, - graphene_rect_t *dst) -{ - graphene_rect_t r = *src; - - g_assert (builder->mv_stack != NULL); - g_assert (builder->mv_stack->len >= 1); - - r.origin.x += builder->dx; - r.origin.y += builder->dy; - - gsk_transform_transform_bounds (builder->current_modelview, &r, dst); -} - -void -ops_init (RenderOpBuilder *builder) -{ - memset (builder, 0, sizeof (*builder)); - - builder->current_opacity = 1.0f; - - op_buffer_init (&builder->render_ops); - builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex)); -} - -void -ops_free (RenderOpBuilder *builder) -{ - g_array_unref (builder->vertices); - op_buffer_destroy (&builder->render_ops); -} - -void -ops_set_program (RenderOpBuilder *builder, - Program *program) -{ - OpProgram *op; - - if (builder->current_program == program) - return; - - op = ops_begin (builder, OP_CHANGE_PROGRAM); - op->program = program; - - builder->current_program = program; -} - -void -ops_push_clip (RenderOpBuilder *self, - const GskRoundedRect *clip) -{ - ClipStackEntry entry; - - if (G_UNLIKELY (self->clip_stack == NULL)) - self->clip_stack = g_array_new (FALSE, TRUE, sizeof (ClipStackEntry)); - - g_assert (self->clip_stack != NULL); - - entry.rect = *clip; - entry.is_rectilinear = gsk_rounded_rect_is_rectilinear (clip); - g_array_append_val (self->clip_stack, entry); - self->current_clip = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1).rect; - self->clip_is_rectilinear = entry.is_rectilinear; -} - -void -ops_pop_clip (RenderOpBuilder *self) -{ - const ClipStackEntry *head; - - g_assert (self->clip_stack); - g_assert (self->clip_stack->len >= 1); - - self->clip_stack->len --; - head = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1); - - if (self->clip_stack->len >= 1) - { - self->current_clip = &head->rect; - self->clip_is_rectilinear = head->is_rectilinear; - } - else - { - self->current_clip = NULL; - self->clip_is_rectilinear = TRUE; - } -} - -gboolean -ops_has_clip (RenderOpBuilder *self) -{ - return self->clip_stack != NULL && - self->clip_stack->len > 1; -} - -/** - * ops_set_modelview: - * @builder - * @transform: (transfer full): The new modelview transform - * - * This sets the modelview to the given one without looking at the - * one that's currently set */ -void -ops_set_modelview (RenderOpBuilder *builder, - GskTransform *transform) -{ - MatrixStackEntry *entry; - - if (G_UNLIKELY (builder->mv_stack == NULL)) - builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry)); - - g_assert (builder->mv_stack != NULL); - - g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1); - entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1); - - entry->transform = transform; - - entry->metadata.dx_before = builder->dx; - entry->metadata.dy_before = builder->dy; - extract_matrix_metadata (entry->transform, &entry->metadata); - - builder->dx = 0; - builder->dy = 0; - builder->current_modelview = entry->transform; - builder->scale_x = entry->metadata.scale_x; - builder->scale_y = entry->metadata.scale_y; -} - -/* This sets the given modelview to the one we get when multiplying - * the given modelview with the current one. */ -void -ops_push_modelview (RenderOpBuilder *builder, - GskTransform *transform) -{ - MatrixStackEntry *entry; - - if (G_UNLIKELY (builder->mv_stack == NULL)) - builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry)); - - g_assert (builder->mv_stack != NULL); - - g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1); - entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1); - - if (G_LIKELY (builder->mv_stack->len >= 2)) - { - const MatrixStackEntry *cur; - GskTransform *t = NULL; - - cur = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 2); - /* Multiply given matrix with current modelview */ - - t = gsk_transform_translate (gsk_transform_ref (cur->transform), - &(graphene_point_t) { builder->dx, builder->dy}); - t = gsk_transform_transform (t, transform); - entry->transform = t; - } - else - { - entry->transform = gsk_transform_ref (transform); - } - - entry->metadata.dx_before = builder->dx; - entry->metadata.dy_before = builder->dy; - extract_matrix_metadata (entry->transform, &entry->metadata); - - builder->dx = 0; - builder->dy = 0; - builder->scale_x = entry->metadata.scale_x; - builder->scale_y = entry->metadata.scale_y; - builder->current_modelview = entry->transform; -} - -void -ops_pop_modelview (RenderOpBuilder *builder) -{ - const MatrixStackEntry *head; - - g_assert (builder->mv_stack); - g_assert (builder->mv_stack->len >= 1); - - head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1); - builder->dx = head->metadata.dx_before; - builder->dy = head->metadata.dy_before; - gsk_transform_unref (head->transform); - - builder->mv_stack->len --; - head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1); - - if (builder->mv_stack->len >= 1) - { - builder->scale_x = head->metadata.scale_x; - builder->scale_y = head->metadata.scale_y; - builder->current_modelview = head->transform; - } - else - { - builder->current_modelview = NULL; - } -} - -graphene_matrix_t -ops_set_projection (RenderOpBuilder *builder, - const graphene_matrix_t *projection) -{ - graphene_matrix_t prev_mv; - - prev_mv = builder->current_projection; - builder->current_projection = *projection; - - return prev_mv; -} - -graphene_rect_t -ops_set_viewport (RenderOpBuilder *builder, - const graphene_rect_t *viewport) -{ - ProgramState *current_program_state = get_current_program_state (builder); - OpViewport *op; - graphene_rect_t prev_viewport; - - if (rect_equal (&builder->current_viewport, viewport)) - return *viewport; - - op = ops_begin (builder, OP_CHANGE_VIEWPORT); - op->viewport = *viewport; - - if (current_program_state != NULL) - current_program_state->viewport = *viewport; - - prev_viewport = builder->current_viewport; - builder->current_viewport = *viewport; - - return prev_viewport; -} - -void -ops_set_texture (RenderOpBuilder *builder, - int texture_id) -{ - OpTexture *op; - - if (builder->current_texture == texture_id) - return; - - op = ops_begin (builder, OP_CHANGE_SOURCE_TEXTURE); - op->texture_id = texture_id; - builder->current_texture = texture_id; -} - -void -ops_set_extra_texture (RenderOpBuilder *builder, - int texture_id, - int idx) -{ - OpExtraTexture *op; - - op = ops_begin (builder, OP_CHANGE_EXTRA_SOURCE_TEXTURE); - op->texture_id = texture_id; - op->idx = idx; -} - -int -ops_set_render_target (RenderOpBuilder *builder, - int render_target_id) -{ - OpRenderTarget *op; - int prev_render_target; - - if (builder->current_render_target == render_target_id) - return render_target_id; - - prev_render_target = builder->current_render_target; - - if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_RENDER_TARGET))) - op = op_buffer_add (&builder->render_ops, OP_CHANGE_RENDER_TARGET); - - op->render_target_id = render_target_id; - - builder->current_render_target = render_target_id; - - return prev_render_target; -} - -float -ops_set_opacity (RenderOpBuilder *builder, - float opacity) -{ - float prev_opacity; - - if (builder->current_opacity == opacity) - return opacity; - - prev_opacity = builder->current_opacity; - builder->current_opacity = opacity; - - return prev_opacity; -} - -void -ops_set_color (RenderOpBuilder *builder, - const GdkRGBA *color) -{ - ProgramState *current_program_state = get_current_program_state (builder); - OpColor *op; - - if (gdk_rgba_equal (color, ¤t_program_state->color)) - return; - - current_program_state->color = *color; - - op = ops_begin (builder, OP_CHANGE_COLOR); - op->rgba = color; -} - -void -ops_set_gl_shader_args (RenderOpBuilder *builder, - GskGLShader *shader, - float width, - float height, - const guchar *uniform_data) -{ - ProgramState *current_program_state = get_current_program_state (builder); - OpGLShader *op; - gsize args_size = gsk_gl_shader_get_args_size (shader); - - if (current_program_state) - { - if (current_program_state->gl_shader.width == width && - current_program_state->gl_shader.height == height && - current_program_state->gl_shader.uniform_data_len == args_size && - memcmp (current_program_state->gl_shader.uniform_data, uniform_data, args_size) == 0) - return; - - current_program_state->gl_shader.width = width; - current_program_state->gl_shader.height = height; - if (args_size > sizeof (current_program_state->gl_shader.uniform_data)) - current_program_state->gl_shader.uniform_data_len = 0; - else - { - current_program_state->gl_shader.uniform_data_len = args_size; - memcpy (current_program_state->gl_shader.uniform_data, uniform_data, args_size); - } - } - - op = ops_begin (builder, OP_CHANGE_GL_SHADER_ARGS); - op->shader = shader; - op->size[0] = width; - op->size[1] = height; - op->uniform_data = uniform_data; -} - -void -ops_set_color_matrix (RenderOpBuilder *builder, - const graphene_matrix_t *matrix, - const graphene_vec4_t *offset) -{ - ProgramState *current_program_state = get_current_program_state (builder); - const bool offset_equal = graphene_vec4_equal (offset, ¤t_program_state->color_matrix.offset); - const bool matrix_equal = graphene_matrix_equal_fast (matrix, - ¤t_program_state->color_matrix.matrix); - OpColorMatrix *op; - - if (offset_equal && matrix_equal) - return; - - op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX); - - if (!matrix_equal) - { - current_program_state->color_matrix.matrix = *matrix; - op->matrix.value = matrix; - op->matrix.send = TRUE; - } - else - op->matrix.send = FALSE; - - if (!offset_equal) - { - current_program_state->color_matrix.offset = *offset; - op->offset.value = offset; - op->offset.send = TRUE; - } - else - op->offset.send = FALSE; -} - -void -ops_set_border (RenderOpBuilder *builder, - const GskRoundedRect *outline) -{ - ProgramState *current_program_state = get_current_program_state (builder); - OpBorder *op; - - if (memcmp (¤t_program_state->border.outline, - outline, sizeof (GskRoundedRect)) == 0) - return; - - current_program_state->border.outline = *outline; - - op = ops_begin (builder, OP_CHANGE_BORDER); - op->outline = *outline; -} - -void -ops_set_border_width (RenderOpBuilder *builder, - const float *widths) -{ - ProgramState *current_program_state = get_current_program_state (builder); - OpBorder *op; - - g_assert (current_program_state); - - if (memcmp (current_program_state->border.widths, - widths, sizeof (float) * 4) == 0) - return; - - memcpy (¤t_program_state->border.widths, - widths, sizeof (float) * 4); - - op = ops_begin (builder, OP_CHANGE_BORDER_WIDTH); - op->widths[0] = widths[0]; - op->widths[1] = widths[1]; - op->widths[2] = widths[2]; - op->widths[3] = widths[3]; -} - -void -ops_set_border_color (RenderOpBuilder *builder, - const GdkRGBA *color) -{ - ProgramState *current_program_state = get_current_program_state (builder); - OpBorder *op; - - if (gdk_rgba_equal (color, ¤t_program_state->border.color)) - return; - - op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR); - op->color = color; - - current_program_state->border.color = *color; -} - -GskQuadVertex * -ops_draw (RenderOpBuilder *builder, - const GskQuadVertex vertex_data[GL_N_VERTICES]) -{ - ProgramState *program_state = get_current_program_state (builder); - OpDraw *op; - - if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0) - { - OpMatrix *opm; - - opm = ops_begin (builder, OP_CHANGE_PROJECTION); - opm->matrix = builder->current_projection; - program_state->projection = builder->current_projection; - } - - if (program_state->modelview == NULL || - !gsk_transform_equal (builder->current_modelview, program_state->modelview)) - { - OpMatrix *opm; - - opm = ops_begin (builder, OP_CHANGE_MODELVIEW); - gsk_transform_to_matrix (builder->current_modelview, &opm->matrix); - gsk_transform_unref (program_state->modelview); - program_state->modelview = gsk_transform_ref (builder->current_modelview); - } - - if (!rect_equal (&builder->current_viewport, &program_state->viewport)) - { - OpViewport *opv; - - opv = ops_begin (builder, OP_CHANGE_VIEWPORT); - opv->viewport = builder->current_viewport; - program_state->viewport = builder->current_viewport; - } - - if (!rounded_rect_equal (builder->current_clip, &program_state->clip)) - { - OpClip *opc; - - opc = ops_begin (builder, OP_CHANGE_CLIP); - opc->clip = *builder->current_clip; - opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip); - program_state->clip = *builder->current_clip; - } - - if (program_state->opacity != builder->current_opacity) - { - OpOpacity *opo; - - opo = ops_begin (builder, OP_CHANGE_OPACITY); - opo->opacity = builder->current_opacity; - program_state->opacity = builder->current_opacity; - } - - /* TODO: Did the additions above break the following optimization? */ - if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW))) - { - op->vao_size += GL_N_VERTICES; - } - else - { - op = op_buffer_add (&builder->render_ops, OP_DRAW); - op->vao_offset = builder->vertices->len; - op->vao_size = GL_N_VERTICES; - } - - if (vertex_data) - { - g_array_append_vals (builder->vertices, vertex_data, GL_N_VERTICES); - return NULL; /* Better not use this on the caller side */ - } - - g_array_set_size (builder->vertices, builder->vertices->len + GL_N_VERTICES); - return &g_array_index (builder->vertices, GskQuadVertex, builder->vertices->len - GL_N_VERTICES); -} - -/* The offset is only valid for the current modelview. - * Setting a new modelview will add the offset to that matrix - * and reset the internal offset to 0. */ -void -ops_offset (RenderOpBuilder *builder, - float x, - float y) -{ - builder->dx += x; - builder->dy += y; -} - -gpointer -ops_begin (RenderOpBuilder *builder, - OpKind kind) -{ - return op_buffer_add (&builder->render_ops, kind); -} - -void -ops_reset (RenderOpBuilder *builder) -{ - op_buffer_clear (&builder->render_ops); - g_array_set_size (builder->vertices, 0); -} - -OpBuffer * -ops_get_buffer (RenderOpBuilder *builder) -{ - return &builder->render_ops; -} - -void -ops_set_inset_shadow (RenderOpBuilder *self, - const GskRoundedRect outline, - float spread, - const GdkRGBA *color, - float dx, - float dy) -{ - ProgramState *current_program_state = get_current_program_state (self); - OpShadow *op; - - g_assert (current_program_state); - - op = ops_begin (self, OP_CHANGE_INSET_SHADOW); - - if (!rounded_rect_equal (&outline, ¤t_program_state->inset_shadow.outline)) - { - op->outline.value = outline; - op->outline.send = TRUE; - op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->inset_shadow.outline, - &outline); - current_program_state->inset_shadow.outline = outline; - } - else - op->outline.send = FALSE; - - if (spread != current_program_state->inset_shadow.spread) - { - op->spread.value = spread; - op->spread.send = TRUE; - - current_program_state->inset_shadow.spread = spread; - } - else - op->spread.send = FALSE; - - if (!gdk_rgba_equal (color, ¤t_program_state->inset_shadow.color)) - { - op->color.value = color; - op->color.send = TRUE; - - current_program_state->inset_shadow.color = *color; - } - else - op->color.send = FALSE; - - if (dx != current_program_state->inset_shadow.dx || - dy != current_program_state->inset_shadow.dy) - { - op->offset.value[0] = dx; - op->offset.value[1] = dy; - op->offset.send = TRUE; - - current_program_state->inset_shadow.dx = dx; - current_program_state->inset_shadow.dy = dy; - } - else - op->offset.send = FALSE; - - if (!op->outline.send && - !op->spread.send && - !op->offset.send && - !op->color.send) - { - op_buffer_pop_tail (&self->render_ops); - } -} -void -ops_set_unblurred_outset_shadow (RenderOpBuilder *self, - const GskRoundedRect outline, - float spread, - const GdkRGBA *color, - float dx, - float dy) -{ - ProgramState *current_program_state = get_current_program_state (self); - OpShadow *op; - - g_assert (current_program_state); - - op = ops_begin (self, OP_CHANGE_UNBLURRED_OUTSET_SHADOW); - - if (!rounded_rect_equal (&outline, ¤t_program_state->unblurred_outset_shadow.outline)) - { - op->outline.value = outline; - op->outline.send = TRUE; - op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->unblurred_outset_shadow.outline, - &outline); - current_program_state->unblurred_outset_shadow.outline = outline; - } - else - op->outline.send = FALSE; - - if (spread != current_program_state->unblurred_outset_shadow.spread) - { - op->spread.value = spread; - op->spread.send = TRUE; - - current_program_state->unblurred_outset_shadow.spread = spread; - } - else - op->spread.send = FALSE; - - if (!gdk_rgba_equal (color, ¤t_program_state->unblurred_outset_shadow.color)) - { - op->color.value = color; - op->color.send = TRUE; - - current_program_state->unblurred_outset_shadow.color = *color; - } - else - op->color.send = FALSE; - - if (dx != current_program_state->unblurred_outset_shadow.dx || - dy != current_program_state->unblurred_outset_shadow.dy) - { - op->offset.value[0] = dx; - op->offset.value[1] = dy; - op->offset.send = TRUE; - - current_program_state->unblurred_outset_shadow.dx = dx; - current_program_state->unblurred_outset_shadow.dy = dy; - } - else - op->offset.send = FALSE; -} - -void -ops_set_linear_gradient (RenderOpBuilder *self, - guint n_color_stops, - const GskColorStop *color_stops, - gboolean repeat, - float start_x, - float start_y, - float end_x, - float end_y) -{ - ProgramState *current_program_state = get_current_program_state (self); - OpLinearGradient *op; - const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops); - - g_assert (current_program_state); - - op = ops_begin (self, OP_CHANGE_LINEAR_GRADIENT); - - /* We always save the n_color_stops value in the op so the renderer can use it in - * cases where we send the color stops, but not n_color_stops */ - op->n_color_stops.value = real_n_color_stops; - if (current_program_state->linear_gradient.n_color_stops != real_n_color_stops) - { - op->n_color_stops.send = TRUE; - current_program_state->linear_gradient.n_color_stops = real_n_color_stops; - } - else - op->n_color_stops.send = FALSE; - - op->color_stops.send = FALSE; - if (!op->n_color_stops.send) - { - g_assert (current_program_state->linear_gradient.n_color_stops == real_n_color_stops); - - for (guint i = 0; i < real_n_color_stops; i ++) - { - const GskColorStop *s1 = &color_stops[i]; - const GskColorStop *s2 = ¤t_program_state->linear_gradient.color_stops[i]; - - if (s1->offset != s2->offset || - !gdk_rgba_equal (&s1->color, &s2->color)) - { - op->color_stops.send = TRUE; - break; - } - } - } - else - op->color_stops.send = TRUE; - - if (op->color_stops.send) - { - op->color_stops.value = color_stops; - memcpy (¤t_program_state->linear_gradient.color_stops, - color_stops, - sizeof (GskColorStop) * real_n_color_stops); - } - - op->repeat = repeat; - op->start_point[0] = start_x; - op->start_point[1] = start_y; - op->end_point[0] = end_x; - op->end_point[1] = end_y; -} - -void -ops_set_radial_gradient (RenderOpBuilder *self, - guint n_color_stops, - const GskColorStop *color_stops, - gboolean repeat, - float center_x, - float center_y, - float start, - float end, - float hradius, - float vradius) -{ - const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops); - OpRadialGradient *op; - - /* TODO: State tracking? */ - - op = ops_begin (self, OP_CHANGE_RADIAL_GRADIENT); - op->n_color_stops.value = real_n_color_stops; - op->n_color_stops.send = true; - op->color_stops.value = color_stops; - op->color_stops.send = true; - op->center[0] = center_x; - op->center[1] = center_y; - op->radius[0] = hradius; - op->radius[1] = vradius; - op->start = start; - op->end = end; - op->repeat = repeat; -} - -void -ops_set_conic_gradient (RenderOpBuilder *self, - guint n_color_stops, - const GskColorStop *color_stops, - float center_x, - float center_y, - float angle) -{ - const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops); - OpConicGradient *op; - - /* TODO: State tracking? */ - - op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT); - op->n_color_stops.value = real_n_color_stops; - op->n_color_stops.send = true; - op->color_stops.value = color_stops; - op->color_stops.send = true; - op->center[0] = center_x; - op->center[1] = center_y; - op->angle = angle; -} - diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h deleted file mode 100644 index 551a1afc05..0000000000 --- a/gsk/gl/gskglrenderopsprivate.h +++ /dev/null @@ -1,353 +0,0 @@ -#ifndef __GSK_GL_RENDER_OPS_H__ -#define __GSK_GL_RENDER_OPS_H__ - -#include -#include -#include - -#include "gskgldriverprivate.h" -#include "gskroundedrectprivate.h" -#include "gskglrenderer.h" -#include "gskrendernodeprivate.h" - -#include "opbuffer.h" - -#define GL_N_VERTICES 6 -#define GL_N_PROGRAMS 15 -#define GL_MAX_GRADIENT_STOPS 6 - -typedef struct -{ - float scale_x; - float scale_y; - - float dx_before; - float dy_before; -} OpsMatrixMetadata; - -typedef struct -{ - GskTransform *transform; - OpsMatrixMetadata metadata; -} MatrixStackEntry; - -typedef struct -{ - GskTransform *modelview; - GskRoundedRect clip; - graphene_matrix_t projection; - int source_texture; - graphene_rect_t viewport; - float opacity; - /* Per-program state */ - union { - GdkRGBA color; - struct { - graphene_matrix_t matrix; - graphene_vec4_t offset; - } color_matrix; - struct { - float widths[4]; - GdkRGBA color; - GskRoundedRect outline; - } border; - struct { - GskRoundedRect outline; - float dx; - float dy; - float spread; - GdkRGBA color; - } inset_shadow; - struct { - GskRoundedRect outline; - float dx; - float dy; - float spread; - GdkRGBA color; - } unblurred_outset_shadow; - struct { - int n_color_stops; - GskColorStop color_stops[GL_MAX_GRADIENT_STOPS]; - float start_point[2]; - float end_point[2]; - } linear_gradient; - struct { - int n_color_stops; - GskColorStop color_stops[GL_MAX_GRADIENT_STOPS]; - float center[2]; - float start; - float end; - float radius[2]; /* h/v */ - } radial_gradient; - struct { - float width; - float height; - int uniform_data_len; - guchar uniform_data[32]; - } gl_shader; - }; -} ProgramState; - -struct _Program -{ - const char *name; - - int index; /* Into the renderer's program array -1 for custom */ - - int id; - /* Common locations (gl_common)*/ - int source_location; - int position_location; - int uv_location; - int alpha_location; - int viewport_location; - int projection_location; - int modelview_location; - int clip_rect_location; - union { - struct { - int color_location; - } color; - struct { - int color_location; - } coloring; - struct { - int color_matrix_location; - int color_offset_location; - } color_matrix; - struct { - int num_color_stops_location; - int color_stops_location; - int points_location; - int repeat_location; - } linear_gradient; - struct { - int num_color_stops_location; - int color_stops_location; - int geometry_location; - int range_location; - int repeat_location; - } radial_gradient; - struct { - int num_color_stops_location; - int color_stops_location; - int geometry_location; - } conic_gradient; - struct { - int blur_radius_location; - int blur_size_location; - int blur_dir_location; - } blur; - struct { - int color_location; - int spread_location; - int offset_location; - int outline_rect_location; - } inset_shadow; - struct { - int color_location; - int outline_rect_location; - } outset_shadow; - struct { - int outline_rect_location; - int color_location; - int spread_location; - int offset_location; - } unblurred_outset_shadow; - struct { - int color_location; - int widths_location; - int outline_rect_location; - } border; - struct { - int source2_location; - int progress_location; - } cross_fade; - struct { - int source2_location; - int mode_location; - } blend; - struct { - int child_bounds_location; - int texture_rect_location; - } repeat; - struct { - int size_location; - int args_locations[8]; - int texture_locations[4]; - GError *compile_error; - } glshader; - }; - ProgramState state; -}; - -typedef struct { - int ref_count; - union { - Program programs[GL_N_PROGRAMS]; - struct { - Program blend_program; - Program blit_program; - Program blur_program; - Program border_program; - Program color_matrix_program; - Program color_program; - Program coloring_program; - Program cross_fade_program; - Program inset_shadow_program; - Program linear_gradient_program; - Program radial_gradient_program; - Program conic_gradient_program; - Program outset_shadow_program; - Program repeat_program; - Program unblurred_outset_shadow_program; - }; - }; - GHashTable *custom_programs; /* GskGLShader -> Program* */ -} GskGLRendererPrograms; - -typedef struct -{ - GskGLRendererPrograms *programs; - Program *current_program; - int current_render_target; - int current_texture; - - graphene_matrix_t current_projection; - graphene_rect_t current_viewport; - float current_opacity; - float dx, dy; - float scale_x, scale_y; - - OpBuffer render_ops; - GArray *vertices; - - GskGLRenderer *renderer; - - /* Stack of modelview matrices */ - GArray *mv_stack; - GskTransform *current_modelview; - - /* Same thing */ - GArray *clip_stack; - /* Pointer into clip_stack */ - const GskRoundedRect *current_clip; - bool clip_is_rectilinear; -} RenderOpBuilder; - - -void ops_dump_framebuffer (RenderOpBuilder *builder, - const char *filename, - int width, - int height); -void ops_init (RenderOpBuilder *builder); -void ops_free (RenderOpBuilder *builder); -void ops_reset (RenderOpBuilder *builder); -void ops_push_debug_group (RenderOpBuilder *builder, - const char *text); -void ops_pop_debug_group (RenderOpBuilder *builder); - -void ops_finish (RenderOpBuilder *builder); -void ops_push_modelview (RenderOpBuilder *builder, - GskTransform *transform); -void ops_set_modelview (RenderOpBuilder *builder, - GskTransform *transform); -void ops_pop_modelview (RenderOpBuilder *builder); -void ops_set_program (RenderOpBuilder *builder, - Program *program); - -void ops_push_clip (RenderOpBuilder *builder, - const GskRoundedRect *clip); -void ops_pop_clip (RenderOpBuilder *builder); -gboolean ops_has_clip (RenderOpBuilder *builder); - -void ops_transform_bounds_modelview (const RenderOpBuilder *builder, - const graphene_rect_t *src, - graphene_rect_t *dst); - -graphene_matrix_t ops_set_projection (RenderOpBuilder *builder, - const graphene_matrix_t *projection); - -graphene_rect_t ops_set_viewport (RenderOpBuilder *builder, - const graphene_rect_t *viewport); - -void ops_set_texture (RenderOpBuilder *builder, - int texture_id); -void ops_set_extra_texture (RenderOpBuilder *builder, - int texture_id, - int idx); - -int ops_set_render_target (RenderOpBuilder *builder, - int render_target_id); - -float ops_set_opacity (RenderOpBuilder *builder, - float opacity); -void ops_set_color (RenderOpBuilder *builder, - const GdkRGBA *color); - -void ops_set_color_matrix (RenderOpBuilder *builder, - const graphene_matrix_t *matrix, - const graphene_vec4_t *offset); - -void ops_set_border (RenderOpBuilder *builder, - const GskRoundedRect *outline); -void ops_set_border_width (RenderOpBuilder *builder, - const float *widths); - -void ops_set_border_color (RenderOpBuilder *builder, - const GdkRGBA *color); -void ops_set_inset_shadow (RenderOpBuilder *self, - const GskRoundedRect outline, - float spread, - const GdkRGBA *color, - float dx, - float dy); -void ops_set_gl_shader_args (RenderOpBuilder *builder, - GskGLShader *shader, - float width, - float height, - const guchar *uniform_data); -void ops_set_unblurred_outset_shadow (RenderOpBuilder *self, - const GskRoundedRect outline, - float spread, - const GdkRGBA *color, - float dx, - float dy); - -void ops_set_linear_gradient (RenderOpBuilder *self, - guint n_color_stops, - const GskColorStop *color_stops, - gboolean repeat, - float start_x, - float start_y, - float end_x, - float end_y); -void ops_set_radial_gradient (RenderOpBuilder *self, - guint n_color_stops, - const GskColorStop *color_stops, - gboolean repeat, - float center_x, - float center_y, - float start, - float end, - float hradius, - float vradius); -void ops_set_conic_gradient (RenderOpBuilder *self, - guint n_color_stops, - const GskColorStop *color_stops, - float center_x, - float center_y, - float angle); - -GskQuadVertex * ops_draw (RenderOpBuilder *builder, - const GskQuadVertex vertex_data[GL_N_VERTICES]); - -void ops_offset (RenderOpBuilder *builder, - float x, - float y); - -gpointer ops_begin (RenderOpBuilder *builder, - OpKind kind); -OpBuffer *ops_get_buffer (RenderOpBuilder *builder); - -#endif diff --git a/gsk/gl/gskglshaderbuilder.c b/gsk/gl/gskglshaderbuilder.c deleted file mode 100644 index d16ad4feb5..0000000000 --- a/gsk/gl/gskglshaderbuilder.c +++ /dev/null @@ -1,271 +0,0 @@ -#include "config.h" - -#include "gskglshaderbuilderprivate.h" - -#include "gskdebugprivate.h" - -#include -#include - -void -gsk_gl_shader_builder_init (GskGLShaderBuilder *self, - const char *common_preamble_resource_path, - const char *vs_preamble_resource_path, - const char *fs_preamble_resource_path) -{ - memset (self, 0, sizeof (*self)); - - self->preamble = g_resources_lookup_data (common_preamble_resource_path, 0, NULL); - self->vs_preamble = g_resources_lookup_data (vs_preamble_resource_path, 0, NULL); - self->fs_preamble = g_resources_lookup_data (fs_preamble_resource_path, 0, NULL); - - g_assert (self->preamble); - g_assert (self->vs_preamble); - g_assert (self->fs_preamble); -} - -void -gsk_gl_shader_builder_finish (GskGLShaderBuilder *self) -{ - g_bytes_unref (self->preamble); - g_bytes_unref (self->vs_preamble); - g_bytes_unref (self->fs_preamble); -} - -void -gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self, - int version) -{ - self->version = version; -} - -static void -prepend_line_numbers (char *code, - GString *s) -{ - char *p; - int line; - - p = code; - line = 1; - while (*p) - { - char *end = strchr (p, '\n'); - if (end) - end = end + 1; /* Include newline */ - else - end = p + strlen (p); - - g_string_append_printf (s, "%3d| ", line++); - g_string_append_len (s, p, end - p); - - p = end; - } -} - -static gboolean -check_shader_error (int shader_id, - int shader_type, - const char *resource_path, - GError **error) -{ - int status; - int log_len; - char *buffer; - int code_len; - char *code; - GString *s; - - glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status); - - if (G_LIKELY (status == GL_TRUE)) - return TRUE; - - glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len); - buffer = g_malloc0 (log_len + 1); - glGetShaderInfoLog (shader_id, log_len, NULL, buffer); - - glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len); - code = g_malloc0 (code_len + 1); - glGetShaderSource (shader_id, code_len, NULL, code); - - s = g_string_new (""); - prepend_line_numbers (code, s); - - g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED, - "Compilation failure in %s shader %s.\nSource Code:\n%s\n\nError Message:\n%s\n\n", - (shader_type == GL_FRAGMENT_SHADER ? "fragment" : "vertex"), - resource_path, - s->str, - buffer); - - g_string_free (s, TRUE); - g_free (buffer); - g_free (code); - - return FALSE; -} - -static void -print_shader_info (const char *prefix, - int shader_id, - const char *resource_path) -{ - if (GSK_DEBUG_CHECK(SHADERS)) - { - int code_len; - char *code; - GString *s; - - glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len); - code = g_malloc0 (code_len + 1); - glGetShaderSource (shader_id, code_len, NULL, code); - - s = g_string_new (""); - prepend_line_numbers (code, s); - - g_message ("%s %d, %s:\n%s", prefix, shader_id, resource_path, s->str); - g_string_free (s, TRUE); - g_free (code); - } -} - -int -gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self, - const char *resource_path, - const char *extra_fragment_snippet, - gsize extra_fragment_length, - GError **error) -{ - - GBytes *source_bytes = g_resources_lookup_data (resource_path, 0, NULL); - char version_buffer[64]; - const char *source; - const char *vertex_shader_start; - const char *fragment_shader_start; - int vertex_id; - int fragment_id; - int program_id = -1; - int status; - - g_assert (source_bytes); - - source = g_bytes_get_data (source_bytes, NULL); - vertex_shader_start = strstr (source, "VERTEX_SHADER"); - fragment_shader_start = strstr (source, "FRAGMENT_SHADER"); - - g_assert (vertex_shader_start); - g_assert (fragment_shader_start); - - /* They both start at the next newline */ - vertex_shader_start = strstr (vertex_shader_start, "\n"); - fragment_shader_start = strstr (fragment_shader_start, "\n"); - - g_snprintf (version_buffer, sizeof (version_buffer), - "#version %d\n", self->version); - - vertex_id = glCreateShader (GL_VERTEX_SHADER); - glShaderSource (vertex_id, 8, - (const char *[]) { - version_buffer, - self->debugging ? "#define GSK_DEBUG 1\n" : "", - self->legacy ? "#define GSK_LEGACY 1\n" : "", - self->gl3 ? "#define GSK_GL3 1\n" : "", - self->gles ? "#define GSK_GLES 1\n" : "", - g_bytes_get_data (self->preamble, NULL), - g_bytes_get_data (self->vs_preamble, NULL), - vertex_shader_start - }, - (int[]) { - -1, - -1, - -1, - -1, - -1, - -1, - -1, - fragment_shader_start - vertex_shader_start - }); - glCompileShader (vertex_id); - - if (!check_shader_error (vertex_id, GL_VERTEX_SHADER, resource_path, error)) - { - glDeleteShader (vertex_id); - goto out; - } - - print_shader_info ("Vertex shader", vertex_id, resource_path); - - fragment_id = glCreateShader (GL_FRAGMENT_SHADER); - glShaderSource (fragment_id, 9, - (const char *[]) { - version_buffer, - self->debugging ? "#define GSK_DEBUG 1\n" : "", - self->legacy ? "#define GSK_LEGACY 1\n" : "", - self->gl3 ? "#define GSK_GL3 1\n" : "", - self->gles ? "#define GSK_GLES 1\n" : "", - g_bytes_get_data (self->preamble, NULL), - g_bytes_get_data (self->fs_preamble, NULL), - fragment_shader_start, - extra_fragment_snippet ? extra_fragment_snippet : "" - }, - (int[]) { - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - extra_fragment_length, - }); - glCompileShader (fragment_id); - - if (!check_shader_error (fragment_id, GL_FRAGMENT_SHADER, resource_path, error)) - { - glDeleteShader (fragment_id); - goto out; - } - - print_shader_info ("Fragment shader", fragment_id, resource_path); - - program_id = glCreateProgram (); - glAttachShader (program_id, vertex_id); - glAttachShader (program_id, fragment_id); - glBindAttribLocation (program_id, 0, "aPosition"); - glBindAttribLocation (program_id, 1, "aUv"); - glLinkProgram (program_id); - glDetachShader (program_id, vertex_id); - glDetachShader (program_id, fragment_id); - - glGetProgramiv (program_id, GL_LINK_STATUS, &status); - if (status == GL_FALSE) - { - char *buffer = NULL; - int log_len = 0; - - glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len); - - buffer = g_malloc0 (log_len + 1); - glGetProgramInfoLog (program_id, log_len, NULL, buffer); - - g_warning ("Linking failure in shader:\n%s", buffer); - g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED, - "Linking failure in shader: %s", buffer); - - g_free (buffer); - - glDeleteProgram (program_id); - program_id = -1; - } - - glDeleteShader (vertex_id); - glDeleteShader (fragment_id); - -out: - g_bytes_unref (source_bytes); - - return program_id; -} - diff --git a/gsk/gl/gskglshaderbuilderprivate.h b/gsk/gl/gskglshaderbuilderprivate.h deleted file mode 100644 index 870df050d0..0000000000 --- a/gsk/gl/gskglshaderbuilderprivate.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __GSK_SHADER_BUILDER_PRIVATE_H__ -#define __GSK_SHADER_BUILDER_PRIVATE_H__ - -#include -#include - -G_BEGIN_DECLS - -typedef struct -{ - GBytes *preamble; - GBytes *vs_preamble; - GBytes *fs_preamble; - - int version; - - guint debugging: 1; - guint gles: 1; - guint gl3: 1; - guint legacy: 1; - -} GskGLShaderBuilder; - - -void gsk_gl_shader_builder_init (GskGLShaderBuilder *self, - const char *common_preamble_resource_path, - const char *vs_preamble_resource_path, - const char *fs_preamble_resource_path); -void gsk_gl_shader_builder_finish (GskGLShaderBuilder *self); - -void gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self, - int version); - -int gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self, - const char *resource_path, - const char *extra_fragment_snippet, - gsize extra_fragment_length, - GError **error); - -G_END_DECLS - -#endif /* __GSK_SHADER_BUILDER_PRIVATE_H__ */ diff --git a/gsk/gl/gskglshadowcache.c b/gsk/gl/gskglshadowcache.c deleted file mode 100644 index a0eeef904d..0000000000 --- a/gsk/gl/gskglshadowcache.c +++ /dev/null @@ -1,141 +0,0 @@ - -#include "gskglshadowcacheprivate.h" - -#define MAX_UNUSED_FRAMES (16 * 5) - -typedef struct -{ - GskRoundedRect outline; - float blur_radius; -} CacheKey; - -typedef struct -{ - GskRoundedRect outline; - float blur_radius; - - int texture_id; - int unused_frames; -} CacheItem; - -static gboolean -key_equal (const void *x, - const void *y) -{ - const CacheKey *a = x; - const CacheKey *b = y; - - return a->blur_radius == b->blur_radius && - graphene_size_equal (&a->outline.corner[0], &b->outline.corner[0]) && - graphene_size_equal (&a->outline.corner[1], &b->outline.corner[1]) && - graphene_size_equal (&a->outline.corner[2], &b->outline.corner[2]) && - graphene_size_equal (&a->outline.corner[3], &b->outline.corner[3]) && - graphene_rect_equal (&a->outline.bounds, &b->outline.bounds); -} - -void -gsk_gl_shadow_cache_init (GskGLShadowCache *self) -{ - self->textures = g_array_new (FALSE, TRUE, sizeof (CacheItem)); -} - -void -gsk_gl_shadow_cache_free (GskGLShadowCache *self, - GskGLDriver *gl_driver) -{ - guint i, p; - - for (i = 0, p = self->textures->len; i < p; i ++) - { - const CacheItem *item = &g_array_index (self->textures, CacheItem, i); - - gsk_gl_driver_destroy_texture (gl_driver, item->texture_id); - } - - g_array_free (self->textures, TRUE); - self->textures = NULL; -} - -void -gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self, - GskGLDriver *gl_driver) -{ - guint i, p; - - for (i = 0, p = self->textures->len; i < p; i ++) - { - CacheItem *item = &g_array_index (self->textures, CacheItem, i); - - if (item->unused_frames > MAX_UNUSED_FRAMES) - { - gsk_gl_driver_destroy_texture (gl_driver, item->texture_id); - g_array_remove_index_fast (self->textures, i); - p --; - i --; - } - else - { - item->unused_frames ++; - } - } -} - -/* XXX - * The offset origin should always be at 0/0, or the blur radius should just go - * away since it defines the origin position anyway? - */ -int -gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self, - GskGLDriver *gl_driver, - const GskRoundedRect *shadow_rect, - float blur_radius) -{ - CacheItem *item= NULL; - guint i; - - g_assert (self != NULL); - g_assert (gl_driver != NULL); - g_assert (shadow_rect != NULL); - - for (i = 0; i < self->textures->len; i ++) - { - CacheItem *k = &g_array_index (self->textures, CacheItem, i); - - if (key_equal (&(CacheKey){*shadow_rect, blur_radius}, - &(CacheKey){k->outline, k->blur_radius})) - { - item = k; - break; - } - } - - if (item == NULL) - return 0; - - item->unused_frames = 0; - - g_assert (item->texture_id != 0); - - return item->texture_id; -} - -void -gsk_gl_shadow_cache_commit (GskGLShadowCache *self, - const GskRoundedRect *shadow_rect, - float blur_radius, - int texture_id) -{ - CacheItem *item; - - g_assert (self != NULL); - g_assert (shadow_rect != NULL); - g_assert (texture_id > 0); - - g_array_set_size (self->textures, self->textures->len + 1); - item = &g_array_index (self->textures, CacheItem, self->textures->len - 1); - - item->outline = *shadow_rect; - item->blur_radius = blur_radius; - item->unused_frames = 0; - item->texture_id = texture_id; -} diff --git a/gsk/gl/gskglshadowcacheprivate.h b/gsk/gl/gskglshadowcacheprivate.h deleted file mode 100644 index 6623f16235..0000000000 --- a/gsk/gl/gskglshadowcacheprivate.h +++ /dev/null @@ -1,31 +0,0 @@ - - -#ifndef __GSK_GL_SHADOW_CACHE_H__ -#define __GSK_GL_SHADOW_CACHE_H__ - -#include -#include "gskgldriverprivate.h" -#include "gskroundedrect.h" - -typedef struct -{ - GArray *textures; -} GskGLShadowCache; - - -void gsk_gl_shadow_cache_init (GskGLShadowCache *self); -void gsk_gl_shadow_cache_free (GskGLShadowCache *self, - GskGLDriver *gl_driver); -void gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self, - GskGLDriver *gl_driver); -int gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self, - GskGLDriver *gl_driver, - const GskRoundedRect *shadow_rect, - float blur_radius); -void gsk_gl_shadow_cache_commit (GskGLShadowCache *self, - const GskRoundedRect *shadow_rect, - float blur_radius, - int texture_id); - - -#endif diff --git a/gsk/gl/gskgltextureatlas.c b/gsk/gl/gskgltextureatlas.c deleted file mode 100644 index 9db2d8e262..0000000000 --- a/gsk/gl/gskgltextureatlas.c +++ /dev/null @@ -1,309 +0,0 @@ - -#include "config.h" -#include "gskgltextureatlasprivate.h" -#include "gskdebugprivate.h" -#include "gdkglcontextprivate.h" -#include - -#define ATLAS_SIZE (512) -#define MAX_OLD_RATIO 0.5 - -static void -free_atlas (gpointer v) -{ - GskGLTextureAtlas *atlas = v; - - gsk_gl_texture_atlas_free (atlas); - - g_free (atlas); -} - -GskGLTextureAtlases * -gsk_gl_texture_atlases_new (void) -{ - GskGLTextureAtlases *self; - - self = g_new (GskGLTextureAtlases, 1); - self->atlases = g_ptr_array_new_with_free_func (free_atlas); - - self->ref_count = 1; - - return self; -} - -GskGLTextureAtlases * -gsk_gl_texture_atlases_ref (GskGLTextureAtlases *self) -{ - self->ref_count++; - - return self; -} - -void -gsk_gl_texture_atlases_unref (GskGLTextureAtlases *self) -{ - g_assert (self->ref_count > 0); - - if (self->ref_count == 1) - { - g_ptr_array_unref (self->atlases); - g_free (self); - return; - } - - self->ref_count--; -} - -#if 0 -static void -write_atlas_to_png (GskGLTextureAtlas *atlas, - const char *filename) -{ - int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width); - guchar *data = g_malloc (atlas->height * stride); - cairo_surface_t *s; - - glBindTexture (GL_TEXTURE_2D, atlas->texture_id); - glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); - s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride); - cairo_surface_write_to_png (s, filename); - - cairo_surface_destroy (s); - g_free (data); -} -#endif - -void -gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *self, - GPtrArray *removed) -{ - int i; - - for (i = self->atlases->len - 1; i >= 0; i--) - { - GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i); - - if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO) - { - GSK_NOTE(GLYPH_CACHE, - g_message ("Dropping atlas %d (%g.2%% old)", i, - 100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas))); - - if (atlas->texture_id != 0) - { - glDeleteTextures (1, &atlas->texture_id); - atlas->texture_id = 0; - } - - g_ptr_array_add (removed, atlas); - g_ptr_array_remove_index (self->atlases, i); - } - } - - GSK_NOTE(GLYPH_CACHE, { - static guint timestamp; - if (timestamp++ % 60 == 0) - g_message ("%d atlases", self->atlases->len); - }); - - -#if 0 - { - static guint timestamp; - - timestamp++; - if (timestamp % 10 == 0) - for (i = 0; i < self->atlases->len; i++) - { - GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i); - - if (atlas->texture_id) - { - char *filename; - - filename = g_strdup_printf ("textureatlas%d-%u.png", i, timestamp); - write_atlas_to_png (atlas, filename); - g_free (filename); - } - } - } -#endif -} - -gboolean -gsk_gl_texture_atlases_pack (GskGLTextureAtlases *self, - int width, - int height, - GskGLTextureAtlas **atlas_out, - int *out_x, - int *out_y) -{ - GskGLTextureAtlas *atlas; - int x, y; - int i; - - g_assert (width < ATLAS_SIZE); - g_assert (height < ATLAS_SIZE); - - atlas = NULL; - - for (i = 0; i < self->atlases->len; i++) - { - atlas = g_ptr_array_index (self->atlases, i); - - if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y)) - break; - - atlas = NULL; - } - - if (atlas == NULL) - { - /* No atlas has enough space, so create a new one... */ - atlas = g_malloc (sizeof (GskGLTextureAtlas)); - gsk_gl_texture_atlas_init (atlas, ATLAS_SIZE, ATLAS_SIZE); - gsk_gl_texture_atlas_realize (atlas); - g_ptr_array_add (self->atlases, atlas); - - /* Pack it onto that one, which surely has enough space... */ - if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y)) - g_assert_not_reached (); - - GSK_NOTE(GLYPH_CACHE, g_message ("adding new atlas")); - } - - *atlas_out = atlas; - *out_x = x; - *out_y = y; - - return TRUE; -} - -void -gsk_gl_texture_atlas_init (GskGLTextureAtlas *self, - int width, - int height) -{ - memset (self, 0, sizeof (*self)); - - self->texture_id = 0; - self->width = width; - self->height = height; - - /* TODO: We might want to change the strategy about the amount of - * nodes here? stb_rect_pack.h says with is optimal. */ - self->nodes = g_malloc0 (sizeof (struct stbrp_node) * width); - stbrp_init_target (&self->context, - width, height, - self->nodes, - width); - - gsk_gl_texture_atlas_realize (self); -} - -void -gsk_gl_texture_atlas_free (GskGLTextureAtlas *self) -{ - if (self->texture_id != 0) - { - glDeleteTextures (1, &self->texture_id); - self->texture_id = 0; - } - - g_clear_pointer (&self->nodes, g_free); -} - -void -gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self, - int width, - int height) -{ - self->unused_pixels += (width * height); -} - - -void -gsk_gl_texture_atlas_mark_used (GskGLTextureAtlas *self, - int width, - int height) -{ - self->unused_pixels -= (width * height); - - g_assert (self->unused_pixels >= 0); -} - -gboolean -gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self, - int width, - int height, - int *out_x, - int *out_y) -{ - stbrp_rect rect; - - g_assert (out_x); - g_assert (out_y); - - rect.w = width; - rect.h = height; - - stbrp_pack_rects (&self->context, &rect, 1); - - if (rect.was_packed) - { - *out_x = rect.x; - *out_y = rect.y; - } - - return rect.was_packed; -} - -double -gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self) -{ - if (self->unused_pixels > 0) - return (double)(self->unused_pixels) / (double)(self->width * self->height); - - return 0.0; -} - -/* Not using gdk_gl_driver_create_texture here, since we want - * this texture to survive the driver and stay around until - * the display gets closed. - */ -static guint -create_shared_texture (int width, - int height) -{ - guint texture_id; - - glGenTextures (1, &texture_id); - glBindTexture (GL_TEXTURE_2D, texture_id); - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - else - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - - glBindTexture (GL_TEXTURE_2D, 0); - - return texture_id; -} - -void -gsk_gl_texture_atlas_realize (GskGLTextureAtlas *atlas) -{ - if (atlas->texture_id) - return; - - atlas->texture_id = create_shared_texture (atlas->width, atlas->height); - gdk_gl_context_label_object_printf (gdk_gl_context_get_current (), - GL_TEXTURE, atlas->texture_id, - "Texture atlas %d", atlas->texture_id); -} diff --git a/gsk/gl/gskgltextureatlasprivate.h b/gsk/gl/gskgltextureatlasprivate.h deleted file mode 100644 index fc4f0365c0..0000000000 --- a/gsk/gl/gskgltextureatlasprivate.h +++ /dev/null @@ -1,72 +0,0 @@ - -#ifndef __GSK_GL_TEXTURE_ATLAS_H__ -#define __GSK_GL_TEXTURE_ATLAS_H__ - -#include "stb_rect_pack.h" -#include "gskglimageprivate.h" -#include "gskgldriverprivate.h" - -struct _GskGLTextureAtlas -{ - struct stbrp_context context; - struct stbrp_node *nodes; - - int width; - int height; - - guint texture_id; - - int unused_pixels; /* Pixels of rects that have been used at some point, - But are now unused. */ - - void *user_data; -}; -typedef struct _GskGLTextureAtlas GskGLTextureAtlas; - -struct _GskGLTextureAtlases -{ - int ref_count; - - GPtrArray *atlases; -}; -typedef struct _GskGLTextureAtlases GskGLTextureAtlases; - -GskGLTextureAtlases *gsk_gl_texture_atlases_new (void); -GskGLTextureAtlases *gsk_gl_texture_atlases_ref (GskGLTextureAtlases *atlases); -void gsk_gl_texture_atlases_unref (GskGLTextureAtlases *atlases); - -void gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *atlases, - GPtrArray *removed); -gboolean gsk_gl_texture_atlases_pack (GskGLTextureAtlases *atlases, - int width, - int height, - GskGLTextureAtlas **atlas_out, - int *out_x, - int *out_y); - -void gsk_gl_texture_atlas_init (GskGLTextureAtlas *self, - int width, - int height); - -void gsk_gl_texture_atlas_free (GskGLTextureAtlas *self); - -void gsk_gl_texture_atlas_realize (GskGLTextureAtlas *self); - -void gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self, - int width, - int height); - -void gsk_gl_texture_atlas_mark_used (GskGLTextureAtlas *self, - int width, - int height); - - -gboolean gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self, - int width, - int height, - int *out_x, - int *out_y); - -double gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self); - -#endif diff --git a/gsk/gl/opbuffer.c b/gsk/gl/opbuffer.c deleted file mode 100644 index 806b8f7ca4..0000000000 --- a/gsk/gl/opbuffer.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "opbuffer.h" - -#include - -static guint op_sizes[OP_LAST] = { - 0, - sizeof (OpOpacity), - sizeof (OpColor), - sizeof (OpMatrix), - sizeof (OpMatrix), - sizeof (OpProgram), - sizeof (OpRenderTarget), - sizeof (OpClip), - sizeof (OpViewport), - sizeof (OpTexture), - sizeof (OpRepeat), - sizeof (OpLinearGradient), - sizeof (OpRadialGradient), - sizeof (OpColorMatrix), - sizeof (OpBlur), - sizeof (OpShadow), - sizeof (OpOutsetShadow), - sizeof (OpBorder), - sizeof (OpBorder), - sizeof (OpBorder), - sizeof (OpCrossFade), - sizeof (OpShadow), - 0, - sizeof (OpDraw), - sizeof (OpDumpFrameBuffer), - sizeof (OpDebugGroup), - 0, - sizeof (OpBlend), - sizeof (OpGLShader), - sizeof (OpExtraTexture), - sizeof (OpConicGradient), -}; - -void -op_buffer_init (OpBuffer *buffer) -{ - static gsize initialized = FALSE; - - if (g_once_init_enter (&initialized)) - { - guint i; - - for (i = 0; i < G_N_ELEMENTS (op_sizes); i++) - { - guint size = op_sizes[i]; - - if (size > 0) - { - /* Round all op entry sizes to the nearest 16 to ensure - * that we guarantee proper alignments for all op entries. - * This is only done once on first use. - */ -#define CHECK_SIZE(s) else if (size < (s)) { size = s; } - if (0) {} - CHECK_SIZE (16) - CHECK_SIZE (32) - CHECK_SIZE (48) - CHECK_SIZE (64) - CHECK_SIZE (80) - CHECK_SIZE (96) - CHECK_SIZE (112) - CHECK_SIZE (128) - CHECK_SIZE (144) - CHECK_SIZE (160) - CHECK_SIZE (176) - CHECK_SIZE (192) - else g_assert_not_reached (); -#undef CHECK_SIZE - - op_sizes[i] = size; - } - } - - g_once_init_leave (&initialized, TRUE); - } - - memset (buffer, 0, sizeof *buffer); - - buffer->buflen = 4096; - buffer->bufpos = 0; - buffer->buf = g_malloc (buffer->buflen); - buffer->index = g_array_new (FALSE, FALSE, sizeof (OpBufferEntry)); - - /* Add dummy entry to guarantee non-empty index */ - op_buffer_add (buffer, OP_NONE); -} - -void -op_buffer_destroy (OpBuffer *buffer) -{ - g_free (buffer->buf); - g_array_unref (buffer->index); -} - -void -op_buffer_clear (OpBuffer *buffer) -{ - if (buffer->index->len > 1) - g_array_remove_range (buffer->index, 1, buffer->index->len - 1); - buffer->bufpos = 0; -} - -static inline void -ensure_buffer_space_for (OpBuffer *buffer, - guint size) -{ - if G_UNLIKELY (buffer->bufpos + size >= buffer->buflen) - { - buffer->buflen *= 2; - buffer->buf = g_realloc (buffer->buf, buffer->buflen); - } -} - -gpointer -op_buffer_add (OpBuffer *buffer, - OpKind kind) -{ - guint size = op_sizes[kind]; - OpBufferEntry entry; - - entry.pos = buffer->bufpos; - entry.kind = kind; - - if (size > 0) - ensure_buffer_space_for (buffer, size); - - g_array_append_val (buffer->index, entry); - - buffer->bufpos += size; - - return &buffer->buf[entry.pos]; -} diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h deleted file mode 100644 index ea954249c5..0000000000 --- a/gsk/gl/opbuffer.h +++ /dev/null @@ -1,306 +0,0 @@ -#ifndef __OP_BUFFER_H__ -#define __OP_BUFFER_H__ - -#include -#include -#include - -#include "gskgldriverprivate.h" - -typedef struct _Program Program; - -typedef enum -{ - OP_NONE = 0, - OP_CHANGE_OPACITY = 1, - OP_CHANGE_COLOR = 2, - OP_CHANGE_PROJECTION = 3, - OP_CHANGE_MODELVIEW = 4, - OP_CHANGE_PROGRAM = 5, - OP_CHANGE_RENDER_TARGET = 6, - OP_CHANGE_CLIP = 7, - OP_CHANGE_VIEWPORT = 8, - OP_CHANGE_SOURCE_TEXTURE = 9, - OP_CHANGE_REPEAT = 10, - OP_CHANGE_LINEAR_GRADIENT = 11, - OP_CHANGE_RADIAL_GRADIENT = 12, - OP_CHANGE_COLOR_MATRIX = 13, - OP_CHANGE_BLUR = 14, - OP_CHANGE_INSET_SHADOW = 15, - OP_CHANGE_OUTSET_SHADOW = 16, - OP_CHANGE_BORDER = 17, - OP_CHANGE_BORDER_COLOR = 18, - OP_CHANGE_BORDER_WIDTH = 19, - OP_CHANGE_CROSS_FADE = 20, - OP_CHANGE_UNBLURRED_OUTSET_SHADOW = 21, - OP_CLEAR = 22, - OP_DRAW = 23, - OP_DUMP_FRAMEBUFFER = 24, - OP_PUSH_DEBUG_GROUP = 25, - OP_POP_DEBUG_GROUP = 26, - OP_CHANGE_BLEND = 27, - OP_CHANGE_GL_SHADER_ARGS = 28, - OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29, - OP_CHANGE_CONIC_GRADIENT = 30, - OP_LAST -} OpKind; - - -typedef struct { int value; guint send: 1; } IntUniformValue; -typedef struct { float value; guint send: 1; } FloatUniformValue; -typedef struct { float value[2]; guint send: 1; } Float2UniformValue; -typedef struct { GskRoundedRect value; guint send: 1; guint send_corners: 1; } RRUniformValue; -typedef struct { const GdkRGBA *value; guint send: 1; } RGBAUniformValue; -typedef struct { const graphene_vec4_t *value; guint send: 1; } Vec4UniformValue; -typedef struct { const GskColorStop *value; guint send: 1; } ColorStopUniformValue; -typedef struct { const graphene_matrix_t *value; guint send: 1; } MatrixUniformValue; - -/* OpNode are allocated within OpBuffer.pos, but we keep - * a secondary index into the locations of that buffer - * from OpBuffer.index. This allows peeking at the kind - * and quickly replacing existing entries when necessary. - */ -typedef struct -{ - RRUniformValue outline; - FloatUniformValue spread; - Float2UniformValue offset; - RGBAUniformValue color; -} OpShadow; - -typedef struct -{ - RRUniformValue outline; -} OpOutsetShadow; - -typedef struct -{ - guint pos; - OpKind kind; -} OpBufferEntry; - -typedef struct -{ - guint8 *buf; - gsize buflen; - gsize bufpos; - GArray *index; -} OpBuffer; - -typedef struct -{ - float opacity; -} OpOpacity; - -typedef struct -{ - graphene_matrix_t matrix; -} OpMatrix; - -typedef struct -{ - const Program *program; -} OpProgram; - -typedef struct -{ - const GdkRGBA *rgba; -} OpColor; - -typedef struct -{ - int render_target_id; -} OpRenderTarget; - -typedef struct -{ - GskRoundedRect clip; - guint send_corners: 1; -} OpClip; - -typedef struct -{ - graphene_rect_t viewport; -} OpViewport; - -typedef struct -{ - int texture_id; -} OpTexture; - -typedef struct -{ - int texture_id; - int idx; -} OpExtraTexture; - -typedef struct -{ - gsize vao_offset; - gsize vao_size; -} OpDraw; - -typedef struct -{ - ColorStopUniformValue color_stops; - IntUniformValue n_color_stops; - float start_point[2]; - float end_point[2]; - gboolean repeat; -} OpLinearGradient; - -typedef struct -{ - ColorStopUniformValue color_stops; - IntUniformValue n_color_stops; - float start; - float end; - float radius[2]; - float center[2]; - gboolean repeat; -} OpRadialGradient; - -typedef struct -{ - ColorStopUniformValue color_stops; - IntUniformValue n_color_stops; - float center[2]; - float angle; -} OpConicGradient; - -typedef struct -{ - MatrixUniformValue matrix; - Vec4UniformValue offset; -} OpColorMatrix; - -typedef struct -{ - float radius; - graphene_size_t size; - float dir[2]; -} OpBlur; - -typedef struct -{ - float widths[4]; - const GdkRGBA *color; - GskRoundedRect outline; -} OpBorder; - -typedef struct -{ - float progress; - int source2; -} OpCrossFade; - -typedef struct -{ - char *filename; - int width; - int height; -} OpDumpFrameBuffer; - -typedef struct -{ - char text[64]; -} OpDebugGroup; - -typedef struct -{ - int source2; - int mode; -} OpBlend; - -typedef struct -{ - float child_bounds[4]; - float texture_rect[4]; -} OpRepeat; - -typedef struct -{ - float size[2]; - GskGLShader *shader; - const guchar *uniform_data; -} OpGLShader; - -void op_buffer_init (OpBuffer *buffer); -void op_buffer_destroy (OpBuffer *buffer); -void op_buffer_clear (OpBuffer *buffer); -gpointer op_buffer_add (OpBuffer *buffer, - OpKind kind); - -typedef struct -{ - GArray *index; - OpBuffer *buffer; - guint pos; -} OpBufferIter; - -static inline void -op_buffer_iter_init (OpBufferIter *iter, - OpBuffer *buffer) -{ - iter->index = buffer->index; - iter->buffer = buffer; - iter->pos = 1; /* Skip first OP_NONE */ -} - -static inline gpointer -op_buffer_iter_next (OpBufferIter *iter, - OpKind *kind) -{ - const OpBufferEntry *entry; - - if (iter->pos == iter->index->len) - return NULL; - - entry = &g_array_index (iter->index, OpBufferEntry, iter->pos); - - iter->pos++; - - *kind = entry->kind; - return &iter->buffer->buf[entry->pos]; -} - -static inline void -op_buffer_pop_tail (OpBuffer *buffer) -{ - /* Never truncate the first OP_NONE */ - if G_LIKELY (buffer->index->len > 0) - buffer->index->len--; -} - -static inline gpointer -op_buffer_peek_tail (OpBuffer *buffer, - OpKind *kind) -{ - const OpBufferEntry *entry; - - entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1); - *kind = entry->kind; - return &buffer->buf[entry->pos]; -} - -static inline gpointer -op_buffer_peek_tail_checked (OpBuffer *buffer, - OpKind kind) -{ - const OpBufferEntry *entry; - - entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1); - - if (entry->kind == kind) - return &buffer->buf[entry->pos]; - - return NULL; -} - -static inline guint -op_buffer_n_ops (OpBuffer *buffer) -{ - return buffer->index->len - 1; -} - -#endif /* __OP_BUFFER_H__ */ diff --git a/gsk/gl/resources/blend.glsl b/gsk/gl/resources/blend.glsl deleted file mode 100644 index 22323402ac..0000000000 --- a/gsk/gl/resources/blend.glsl +++ /dev/null @@ -1,310 +0,0 @@ -// VERTEX_SHADER: -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -uniform int u_mode; -uniform sampler2D u_source2; - -float -combine (float source, float backdrop) -{ - return source + backdrop * (1.0 - source); -} - -vec4 -composite (vec4 Cs, vec4 Cb, vec3 B) -{ - float ao = Cs.a + Cb.a * (1.0 - Cs.a); - vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao; - return vec4(Co, ao); -} - -vec4 -normal (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, Cs.rgb); -} - -vec4 -multiply (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, Cs.rgb * Cb.rgb); -} - -vec4 -difference (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb)); -} - -vec4 -screen (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb); -} - -float -hard_light (float source, float backdrop) -{ - if (source <= 0.5) - return 2.0 * backdrop * source; - else - return 2.0 * (backdrop + source - backdrop * source) - 1.0; -} - -vec4 -hard_light (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (hard_light (Cs.r, Cb.r), - hard_light (Cs.g, Cb.g), - hard_light (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - -float -soft_light (float source, float backdrop) -{ - float db; - - if (backdrop <= 0.25) - db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop; - else - db = sqrt (backdrop); - - if (source <= 0.5) - return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop); - else - return backdrop + (2.0 * source - 1.0) * (db - backdrop); -} - -vec4 -soft_light (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (soft_light (Cs.r, Cb.r), - soft_light (Cs.g, Cb.g), - soft_light (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - -vec4 -overlay (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (hard_light (Cb.r, Cs.r), - hard_light (Cb.g, Cs.g), - hard_light (Cb.b, Cs.b)); - return composite (Cs, Cb, B); -} - -vec4 -darken (vec4 Cs, vec4 Cb) -{ - vec3 B = min (Cs.rgb, Cb.rgb); - return composite (Cs, Cb, B); -} - -vec4 -lighten (vec4 Cs, vec4 Cb) -{ - vec3 B = max (Cs.rgb, Cb.rgb); - return composite (Cs, Cb, B); -} - -float -color_dodge (float source, float backdrop) -{ - return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0); -} - -vec4 -color_dodge (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (color_dodge (Cs.r, Cb.r), - color_dodge (Cs.g, Cb.g), - color_dodge (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - - -float -color_burn (float source, float backdrop) -{ - return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0); -} - -vec4 -color_burn (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (color_burn (Cs.r, Cb.r), - color_burn (Cs.g, Cb.g), - color_burn (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - -vec4 -exclusion (vec4 Cs, vec4 Cb) -{ - vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb; - return composite (Cs, Cb, B); -} - -float -lum (vec3 c) -{ - return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; -} - -vec3 -clip_color (vec3 c) -{ - float l = lum (c); - float n = min (c.r, min (c.g, c.b)); - float x = max (c.r, max (c.g, c.b)); - if (n < 0.0) c = l + (((c - l) * l) / (l - n)); - if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l)); - return c; -} - -vec3 -set_lum (vec3 c, float l) -{ - float d = l - lum (c); - return clip_color (vec3 (c.r + d, c.g + d, c.b + d)); -} - -float -sat (vec3 c) -{ - return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b)); -} - -vec3 -set_sat (vec3 c, float s) -{ - float cmin = min (c.r, min (c.g, c.b)); - float cmax = max (c.r, max (c.g, c.b)); - vec3 res; - - if (cmax == cmin) - res = vec3 (0, 0, 0); - else - { - if (c.r == cmax) - { - if (c.g == cmin) - { - res.b = ((c.b - cmin) * s) / (cmax - cmin); - res.g = 0.0; - } - else - { - res.g = ((c.g - cmin) * s) / (cmax - cmin); - res.b = 0.0; - } - res.r = s; - } - else if (c.g == cmax) - { - if (c.r == cmin) - { - res.b = ((c.b - cmin) * s) / (cmax - cmin); - res.r = 0.0; - } - else - { - res.r = ((c.r - cmin) * s) / (cmax - cmin); - res.b = 0.0; - } - res.g = s; - } - else - { - if (c.r == cmin) - { - res.g = ((c.g - cmin) * s) / (cmax - cmin); - res.r = 0.0; - } - else - { - res.r = ((c.r - cmin) * s) / (cmax - cmin); - res.g = 0.0; - } - res.b = s; - } - } - return res; -} - -vec4 -color (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (Cs.rgb, lum (Cb.rgb)); - return composite (Cs, Cb, B); -} - -vec4 -hue (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb)); - return composite (Cs, Cb, B); -} - -vec4 -saturation (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb)); - return composite (Cs, Cb, B); -} - -vec4 -luminosity (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (Cb.rgb, lum (Cs.rgb)); - return composite (Cs, Cb, B); -} - -void main() { - vec4 bottom_color = GskTexture(u_source, vUv); - vec4 top_color = GskTexture(u_source2, vUv); - - vec4 result; - if (u_mode == 0) - result = normal(top_color, bottom_color); - else if (u_mode == 1) - result = multiply(top_color, bottom_color); - else if (u_mode == 2) - result = screen(top_color, bottom_color); - else if (u_mode == 3) - result = overlay(top_color, bottom_color); - else if (u_mode == 4) - result = darken(top_color, bottom_color); - else if (u_mode == 5) - result = lighten(top_color, bottom_color); - else if (u_mode == 6) - result = color_dodge(top_color, bottom_color); - else if (u_mode == 7) - result = color_burn(top_color, bottom_color); - else if (u_mode == 8) - result = hard_light(top_color, bottom_color); - else if (u_mode == 9) - result = soft_light(top_color, bottom_color); - else if (u_mode == 10) - result = difference(top_color, bottom_color); - else if (u_mode == 11) - result = exclusion(top_color, bottom_color); - else if (u_mode == 12) - result = color(top_color, bottom_color); - else if (u_mode == 13) - result = hue(top_color, bottom_color); - else if (u_mode == 14) - result = saturation(top_color, bottom_color); - else if (u_mode == 15) - result = luminosity(top_color, bottom_color); - else - discard; - - gskSetOutputColor(result * u_alpha); -} diff --git a/gsk/gl/resources/blit.glsl b/gsk/gl/resources/blit.glsl deleted file mode 100644 index f01cd238ec..0000000000 --- a/gsk/gl/resources/blit.glsl +++ /dev/null @@ -1,13 +0,0 @@ -// VERTEX_SHADER: -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -void main() { - vec4 diffuse = GskTexture(u_source, vUv); - - gskSetOutputColor(diffuse * u_alpha); -} diff --git a/gsk/gl/resources/blur.glsl b/gsk/gl/resources/blur.glsl deleted file mode 100644 index f782ab48cc..0000000000 --- a/gsk/gl/resources/blur.glsl +++ /dev/null @@ -1,55 +0,0 @@ -// VERTEX_SHADER: -uniform float u_blur_radius; -uniform vec2 u_blur_size; -uniform vec2 u_blur_dir; - -_OUT_ vec2 pixel_step; -_OUT_ float pixels_per_side; -_OUT_ vec3 initial_gaussian; - -const float PI = 3.14159265; -const float RADIUS_MULTIPLIER = 2.0; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); - - pixel_step = (vec2(1.0) / u_blur_size) * u_blur_dir; - pixels_per_side = floor(u_blur_radius * RADIUS_MULTIPLIER / 2.0); - - float sigma = u_blur_radius / 2.0; // *shrug* - initial_gaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma); - initial_gaussian.y = exp(-0.5 / (sigma * sigma)); - initial_gaussian.z = initial_gaussian.y * initial_gaussian.y; -} - -// FRAGMENT_SHADER: -_IN_ vec2 pixel_step; -_IN_ float pixels_per_side; -_IN_ vec3 initial_gaussian; - -// blur_radius 0 is NOT supported and MUST be caught before. - -// Partially from http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html -void main() { - vec3 incrementalGaussian = initial_gaussian; - - float coefficientSum = 0.0; - vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x; - coefficientSum += incrementalGaussian.x; - incrementalGaussian.xy *= incrementalGaussian.yz; - - vec2 p = pixel_step; - for (int i = 1; i <= int(pixels_per_side); i++) { - sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x; - sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x; - - coefficientSum += 2.0 * incrementalGaussian.x; - incrementalGaussian.xy *= incrementalGaussian.yz; - - p += pixel_step; - } - - gskSetOutputColor(sum / coefficientSum); -} diff --git a/gsk/gl/resources/border.glsl b/gsk/gl/resources/border.glsl deleted file mode 100644 index 677a0df7cd..0000000000 --- a/gsk/gl/resources/border.glsl +++ /dev/null @@ -1,40 +0,0 @@ -// VERTEX_SHADER: -uniform vec4 u_color; -uniform vec4 u_widths; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_premultiply(u_color) * u_alpha; - - GskRoundedRect outside = gsk_create_rect(u_outline_rect); - GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths); - - gsk_rounded_rect_transform(outside, u_modelview); - gsk_rounded_rect_transform(inside, u_modelview); - - gsk_rounded_rect_encode(outside, transformed_outside_outline); - gsk_rounded_rect_encode(inside, transformed_inside_outline); -} - -// FRAGMENT_SHADER: -uniform vec4[3] u_outline_rect; - -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - - float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - - gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), - 0.0, 1.0); - - gskSetOutputColor(final_color * alpha); -} diff --git a/gsk/gl/resources/color.glsl b/gsk/gl/resources/color.glsl deleted file mode 100644 index 636456ce0d..0000000000 --- a/gsk/gl/resources/color.glsl +++ /dev/null @@ -1,18 +0,0 @@ -// VERTEX_SHADER: -uniform vec4 u_color; - -_OUT_ vec4 final_color; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_premultiply(u_color) * u_alpha; -} - -// FRAGMENT_SHADER: -_IN_ vec4 final_color; - -void main() { - gskSetOutputColor(final_color); -} - diff --git a/gsk/gl/resources/color_matrix.glsl b/gsk/gl/resources/color_matrix.glsl deleted file mode 100644 index 79cb36434e..0000000000 --- a/gsk/gl/resources/color_matrix.glsl +++ /dev/null @@ -1,25 +0,0 @@ -// VERTEX_SHADER: -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -uniform mat4 u_color_matrix; -uniform vec4 u_color_offset; - -void main() { - vec4 color = GskTexture(u_source, vUv); - - // Un-premultilpy - if (color.a != 0.0) - color.rgb /= color.a; - - color = u_color_matrix * color + u_color_offset; - color = clamp(color, 0.0, 1.0); - - color.rgb *= color.a; - - gskSetOutputColor(color * u_alpha); -} diff --git a/gsk/gl/resources/coloring.glsl b/gsk/gl/resources/coloring.glsl deleted file mode 100644 index a675493030..0000000000 --- a/gsk/gl/resources/coloring.glsl +++ /dev/null @@ -1,22 +0,0 @@ -// VERTEX_SHADER: -uniform vec4 u_color; - -_OUT_ vec4 final_color; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); - - final_color = gsk_premultiply(u_color) * u_alpha; -} - -// FRAGMENT_SHADER: - -_IN_ vec4 final_color; - -void main() { - vec4 diffuse = GskTexture(u_source, vUv); - - gskSetOutputColor(final_color * diffuse.a); -} diff --git a/gsk/gl/resources/conic_gradient.glsl b/gsk/gl/resources/conic_gradient.glsl deleted file mode 100644 index 630a42c5e6..0000000000 --- a/gsk/gl/resources/conic_gradient.glsl +++ /dev/null @@ -1,73 +0,0 @@ -// VERTEX_SHADER -uniform vec4 u_geometry; - -_NOPERSPECTIVE_ _OUT_ vec2 coord; - -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_geometry.xy; - - coord = vec2(dot(mv0, offset), - dot(mv1, offset)); -} - -// FRAGMENT_SHADER: -#ifdef GSK_LEGACY -uniform int u_num_color_stops; -#else -uniform highp int u_num_color_stops; // Why? Because it works like this. -#endif - -uniform vec4 u_geometry; -uniform float u_color_stops[6 * 5]; - -_NOPERSPECTIVE_ _IN_ vec2 coord; - -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() { - // direction of point in range [-PI, PI] - vec2 pos = floor(coord); - float angle = atan(pos.y, pos.x); - - // fract() does the modulo here, so now we have progress - // into the current conic - float offset = fract(angle * u_geometry.z + u_geometry.w); - - if (offset < get_offset(0)) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); - return; - } - - int n = u_num_color_stops - 1; - for (int i = 0; i < n; i++) { - float curr_offset = get_offset(i); - float next_offset = get_offset(i + 1); - - if (offset >= curr_offset && offset < next_offset) { - float f = (offset - curr_offset) / (next_offset - curr_offset); - vec4 curr_color = gsk_premultiply(get_color(i)); - vec4 next_color = gsk_premultiply(get_color(i + 1)); - vec4 color = mix(curr_color, next_color, f); - - gskSetOutputColor(color * u_alpha); - return; - } - } - - gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha)); -} diff --git a/gsk/gl/resources/cross_fade.glsl b/gsk/gl/resources/cross_fade.glsl deleted file mode 100644 index f824430f9d..0000000000 --- a/gsk/gl/resources/cross_fade.glsl +++ /dev/null @@ -1,20 +0,0 @@ -// VERTEX_SHADER: -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -uniform float u_progress; -uniform sampler2D u_source2; - -void main() { - vec4 source1 = GskTexture(u_source, vUv); // start child - vec4 source2 = GskTexture(u_source2, vUv); // end child - - float p_start = (1.0 - u_progress) * u_alpha; - float p_end = u_progress * u_alpha; - vec4 color = (p_start * source1) + (p_end * source2); - gskSetOutputColor(color); -} diff --git a/gsk/gl/resources/custom.glsl b/gsk/gl/resources/custom.glsl deleted file mode 100644 index d2aed97fc8..0000000000 --- a/gsk/gl/resources/custom.glsl +++ /dev/null @@ -1,21 +0,0 @@ -// VERTEX_SHADER: -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// The shader supplies: -void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv); - -uniform vec2 u_size; -uniform sampler2D u_source2; -uniform sampler2D u_source3; -uniform sampler2D u_source4; - -void main() { - vec4 fragColor; - vec2 fragCoord = vec2(vUv.x * u_size.x, (1.0-vUv.y) * u_size.y); - mainImage(fragColor, fragCoord, u_size, vUv); - gskSetOutputColor(fragColor); -} diff --git a/gsk/gl/resources/inset_shadow.glsl b/gsk/gl/resources/inset_shadow.glsl deleted file mode 100644 index 9a21cdef09..0000000000 --- a/gsk/gl/resources/inset_shadow.glsl +++ /dev/null @@ -1,41 +0,0 @@ -// VERTEX_SHADER: -uniform vec4 u_color; -uniform float u_spread; -uniform vec2 u_offset; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_premultiply(u_color) * u_alpha; - - GskRoundedRect outside = gsk_create_rect(u_outline_rect); - GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread)); - - gsk_rounded_rect_offset(inside, u_offset); - - gsk_rounded_rect_transform(outside, u_modelview); - gsk_rounded_rect_transform(inside, u_modelview); - - gsk_rounded_rect_encode(outside, transformed_outside_outline); - gsk_rounded_rect_encode(inside, transformed_inside_outline); -} - -// FRAGMENT_SHADER: -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - - float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - - gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), - 0.0, 1.0); - - gskSetOutputColor(final_color * alpha); -} diff --git a/gsk/gl/resources/linear_gradient.glsl b/gsk/gl/resources/linear_gradient.glsl deleted file mode 100644 index cc90392c06..0000000000 --- a/gsk/gl/resources/linear_gradient.glsl +++ /dev/null @@ -1,95 +0,0 @@ -// 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: -#ifdef GSK_LEGACY -uniform int u_num_color_stops; -#else -uniform highp int u_num_color_stops; // Why? Because it works like this. -#endif - -uniform float u_color_stops[6 * 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); - } - - if (offset < get_offset(0)) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); - return; - } - - int n = u_num_color_stops - 1; - for (int i = 0; i < n; i++) { - float curr_offset = get_offset(i); - float next_offset = get_offset(i + 1); - - if (offset >= curr_offset && offset < next_offset) { - float f = (offset - curr_offset) / (next_offset - curr_offset); - vec4 curr_color = gsk_premultiply(get_color(i)); - vec4 next_color = gsk_premultiply(get_color(i + 1)); - vec4 color = mix(curr_color, next_color, f); - - gskSetOutputColor(color * u_alpha); - return; - } - } - - gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha)); -} diff --git a/gsk/gl/resources/outset_shadow.glsl b/gsk/gl/resources/outset_shadow.glsl deleted file mode 100644 index 373c650179..0000000000 --- a/gsk/gl/resources/outset_shadow.glsl +++ /dev/null @@ -1,33 +0,0 @@ -// VERTEX_SHADER: -uniform vec4 u_color; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); - - final_color = gsk_premultiply(u_color) * u_alpha; - - GskRoundedRect outline = gsk_create_rect(u_outline_rect); - gsk_rounded_rect_transform(outline, u_modelview); - gsk_rounded_rect_encode(outline, transformed_outline); -} - -// FRAGMENT_SHADER: -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - - float alpha = GskTexture(u_source, vUv).a; - alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0)); - - vec4 color = final_color * alpha; - - gskSetOutputColor(color); -} diff --git a/gsk/gl/resources/preamble.fs.glsl b/gsk/gl/resources/preamble.fs.glsl deleted file mode 100644 index 3a2fe49240..0000000000 --- a/gsk/gl/resources/preamble.fs.glsl +++ /dev/null @@ -1,139 +0,0 @@ -uniform sampler2D u_source; -uniform mat4 u_projection; -uniform mat4 u_modelview; -uniform float u_alpha;// = 1.0; -uniform vec4 u_viewport; -uniform vec4[3] u_clip_rect; - -#if defined(GSK_LEGACY) -_OUT_ vec4 outputColor; -#elif !defined(GSK_GLES) -_OUT_ vec4 outputColor; -#endif - -_IN_ vec2 vUv; - - - -GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r) -{ -#if defined(GSK_GLES) || defined(GSK_LEGACY) - return GskRoundedRect(r[0], r[1], r[2]); -#else - return r; -#endif -} - -float -gsk_ellipsis_dist (vec2 p, vec2 radius) -{ - if (radius == vec2(0, 0)) - return 0.0; - - vec2 p0 = p / radius; - vec2 p1 = 2.0 * p0 / radius; - - return (dot(p0, p0) - 1.0) / length (p1); -} - -float -gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius) -{ - float d = gsk_ellipsis_dist (point - center, radius); - return clamp (0.5 - d, 0.0, 1.0); -} - -float -gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p) -{ - if (p.x < r.bounds.x || p.y < r.bounds.y || - p.x >= r.bounds.z || p.y >= r.bounds.w) - return 0.0; - - vec2 ref_tl = r.corner_points1.xy; - vec2 ref_tr = r.corner_points1.zw; - vec2 ref_br = r.corner_points2.xy; - vec2 ref_bl = r.corner_points2.zw; - - if (p.x >= ref_tl.x && p.x >= ref_bl.x && - p.x <= ref_tr.x && p.x <= ref_br.x) - return 1.0; - - if (p.y >= ref_tl.y && p.y >= ref_tr.y && - p.y <= ref_bl.y && p.y <= ref_br.y) - return 1.0; - - vec2 rad_tl = r.corner_points1.xy - r.bounds.xy; - vec2 rad_tr = r.corner_points1.zw - r.bounds.zy; - vec2 rad_br = r.corner_points2.xy - r.bounds.zw; - vec2 rad_bl = r.corner_points2.zw - r.bounds.xw; - - float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl); - float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr); - float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br); - float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl); - - vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl); - - bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y, - p.x > ref_tr.x && p.y < ref_tr.y, - p.x > ref_br.x && p.y > ref_br.y, - p.x < ref_bl.x && p.y > ref_bl.y); - - return 1.0 - dot(vec4(is_out), corner_coverages); -} - -float -gsk_rect_coverage (vec4 r, vec2 p) -{ - if (p.x < r.x || p.y < r.y || - p.x >= r.z || p.y >= r.w) - return 0.0; - - return 1.0; -} - -vec4 GskTexture(sampler2D sampler, vec2 texCoords) { -#if defined(GSK_GLES) || defined(GSK_LEGACY) - return texture2D(sampler, texCoords); -#else - return texture(sampler, texCoords); -#endif -} - -#ifdef GSK_GL3 -layout(origin_upper_left) in vec4 gl_FragCoord; -#endif - -vec2 gsk_get_frag_coord() { - vec2 fc = gl_FragCoord.xy; - -#ifdef GSK_GL3 - fc += u_viewport.xy; -#else - fc.x += u_viewport.x; - fc.y = (u_viewport.y + u_viewport.w) - fc.y; -#endif - - return fc; -} - -void gskSetOutputColor(vec4 color) { - vec4 result; - -#if defined(NO_CLIP) - result = color; -#elif defined(RECT_CLIP) - result = color * gsk_rect_coverage(gsk_get_bounds(u_clip_rect), - gsk_get_frag_coord()); -#else - result = color * gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect), - gsk_get_frag_coord()); -#endif - -#if defined(GSK_GLES) || defined(GSK_LEGACY) - gl_FragColor = result; -#else - outputColor = result; -#endif -} diff --git a/gsk/gl/resources/preamble.glsl b/gsk/gl/resources/preamble.glsl deleted file mode 100644 index 8bc007ba67..0000000000 --- a/gsk/gl/resources/preamble.glsl +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef GSK_LEGACY -precision highp float; -#endif - -#if defined(GSK_GLES) || defined(GSK_LEGACY) -#define _OUT_ varying -#define _IN_ varying -#define _NOPERSPECTIVE_ -#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3] -#else -#define _OUT_ out -#define _IN_ in -#define _NOPERSPECTIVE_ noperspective -#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect -#endif - - -struct GskRoundedRect -{ - vec4 bounds; // Top left and bottom right - // Look, arrays can't be in structs if you want to return the struct - // from a function in gles or whatever. Just kill me. - vec4 corner_points1; // xy = top left, zw = top right - vec4 corner_points2; // xy = bottom right, zw = bottom left -}; - -// Transform from a C GskRoundedRect to what we need. -GskRoundedRect -gsk_create_rect(vec4[3] data) -{ - vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw); - - vec4 corner_points1 = vec4(bounds.xy + data[1].xy, - bounds.zy + vec2(data[1].zw * vec2(-1, 1))); - vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)), - bounds.xw + vec2(data[2].zw * vec2(1, -1))); - - return GskRoundedRect(bounds, corner_points1, corner_points2); -} - -vec4 -gsk_get_bounds(vec4[3] data) -{ - return vec4(data[0].xy, data[0].xy + data[0].zw); -} - -vec4 gsk_premultiply(vec4 c) { - return vec4(c.rgb * c.a, c.a); -} - -vec4 gsk_scaled_premultiply(vec4 c, float s) { - // Fast version of gsk_premultiply(c) * s - // 4 muls instead of 7 - float a = s * c.a; - - return vec4(c.rgb * a, a); -} diff --git a/gsk/gl/resources/preamble.vs.glsl b/gsk/gl/resources/preamble.vs.glsl deleted file mode 100644 index 89ee6f74e0..0000000000 --- a/gsk/gl/resources/preamble.vs.glsl +++ /dev/null @@ -1,69 +0,0 @@ -uniform mat4 u_projection; -uniform mat4 u_modelview; -uniform float u_alpha; - -#if defined(GSK_GLES) || defined(GSK_LEGACY) -attribute vec2 aPosition; -attribute vec2 aUv; -_OUT_ vec2 vUv; -#else -_IN_ vec2 aPosition; -_IN_ vec2 aUv; -_OUT_ vec2 vUv; -#endif - -// amount is: top, right, bottom, left -GskRoundedRect -gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount) -{ - vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz; - vec4 new_corner_points1 = r.corner_points1; - vec4 new_corner_points2 = r.corner_points2; - - if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy; - if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy; - if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw; - if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw; - - return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2); -} - -void -gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset) -{ - r.bounds.xy += offset; - r.bounds.zw += offset; - r.corner_points1.xy += offset; - r.corner_points1.zw += offset; - r.corner_points2.xy += offset; - r.corner_points2.zw += offset; -} - -void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat) -{ - r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy; - r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy; - - r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy; - r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy; - - r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy; - r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy; -} - -#if defined(GSK_LEGACY) -// Can't have out or inout array parameters... -#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2; -#else -void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r) -{ -#if defined(GSK_GLES) - out_r[0] = r.bounds; - out_r[1] = r.corner_points1; - out_r[2] = r.corner_points2; -#else - out_r = r; -#endif -} - -#endif diff --git a/gsk/gl/resources/radial_gradient.glsl b/gsk/gl/resources/radial_gradient.glsl deleted file mode 100644 index 0ab3fdf07a..0000000000 --- a/gsk/gl/resources/radial_gradient.glsl +++ /dev/null @@ -1,74 +0,0 @@ -// VERTEX_SHADER -uniform vec4 u_geometry; - -_NOPERSPECTIVE_ _OUT_ vec2 coord; - -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_geometry.xy; - vec2 dir = vec2(dot(mv0, offset), - dot(mv1, offset)); - - coord = dir * u_geometry.zw; -} - -// FRAGMENT_SHADER: -#ifdef GSK_LEGACY -uniform int u_num_color_stops; -#else -uniform highp int u_num_color_stops; -#endif - -uniform bool u_repeat; -uniform vec2 u_range; -uniform float u_color_stops[6 * 5]; - -_NOPERSPECTIVE_ _IN_ vec2 coord; - -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() { - // Reverse scale - float offset = length(coord) * u_range.x + u_range.y; - - if (u_repeat) { - offset = fract(offset); - } - - if (offset < get_offset(0)) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); - return; - } - - int n = u_num_color_stops - 1; - for (int i = 0; i < n; i++) { - float curr_offset = get_offset(i); - float next_offset = get_offset(i + 1); - - if (offset >= curr_offset && offset < next_offset) { - float f = (offset - curr_offset) / (next_offset - curr_offset); - vec4 curr_color = gsk_premultiply(get_color(i)); - vec4 next_color = gsk_premultiply(get_color(i + 1)); - vec4 color = mix(curr_color, next_color, f); - - gskSetOutputColor(color * u_alpha); - return; - } - } - - gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha)); -} diff --git a/gsk/gl/resources/repeat.glsl b/gsk/gl/resources/repeat.glsl deleted file mode 100644 index a9ebcc5e10..0000000000 --- a/gsk/gl/resources/repeat.glsl +++ /dev/null @@ -1,41 +0,0 @@ -// VERTEX_SHADER: -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -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(u_child_bounds.x + mapped_x * u_child_bounds.z, 1.0); - float wrapped_y = wrap(u_child_bounds.y + 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 = GskTexture(u_source, tp); - - gskSetOutputColor(diffuse * u_alpha); -} diff --git a/gsk/gl/resources/unblurred_outset_shadow.glsl b/gsk/gl/resources/unblurred_outset_shadow.glsl deleted file mode 100644 index f110370412..0000000000 --- a/gsk/gl/resources/unblurred_outset_shadow.glsl +++ /dev/null @@ -1,42 +0,0 @@ -// VERTEX_SHADER: -uniform vec4 u_color; -uniform float u_spread; -uniform vec2 u_offset; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_premultiply(u_color) * u_alpha; - - GskRoundedRect inside = gsk_create_rect(u_outline_rect); - GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread)); - - gsk_rounded_rect_offset(outside, u_offset); - - gsk_rounded_rect_transform(outside, u_modelview); - gsk_rounded_rect_transform(inside, u_modelview); - - gsk_rounded_rect_encode(outside, transformed_outside_outline); - gsk_rounded_rect_encode(inside, transformed_inside_outline); -} - -// FRAGMENT_SHADER: -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - - float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - - gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), - 0.0, 1.0); - - gskSetOutputColor(final_color * alpha); -} - diff --git a/gsk/gl/stb_rect_pack.c b/gsk/gl/stb_rect_pack.c deleted file mode 100644 index ff658466a1..0000000000 --- a/gsk/gl/stb_rect_pack.c +++ /dev/null @@ -1,431 +0,0 @@ - -#include "stb_rect_pack.h" -#define STB_RECT_PACK_IMPLEMENTATION -////////////////////////////////////////////////////////////////////////////// -// -// IMPLEMENTATION SECTION -// - -#ifdef STB_RECT_PACK_IMPLEMENTATION -#ifndef STBRP_SORT -#include -#define STBRP_SORT qsort -#endif - -#ifndef STBRP_ASSERT -#include -#define STBRP_ASSERT assert -#endif - -#ifdef _MSC_VER -#define STBRP__NOTUSED(v) (void)(v) -#else -#define STBRP__NOTUSED(v) (void)sizeof(v) -#endif - -enum -{ - STBRP__INIT_skyline = 1 -}; - -STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) -{ - switch (context->init_mode) { - case STBRP__INIT_skyline: - STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); - context->heuristic = heuristic; - break; - default: - STBRP_ASSERT(0); - } -} - -STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) -{ - if (allow_out_of_mem) - // if it's ok to run out of memory, then don't bother aligning them; - // this gives better packing, but may fail due to OOM (even though - // the rectangles easily fit). @TODO a smarter approach would be to only - // quantize once we've hit OOM, then we could get rid of this parameter. - context->align = 1; - else { - // if it's not ok to run out of memory, then quantize the widths - // so that num_nodes is always enough nodes. - // - // I.e. num_nodes * align >= width - // align >= width / num_nodes - // align = ceil(width/num_nodes) - - context->align = (context->width + context->num_nodes-1) / context->num_nodes; - } -} - -STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) -{ - int i; -#ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(width <= 0xffff && height <= 0xffff); -#endif - - for (i=0; i < num_nodes-1; ++i) - nodes[i].next = &nodes[i+1]; - nodes[i].next = NULL; - context->init_mode = STBRP__INIT_skyline; - context->heuristic = STBRP_HEURISTIC_Skyline_default; - context->free_head = &nodes[0]; - context->active_head = &context->extra[0]; - context->width = width; - context->height = height; - context->num_nodes = num_nodes; - stbrp_setup_allow_out_of_mem(context, 0); - - // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) - context->extra[0].x = 0; - context->extra[0].y = 0; - context->extra[0].next = &context->extra[1]; - context->extra[1].x = (stbrp_coord) width; -#ifdef STBRP_LARGE_RECTS - context->extra[1].y = (1<<30); -#else - context->extra[1].y = 65535; -#endif - context->extra[1].next = NULL; -} - -// find minimum y position if it starts at x1 -static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) -{ - stbrp_node *node = first; - int x1 = x0 + width; - int min_y, visited_width, waste_area; - - STBRP__NOTUSED(c); - - STBRP_ASSERT(first->x <= x0); - - #if 0 - // skip in case we're past the node - while (node->next->x <= x0) - ++node; - #else - STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency - #endif - - STBRP_ASSERT(node->x <= x0); - - min_y = 0; - waste_area = 0; - visited_width = 0; - while (node->x < x1) { - if (node->y > min_y) { - // raise min_y higher. - // we've accounted for all waste up to min_y, - // but we'll now add more waste for everything we've visited - waste_area += visited_width * (node->y - min_y); - min_y = node->y; - // the first time through, visited_width might be reduced - if (node->x < x0) - visited_width += node->next->x - x0; - else - visited_width += node->next->x - node->x; - } else { - // add waste area - int under_width = node->next->x - node->x; - if (under_width + visited_width > width) - under_width = width - visited_width; - waste_area += under_width * (min_y - node->y); - visited_width += under_width; - } - node = node->next; - } - - *pwaste = waste_area; - return min_y; -} - -typedef struct -{ - int x,y; - stbrp_node **prev_link; -} stbrp__findresult; - -static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) -{ - int best_waste = (1<<30), best_x, best_y = (1 << 30); - stbrp__findresult fr; - stbrp_node **prev, *node, *tail, **best = NULL; - - // align to multiple of c->align - width = (width + c->align - 1); - width -= width % c->align; - STBRP_ASSERT(width % c->align == 0); - - node = c->active_head; - prev = &c->active_head; - while (node->x + width <= c->width) { - int y,waste; - y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); - if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL - // bottom left - if (y < best_y) { - best_y = y; - best = prev; - } - } else { - // best-fit - if (y + height <= c->height) { - // can only use it if it first vertically - if (y < best_y || (y == best_y && waste < best_waste)) { - best_y = y; - best_waste = waste; - best = prev; - } - } - } - prev = &node->next; - node = node->next; - } - - best_x = (best == NULL) ? 0 : (*best)->x; - - // if doing best-fit (BF), we also have to try aligning right edge to each node position - // - // e.g, if fitting - // - // ____________________ - // |____________________| - // - // into - // - // | | - // | ____________| - // |____________| - // - // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned - // - // This makes BF take about 2x the time - - if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { - tail = c->active_head; - node = c->active_head; - prev = &c->active_head; - // find first node that's admissible - while (tail->x < width) - tail = tail->next; - while (tail) { - int xpos = tail->x - width; - int y,waste; - STBRP_ASSERT(xpos >= 0); - // find the left position that matches this - while (node->next->x <= xpos) { - prev = &node->next; - node = node->next; - } - STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); - y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); - if (y + height < c->height) { - if (y <= best_y) { - if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { - best_x = xpos; - STBRP_ASSERT(y <= best_y); - best_y = y; - best_waste = waste; - best = prev; - } - } - } - tail = tail->next; - } - } - - fr.prev_link = best; - fr.x = best_x; - fr.y = best_y; - return fr; -} - -static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) -{ - // find best position according to heuristic - stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); - stbrp_node *node, *cur; - - // bail if: - // 1. it failed - // 2. the best node doesn't fit (we don't always check this) - // 3. we're out of memory - if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { - res.prev_link = NULL; - return res; - } - - // on success, create new node - node = context->free_head; - node->x = (stbrp_coord) res.x; - node->y = (stbrp_coord) (res.y + height); - - context->free_head = node->next; - - // insert the new node into the right starting point, and - // let 'cur' point to the remaining nodes needing to be - // stiched back in - - cur = *res.prev_link; - if (cur->x < res.x) { - // preserve the existing one, so start testing with the next one - stbrp_node *next = cur->next; - cur->next = node; - cur = next; - } else { - *res.prev_link = node; - } - - // from here, traverse cur and free the nodes, until we get to one - // that shouldn't be freed - while (cur->next && cur->next->x <= res.x + width) { - stbrp_node *next = cur->next; - // move the current node to the free list - cur->next = context->free_head; - context->free_head = cur; - cur = next; - } - - // stitch the list back in - node->next = cur; - - if (cur->x < res.x + width) - cur->x = (stbrp_coord) (res.x + width); - -#ifdef _DEBUG - cur = context->active_head; - while (cur->x < context->width) { - STBRP_ASSERT(cur->x < cur->next->x); - cur = cur->next; - } - STBRP_ASSERT(cur->next == NULL); - - { - int count=0; - cur = context->active_head; - while (cur) { - cur = cur->next; - ++count; - } - cur = context->free_head; - while (cur) { - cur = cur->next; - ++count; - } - STBRP_ASSERT(count == context->num_nodes+2); - } -#endif - - return res; -} - -static int rect_height_compare(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - if (p->h > q->h) - return -1; - if (p->h < q->h) - return 1; - return (p->w > q->w) ? -1 : (p->w < q->w); -} - -static int rect_original_order(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); -} - -#ifdef STBRP_LARGE_RECTS -#define STBRP__MAXVAL 0xffffffff -#else -#define STBRP__MAXVAL 0xffff -#endif - -STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) -{ - int i, all_rects_packed = 1; - - // we use the 'was_packed' field internally to allow sorting/unsorting - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = i; - } - - // sort according to heuristic - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); - - for (i=0; i < num_rects; ++i) { - if (rects[i].w == 0 || rects[i].h == 0) { - rects[i].x = rects[i].y = 0; // empty rect needs no space - } else { - stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); - if (fr.prev_link) { - rects[i].x = (stbrp_coord) fr.x; - rects[i].y = (stbrp_coord) fr.y; - } else { - rects[i].x = rects[i].y = STBRP__MAXVAL; - } - } - } - - // unsort - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); - - // set was_packed flags and all_rects_packed status - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); - if (!rects[i].was_packed) - all_rects_packed = 0; - } - - // return the all_rects_packed status - return all_rects_packed; -} -#endif - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/gsk/gl/stb_rect_pack.h b/gsk/gl/stb_rect_pack.h deleted file mode 100644 index 3ecb1c2fa2..0000000000 --- a/gsk/gl/stb_rect_pack.h +++ /dev/null @@ -1,191 +0,0 @@ -// stb_rect_pack.h - v0.99 - public domain - rectangle packing -// Sean Barrett 2014 -// -// Useful for e.g. packing rectangular textures into an atlas. -// Does not do rotation. -// -// Not necessarily the awesomest packing method, but better than -// the totally naive one in stb_truetype (which is primarily what -// this is meant to replace). -// -// Has only had a few tests run, may have issues. -// -// More docs to come. -// -// No memory allocations; uses qsort() and assert() from stdlib. -// Can override those by defining STBRP_SORT and STBRP_ASSERT. -// -// This library currently uses the Skyline Bottom-Left algorithm. -// -// Please note: better rectangle packers are welcome! Please -// implement them to the same API, but with a different init -// function. -// -// Credits -// -// Library -// Sean Barrett -// Minor features -// Martins Mozeiko -// github:IntellectualKitty -// -// Bugfixes / warning fixes -// Jeremy Jaussaud -// -// Version history: -// -// 0.99 (2019-02-07) warning fixes -// 0.11 (2017-03-03) return packing success/fail result -// 0.10 (2016-10-25) remove cast-away-const to avoid warnings -// 0.09 (2016-08-27) fix compiler warnings -// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) -// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) -// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort -// 0.05: added STBRP_ASSERT to allow replacing assert -// 0.04: fixed minor bug in STBRP_LARGE_RECTS support -// 0.01: initial release -// -// LICENSE -// -// See end of file for license information. - -////////////////////////////////////////////////////////////////////////////// -// -// INCLUDE SECTION -// - -#ifndef STB_INCLUDE_STB_RECT_PACK_H -#define STB_INCLUDE_STB_RECT_PACK_H - -#define STB_RECT_PACK_VERSION 1 - -#ifdef STBRP_STATIC -#define STBRP_DEF static -#else -#define STBRP_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct stbrp_context stbrp_context; -typedef struct stbrp_node stbrp_node; -typedef struct stbrp_rect stbrp_rect; - -#ifdef STBRP_LARGE_RECTS -typedef int stbrp_coord; -#else -typedef unsigned short stbrp_coord; -#endif - -STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); -// Assign packed locations to rectangles. The rectangles are of type -// 'stbrp_rect' defined below, stored in the array 'rects', and there -// are 'num_rects' many of them. -// -// Rectangles which are successfully packed have the 'was_packed' flag -// set to a non-zero value and 'x' and 'y' store the minimum location -// on each axis (i.e. bottom-left in cartesian coordinates, top-left -// if you imagine y increasing downwards). Rectangles which do not fit -// have the 'was_packed' flag set to 0. -// -// You should not try to access the 'rects' array from another thread -// while this function is running, as the function temporarily reorders -// the array while it executes. -// -// To pack into another rectangle, you need to call stbrp_init_target -// again. To continue packing into the same rectangle, you can call -// this function again. Calling this multiple times with multiple rect -// arrays will probably produce worse packing results than calling it -// a single time with the full rectangle array, but the option is -// available. -// -// The function returns 1 if all of the rectangles were successfully -// packed and 0 otherwise. - -struct stbrp_rect -{ - // reserved for your use: - int id; - - // input: - stbrp_coord w, h; - - // output: - stbrp_coord x, y; - int was_packed; // non-zero if valid packing - -}; // 16 bytes, nominally - - -STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); -// Initialize a rectangle packer to: -// pack a rectangle that is 'width' by 'height' in dimensions -// using temporary storage provided by the array 'nodes', which is 'num_nodes' long -// -// You must call this function every time you start packing into a new target. -// -// There is no "shutdown" function. The 'nodes' memory must stay valid for -// the following stbrp_pack_rects() call (or calls), but can be freed after -// the call (or calls) finish. -// -// Note: to guarantee best results, either: -// 1. make sure 'num_nodes' >= 'width' -// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' -// -// If you don't do either of the above things, widths will be quantized to multiples -// of small integers to guarantee the algorithm doesn't run out of temporary storage. -// -// If you do #2, then the non-quantized algorithm will be used, but the algorithm -// may run out of temporary storage and be unable to pack some rectangles. - -STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); -// Optionally call this function after init but before doing any packing to -// change the handling of the out-of-temp-memory scenario, described above. -// If you call init again, this will be reset to the default (false). - - -STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); -// Optionally select which packing heuristic the library should use. Different -// heuristics will produce better/worse results for different data sets. -// If you call init again, this will be reset to the default. - -enum -{ - STBRP_HEURISTIC_Skyline_default=0, - STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, - STBRP_HEURISTIC_Skyline_BF_sortHeight -}; - - -////////////////////////////////////////////////////////////////////////////// -// -// the details of the following structures don't matter to you, but they must -// be visible so you can handle the memory allocations for them - -struct stbrp_node -{ - stbrp_coord x,y; - stbrp_node *next; -}; - -struct stbrp_context -{ - int width; - int height; - int align; - int init_mode; - int heuristic; - int num_nodes; - stbrp_node *active_head; - stbrp_node *free_head; - stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' -}; - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/gsk/gskglshader.c b/gsk/gskglshader.c index 996601d9c6..af6704a556 100644 --- a/gsk/gskglshader.c +++ b/gsk/gskglshader.c @@ -139,7 +139,6 @@ #include "gskglshaderprivate.h" #include "gskdebugprivate.h" -#include "gl/gskglrendererprivate.h" #include "ngl/gsknglrendererprivate.h" static GskGLUniformType @@ -544,9 +543,7 @@ gsk_gl_shader_compile (GskGLShader *shader, { g_return_val_if_fail (GSK_IS_GL_SHADER (shader), FALSE); - if (GSK_IS_GL_RENDERER (renderer)) - return gsk_gl_renderer_try_compile_gl_shader (GSK_GL_RENDERER (renderer), shader, error); - else if (GSK_IS_NGL_RENDERER (renderer)) + if (GSK_IS_NGL_RENDERER (renderer)) return gsk_ngl_renderer_try_compile_gl_shader (GSK_NGL_RENDERER (renderer), shader, error); g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index 082ec4c1e8..77fc9a0dfa 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -38,7 +38,6 @@ #include "gskcairorenderer.h" #include "gskdebugprivate.h" -#include "gl/gskglrenderer.h" #include "ngl/gsknglrenderer.h" #include "gskprofilerprivate.h" #include "gskrendernodeprivate.h" @@ -508,8 +507,6 @@ get_renderer_for_name (const char *renderer_name) else if (g_ascii_strcasecmp (renderer_name, "opengl") == 0 || g_ascii_strcasecmp (renderer_name, "ngl") == 0) return GSK_TYPE_NGL_RENDERER; - else if (g_ascii_strcasecmp (renderer_name, "gl") == 0) - return GSK_TYPE_GL_RENDERER; #ifdef GDK_RENDERING_VULKAN else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0) return GSK_TYPE_VULKAN_RENDERER; @@ -520,12 +517,11 @@ get_renderer_for_name (const char *renderer_name) #ifdef GDK_WINDOWING_BROADWAY g_print ("broadway - Use the Broadway specific renderer\n"); #else - g_print ("broadway - disabled during GTK build\n"); + g_print ("broadway - Disabled during GTK build\n"); #endif g_print (" cairo - Use the Cairo fallback renderer\n"); g_print (" opengl - Use the default OpenGL renderer\n"); - g_print (" gl - An OpenGL renderer\n"); - g_print (" ngl - Another OpenGL renderer\n"); + g_print (" ngl - An OpenGL renderer\n"); #ifdef GDK_RENDERING_VULKAN g_print (" vulkan - Use the Vulkan renderer\n"); #else diff --git a/gsk/meson.build b/gsk/meson.build index 7b82108286..20fd33185d 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -1,25 +1,3 @@ -gsk_private_gl_shaders = [ - 'gl/resources/preamble.glsl', - 'gl/resources/preamble.fs.glsl', - 'gl/resources/preamble.vs.glsl', - 'gl/resources/border.glsl', - 'gl/resources/blit.glsl', - 'gl/resources/coloring.glsl', - 'gl/resources/color.glsl', - 'gl/resources/linear_gradient.glsl', - 'gl/resources/radial_gradient.glsl', - 'gl/resources/conic_gradient.glsl', - 'gl/resources/color_matrix.glsl', - 'gl/resources/blur.glsl', - 'gl/resources/inset_shadow.glsl', - 'gl/resources/outset_shadow.glsl', - 'gl/resources/unblurred_outset_shadow.glsl', - 'gl/resources/cross_fade.glsl', - 'gl/resources/blend.glsl', - 'gl/resources/repeat.glsl', - 'gl/resources/custom.glsl', -] - gsk_private_ngl_shaders = [ 'ngl/resources/preamble.glsl', 'ngl/resources/preamble.fs.glsl', @@ -53,7 +31,6 @@ gsk_public_sources = files([ 'gskrendernodeparser.c', 'gskroundedrect.c', 'gsktransform.c', - 'gl/gskglrenderer.c', 'ngl/gsknglrenderer.c', ]) @@ -62,16 +39,6 @@ gsk_private_sources = files([ 'gskdebug.c', 'gskprivate.c', 'gskprofiler.c', - 'gl/gskglshaderbuilder.c', - 'gl/gskglprofiler.c', - 'gl/gskglglyphcache.c', - 'gl/gskgldriver.c', - 'gl/gskglrenderops.c', - 'gl/gskglshadowcache.c', - 'gl/gskgltextureatlas.c', - 'gl/gskgliconcache.c', - 'gl/opbuffer.c', - 'gl/stb_rect_pack.c', 'ngl/gsknglattachmentstate.c', 'ngl/gsknglbuffer.c', 'ngl/gsknglcommandqueue.c', @@ -85,6 +52,8 @@ gsk_private_sources = files([ 'ngl/gskngltexturelibrary.c', 'ngl/gskngluniformstate.c', 'ngl/gskngltexturepool.c', + 'ngl/gskglprofiler.c', + 'ngl/stb_rect_pack.c', 'ngl/fp16.c', ]) @@ -106,7 +75,6 @@ gsk_public_headers = files([ install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk') gsk_public_gl_headers = files([ - 'gl/gskglrenderer.h', 'ngl/gsknglrenderer.h', ]) install_headers(gsk_public_gl_headers, subdir: 'gtk-4.0/gsk/gl') @@ -177,7 +145,6 @@ gsk_resources_xml = configure_file(output: 'gsk.resources.xml', command: [ find_program('gen-gsk-gresources-xml.py'), '@OUTPUT@', - gsk_private_gl_shaders, gsk_private_ngl_shaders, gsk_private_vulkan_compiled_shaders, gsk_private_vulkan_shaders diff --git a/gsk/ngl/gskglprofiler.c b/gsk/ngl/gskglprofiler.c new file mode 100644 index 0000000000..9b834e5fc8 --- /dev/null +++ b/gsk/ngl/gskglprofiler.c @@ -0,0 +1,181 @@ +#include "config.h" + +#include "gskglprofilerprivate.h" + +#include + +#define N_QUERIES 4 + +struct _GskGLProfiler +{ + GObject parent_instance; + + GdkGLContext *gl_context; + + /* Creating GL queries is kind of expensive, so we pay the + * price upfront and create a circular buffer of queries + */ + GLuint gl_queries[N_QUERIES]; + GLuint active_query; + + gboolean has_queries : 1; + gboolean has_timer : 1; + gboolean first_frame : 1; +}; + +enum { + PROP_GL_CONTEXT = 1, + + N_PROPERTIES +}; + +static GParamSpec *gsk_gl_profiler_properties[N_PROPERTIES]; + +G_DEFINE_TYPE (GskGLProfiler, gsk_gl_profiler, G_TYPE_OBJECT) + +static void +gsk_gl_profiler_finalize (GObject *gobject) +{ + GskGLProfiler *self = GSK_GL_PROFILER (gobject); + + if (self->has_queries) + glDeleteQueries (N_QUERIES, self->gl_queries); + + g_clear_object (&self->gl_context); + + G_OBJECT_CLASS (gsk_gl_profiler_parent_class)->finalize (gobject); +} + +static void +gsk_gl_profiler_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GskGLProfiler *self = GSK_GL_PROFILER (gobject); + + switch (prop_id) + { + case PROP_GL_CONTEXT: + self->gl_context = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +gsk_gl_profiler_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GskGLProfiler *self = GSK_GL_PROFILER (gobject); + + switch (prop_id) + { + case PROP_GL_CONTEXT: + g_value_set_object (value, self->gl_context); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +gsk_gl_profiler_class_init (GskGLProfilerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gsk_gl_profiler_set_property; + gobject_class->get_property = gsk_gl_profiler_get_property; + gobject_class->finalize = gsk_gl_profiler_finalize; + + gsk_gl_profiler_properties[PROP_GL_CONTEXT] = + g_param_spec_object ("gl-context", + "GL Context", + "The GdkGLContext used by the GL profiler", + GDK_TYPE_GL_CONTEXT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPERTIES, gsk_gl_profiler_properties); +} + +static void +gsk_gl_profiler_init (GskGLProfiler *self) +{ + self->has_queries = epoxy_is_desktop_gl(); + self->has_timer = epoxy_is_desktop_gl() && (epoxy_gl_version () >= 33 || epoxy_has_gl_extension ("GL_ARB_timer_query")); + + if (!self->has_queries) + return; + + glGenQueries (N_QUERIES, self->gl_queries); + self->first_frame = TRUE; +} + +GskGLProfiler * +gsk_gl_profiler_new (GdkGLContext *context) +{ + g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); + + return g_object_new (GSK_TYPE_GL_PROFILER, "gl-context", context, NULL); +} + +void +gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler) +{ + GLuint query_id; + + g_return_if_fail (GSK_IS_GL_PROFILER (profiler)); + + if (!profiler->has_timer || !profiler->has_queries) + return; + + query_id = profiler->gl_queries[profiler->active_query]; + glBeginQuery (GL_TIME_ELAPSED, query_id); +} + +guint64 +gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler) +{ + GLuint last_query_id; + GLint res; + GLuint64 elapsed; + + g_return_val_if_fail (GSK_IS_GL_PROFILER (profiler), 0); + + if (!profiler->has_timer || !profiler->has_queries) + return 0; + + glEndQuery (GL_TIME_ELAPSED); + + if (profiler->active_query == 0) + last_query_id = N_QUERIES - 1; + else + last_query_id = profiler->active_query - 1; + + /* Advance iterator */ + profiler->active_query += 1; + if (profiler->active_query == N_QUERIES) + profiler->active_query = 0; + + /* If this is the first frame we already have a result */ + if (profiler->first_frame) + { + profiler->first_frame = FALSE; + return 0; + } + + glGetQueryObjectiv (profiler->gl_queries[last_query_id], GL_QUERY_RESULT_AVAILABLE, &res); + if (res == 1) + glGetQueryObjectui64v (profiler->gl_queries[last_query_id], GL_QUERY_RESULT, &elapsed); + else + elapsed = 0; + + return elapsed / 1000; /* Convert to usec to match other profiler APIs */ +} diff --git a/gsk/ngl/gskglprofilerprivate.h b/gsk/ngl/gskglprofilerprivate.h new file mode 100644 index 0000000000..5b2a24b09f --- /dev/null +++ b/gsk/ngl/gskglprofilerprivate.h @@ -0,0 +1,18 @@ +#ifndef __GSK_GL_PROFILER_PRIVATE_H__ +#define __GSK_GL_PROFILER_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +#define GSK_TYPE_GL_PROFILER (gsk_gl_profiler_get_type ()) +G_DECLARE_FINAL_TYPE (GskGLProfiler, gsk_gl_profiler, GSK, GL_PROFILER, GObject) + +GskGLProfiler * gsk_gl_profiler_new (GdkGLContext *context); + +void gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler); +guint64 gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler); + +G_END_DECLS + +#endif /* __GSK_GL_PROFILER_PRIVATE_H__ */ diff --git a/gsk/ngl/gsknglcommandqueueprivate.h b/gsk/ngl/gsknglcommandqueueprivate.h index a2f50c485d..b13838d48e 100644 --- a/gsk/ngl/gsknglcommandqueueprivate.h +++ b/gsk/ngl/gsknglcommandqueueprivate.h @@ -30,7 +30,7 @@ #include "inlinearray.h" -#include "../gl/gskglprofilerprivate.h" +#include "gskglprofilerprivate.h" G_BEGIN_DECLS diff --git a/gsk/ngl/gskngltexturelibraryprivate.h b/gsk/ngl/gskngltexturelibraryprivate.h index 05c2216ed7..13e651e0d7 100644 --- a/gsk/ngl/gskngltexturelibraryprivate.h +++ b/gsk/ngl/gskngltexturelibraryprivate.h @@ -24,7 +24,7 @@ #include "gskngltypesprivate.h" #include "gskngltexturepoolprivate.h" -#include "../gl/stb_rect_pack.h" +#include "stb_rect_pack.h" G_BEGIN_DECLS diff --git a/gsk/ngl/stb_rect_pack.c b/gsk/ngl/stb_rect_pack.c new file mode 100644 index 0000000000..ff658466a1 --- /dev/null +++ b/gsk/ngl/stb_rect_pack.c @@ -0,0 +1,431 @@ + +#include "stb_rect_pack.h" +#define STB_RECT_PACK_IMPLEMENTATION +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT +#include +#define STBRP_SORT qsort +#endif + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +#ifdef _MSC_VER +#define STBRP__NOTUSED(v) (void)(v) +#else +#define STBRP__NOTUSED(v) (void)sizeof(v) +#endif + +enum +{ + STBRP__INIT_skyline = 1 +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + + STBRP__NOTUSED(c); + + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visited + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + int count=0; + cur = context->active_head; + while (cur) { + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_original_order(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i, all_rects_packed = 1; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + } + + // sort according to heuristic + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + if (rects[i].w == 0 || rects[i].h == 0) { + rects[i].x = rects[i].y = 0; // empty rect needs no space + } else { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + } + + // unsort + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags and all_rects_packed status + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); + if (!rects[i].was_packed) + all_rects_packed = 0; + } + + // return the all_rects_packed status + return all_rects_packed; +} +#endif + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/gsk/ngl/stb_rect_pack.h b/gsk/ngl/stb_rect_pack.h new file mode 100644 index 0000000000..3ecb1c2fa2 --- /dev/null +++ b/gsk/ngl/stb_rect_pack.h @@ -0,0 +1,191 @@ +// stb_rect_pack.h - v0.99 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// github:IntellectualKitty +// +// Bugfixes / warning fixes +// Jeremy Jaussaud +// +// Version history: +// +// 0.99 (2019-02-07) warning fixes +// 0.11 (2017-03-03) return packing success/fail result +// 0.10 (2016-10-25) remove cast-away-const to avoid warnings +// 0.09 (2016-08-27) fix compiler warnings +// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) +// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release +// +// LICENSE +// +// See end of file for license information. + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. +// +// The function returns 1 if all of the rectangles were successfully +// packed and 0 otherwise. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + -- cgit v1.2.1