mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-14 14:20:21 +00:00
gl renderer: Support large textures
By tiling them.
This commit is contained in:
parent
3f367277fe
commit
0124740fa0
@ -25,6 +25,10 @@ typedef struct {
|
|||||||
GdkTexture *user;
|
GdkTexture *user;
|
||||||
guint in_use : 1;
|
guint in_use : 1;
|
||||||
guint permanent : 1;
|
guint permanent : 1;
|
||||||
|
|
||||||
|
/* TODO: Make this optional and not for every texture... */
|
||||||
|
TextureSlice *slices;
|
||||||
|
guint n_slices;
|
||||||
} Texture;
|
} Texture;
|
||||||
|
|
||||||
struct _GskGLDriver
|
struct _GskGLDriver
|
||||||
@ -72,6 +76,7 @@ static void
|
|||||||
texture_free (gpointer data)
|
texture_free (gpointer data)
|
||||||
{
|
{
|
||||||
Texture *t = data;
|
Texture *t = data;
|
||||||
|
guint i;
|
||||||
|
|
||||||
if (t->user)
|
if (t->user)
|
||||||
gdk_texture_clear_render_data (t->user);
|
gdk_texture_clear_render_data (t->user);
|
||||||
@ -79,10 +84,32 @@ texture_free (gpointer data)
|
|||||||
if (t->fbo.fbo_id != 0)
|
if (t->fbo.fbo_id != 0)
|
||||||
fbo_clear (&t->fbo);
|
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);
|
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
|
static void
|
||||||
gsk_gl_driver_finalize (GObject *gobject)
|
gsk_gl_driver_finalize (GObject *gobject)
|
||||||
@ -313,8 +340,8 @@ create_texture (GskGLDriver *self,
|
|||||||
g_assert (width > 0);
|
g_assert (width > 0);
|
||||||
g_assert (height > 0);
|
g_assert (height > 0);
|
||||||
|
|
||||||
if (width >= self->max_texture_size ||
|
if (width > self->max_texture_size ||
|
||||||
height >= self->max_texture_size)
|
height > self->max_texture_size)
|
||||||
{
|
{
|
||||||
g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...",
|
g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...",
|
||||||
width, height,
|
width, height,
|
||||||
@ -362,6 +389,108 @@ gsk_gl_driver_release_texture (gpointer data)
|
|||||||
t->user = NULL;
|
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
|
int
|
||||||
gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
|
gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
|
||||||
GdkTexture *texture,
|
GdkTexture *texture,
|
||||||
@ -386,7 +515,7 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* A GL texture from the same GL context is a simple task... */
|
/* 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
|
else
|
||||||
@ -573,17 +702,6 @@ gsk_gl_driver_destroy_texture (GskGLDriver *self,
|
|||||||
g_hash_table_remove (self->textures, GINT_TO_POINTER (texture_id));
|
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
|
void
|
||||||
gsk_gl_driver_init_texture_empty (GskGLDriver *self,
|
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)
|
if (t->min_filter != GL_NEAREST)
|
||||||
glGenerateMipmap (GL_TEXTURE_2D);
|
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;
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,12 @@ typedef struct {
|
|||||||
float uv[2];
|
float uv[2];
|
||||||
} GskQuadVertex;
|
} GskQuadVertex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
cairo_rectangle_int_t rect;
|
||||||
|
guint texture_id;
|
||||||
|
} TextureSlice;
|
||||||
|
|
||||||
|
|
||||||
GskGLDriver * gsk_gl_driver_new (GdkGLContext *context);
|
GskGLDriver * gsk_gl_driver_new (GdkGLContext *context);
|
||||||
|
|
||||||
int gsk_gl_driver_get_max_texture_size (GskGLDriver *driver);
|
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,
|
int gsk_gl_driver_create_texture (GskGLDriver *driver,
|
||||||
float width,
|
float width,
|
||||||
float height);
|
float height);
|
||||||
gboolean gsk_gl_driver_texture_needs_tiling (GskGLDriver *driver,
|
|
||||||
GdkTexture *texture);
|
|
||||||
int gsk_gl_driver_create_render_target (GskGLDriver *driver,
|
int gsk_gl_driver_create_render_target (GskGLDriver *driver,
|
||||||
int texture_id,
|
int texture_id,
|
||||||
gboolean add_depth_buffer,
|
gboolean add_depth_buffer,
|
||||||
@ -57,6 +61,10 @@ void gsk_gl_driver_destroy_texture (GskGLDriver *driver
|
|||||||
int texture_id);
|
int texture_id);
|
||||||
|
|
||||||
int gsk_gl_driver_collect_textures (GskGLDriver *driver);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -556,17 +556,47 @@ render_texture_node (GskGLRenderer *self,
|
|||||||
RenderOpBuilder *builder)
|
RenderOpBuilder *builder)
|
||||||
{
|
{
|
||||||
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
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
|
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 gl_min_filter = GL_NEAREST, gl_mag_filter = GL_NEAREST;
|
||||||
int texture_id;
|
int texture_id;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user