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;
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user