summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimm Bäder <mail@baedert.org>2018-03-20 09:17:26 +0100
committerTimm Bäder <mail@baedert.org>2018-03-20 09:40:10 +0100
commit0124740fa01d9b50886c4030db34d71716c35c16 (patch)
tree2322181a265239e0f538698ae2b6120eb185dfd5
parent3f367277fef5d6cdf921428279ba54904b8c9201 (diff)
downloadgtk+-0124740fa01d9b50886c4030db34d71716c35c16.tar.gz
gl renderer: Support large textures
By tiling them.
-rw-r--r--gsk/gl/gskgldriver.c159
-rw-r--r--gsk/gl/gskgldriverprivate.h12
-rw-r--r--gsk/gl/gskglrenderer.c40
3 files changed, 178 insertions, 33 deletions
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index c97522b812..3f6c4c70d0 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -25,6 +25,10 @@ typedef struct {
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
@@ -72,6 +76,7 @@ static void
texture_free (gpointer data)
{
Texture *t = data;
+ guint i;
if (t->user)
gdk_texture_clear_render_data (t->user);
@@ -79,10 +84,32 @@ texture_free (gpointer data)
if (t->fbo.fbo_id != 0)
fbo_clear (&t->fbo);
- glDeleteTextures (1, &t->texture_id);
+ 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)
@@ -313,8 +340,8 @@ create_texture (GskGLDriver *self,
g_assert (width > 0);
g_assert (height > 0);
- if (width >= self->max_texture_size ||
- height >= self->max_texture_size)
+ 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,
@@ -362,6 +389,108 @@ gsk_gl_driver_release_texture (gpointer 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 tex_width = texture->width;
+ const int tex_height = texture->height;
+ 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 (tex_width > max_texture_size || tex_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);
+
+ /* TODO: (Perf):
+ * We still create a surface here, which should obviously be unnecessary
+ * and we should eventually remove it and upload the data directly.
+ */
+ for (col = 0; col < cols; col ++)
+ {
+ const int slice_width = MIN (max_texture_size, texture->width - x);
+ const int stride = slice_width * 4;
+
+ for (row = 0; row < rows; row ++)
+ {
+ const int slice_height = MIN (max_texture_size, texture->height - y);
+ const int slice_index = (col * rows) + row;
+ guchar *data;
+ guint texture_id;
+ cairo_surface_t *surface;
+
+ data = g_malloc (sizeof (guchar) * stride * slice_height);
+
+ gdk_texture_download_area (texture,
+ &(GdkRectangle){x, y, slice_width, slice_height},
+ data, stride);
+ surface = cairo_image_surface_create_for_data (data,
+ CAIRO_FORMAT_ARGB32,
+ slice_width, slice_height,
+ stride);
+
+ 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);
+ gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, slice_width, slice_height, NULL);
+
+#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;
+
+ g_free (data);
+ cairo_surface_destroy (surface);
+
+ 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,
@@ -386,7 +515,7 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
else
{
/* A GL texture from the same GL context is a simple task... */
- return gdk_gl_texture_get_id (GDK_GL_TEXTURE (texture));
+ return gdk_gl_texture_get_id ((GdkGLTexture *)texture);
}
}
else
@@ -573,17 +702,6 @@ gsk_gl_driver_destroy_texture (GskGLDriver *self,
g_hash_table_remove (self->textures, GINT_TO_POINTER (texture_id));
}
-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);
-}
void
gsk_gl_driver_init_texture_empty (GskGLDriver *self,
@@ -654,14 +772,3 @@ gsk_gl_driver_init_texture_with_surface (GskGLDriver *self,
if (t->min_filter != GL_NEAREST)
glGenerateMipmap (GL_TEXTURE_2D);
}
-
-gboolean
-gsk_gl_driver_texture_needs_tiling (GskGLDriver *self,
- GdkTexture *texture)
-{
- int max = gsk_gl_driver_get_max_texture_size (self);
-
- g_assert (self->max_texture_size > -1);
-
- return texture->width > max || texture->height > max;
-}
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index 04f97a0661..7d7a41fe28 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -16,6 +16,12 @@ typedef struct {
float uv[2];
} GskQuadVertex;
+typedef struct {
+ cairo_rectangle_int_t rect;
+ guint texture_id;
+} TextureSlice;
+
+
GskGLDriver * gsk_gl_driver_new (GdkGLContext *context);
int gsk_gl_driver_get_max_texture_size (GskGLDriver *driver);
@@ -33,8 +39,6 @@ int gsk_gl_driver_create_permanent_texture (GskGLDriver *driver
int gsk_gl_driver_create_texture (GskGLDriver *driver,
float width,
float height);
-gboolean gsk_gl_driver_texture_needs_tiling (GskGLDriver *driver,
- GdkTexture *texture);
int gsk_gl_driver_create_render_target (GskGLDriver *driver,
int texture_id,
gboolean add_depth_buffer,
@@ -57,6 +61,10 @@ 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
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index f41ec1a64c..127f50e0f1 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -556,17 +556,47 @@ render_texture_node (GskGLRenderer *self,
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);
+ 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;
- if (gsk_gl_driver_texture_needs_tiling (self->gl_driver, texture))
+ if (texture->width > max_texture_size || texture->height > max_texture_size)
{
+ 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->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
{
- 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;
int gl_min_filter = GL_NEAREST, gl_mag_filter = GL_NEAREST;
int texture_id;