Correctly upload textures for GLES

GLES doesn't support the GL_BGRA +  GL_UNSIGNED_INT_24_8 hack that
we use on desktop OpenGL to upload textures directly in the cairo
pixel format. This adds the required conversions to all the places
that currently need it.

We also add a data_format to the internal gdk_gl_context_upload_texture()
function to make it clearer what the format are. Currently it is always
the cairo image surface format, but eventually we want to support other
formats so that we can avoid some of the unnecessary conversions we do.

Also, the current gdk_gl_context_upload_texture() code always converts
to a cairo format and uploads that like we did before. Later commits
will allow this to use other upload formats that gl supports to avoid
conversions.
This commit is contained in:
Alexander Larsson 2020-09-24 15:03:48 +02:00
parent 8e59cdabac
commit 1001995d49
5 changed files with 119 additions and 50 deletions

View File

@ -482,6 +482,7 @@ gdk_cairo_surface_upload_to_gl (cairo_surface_t *surface,
rect.width, rect.width,
rect.height, rect.height,
cairo_image_surface_get_stride (tmp), cairo_image_surface_get_stride (tmp),
GDK_MEMORY_DEFAULT,
target); target);
cairo_surface_unmap_image (surface, tmp); cairo_surface_unmap_image (surface, tmp);

View File

@ -87,6 +87,7 @@
#include "gdkglcontextprivate.h" #include "gdkglcontextprivate.h"
#include "gdkdisplayprivate.h" #include "gdkdisplayprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkinternals.h" #include "gdkinternals.h"
#include "gdkintl.h" #include "gdkintl.h"
@ -227,49 +228,81 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
int width, int width,
int height, int height,
int stride, int stride,
GdkMemoryFormat data_format,
guint texture_target) guint texture_target)
{ {
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
guchar *copy = NULL;
guint gl_format;
guint gl_type;
g_return_if_fail (GDK_IS_GL_CONTEXT (context)); g_return_if_fail (GDK_IS_GL_CONTEXT (context));
if (priv->use_es)
{
/* GLES only supports rgba, so convert if necessary */
if (data_format != GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
{
copy = g_malloc (width * height * 4);
gdk_memory_convert (copy, width * 4,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
data, stride, data_format,
width, height);
stride = width * 4;
data = copy;
}
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
else
{
if (data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */
{
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
else /* Fall-back, convert to cairo-surface-format */
{
copy = g_malloc (width * height * 4);
gdk_memory_convert (copy, width * 4,
GDK_MEMORY_DEFAULT,
data, stride, data_format,
width, height);
stride = width * 4;
data = copy;
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
}
/* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
* the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
*/ */
if (!priv->use_es || if (stride == width * 4)
(priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage))) {
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
}
else if (!priv->use_es ||
(priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage)))
{ {
glPixelStorei (GL_UNPACK_ALIGNMENT, 4); glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4); glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4);
if (priv->use_es) glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
data);
else
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
data);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
} }
else else
{ {
int i; int i;
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, NULL);
if (priv->use_es) for (i = 0; i < height; i++)
{ glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
for (i = 0; i < height; i++)
glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, data + (i * stride));
}
else
{
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
for (i = 0; i < height; i++)
glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data + (i * stride));
}
} }
g_free (copy);
} }
static gboolean static gboolean

View File

@ -23,6 +23,7 @@
#include "gdkglcontext.h" #include "gdkglcontext.h"
#include "gdkdrawcontextprivate.h" #include "gdkdrawcontextprivate.h"
#include "gdkmemorytexture.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -84,6 +85,7 @@ void gdk_gl_context_upload_texture (GdkGLContext
int width, int width,
int height, int height,
int stride, int stride,
GdkMemoryFormat data_format,
guint texture_target); guint texture_target);
GdkGLContextPaintData * gdk_gl_context_get_paint_data (GdkGLContext *context); GdkGLContextPaintData * gdk_gl_context_get_paint_data (GdkGLContext *context);
gboolean gdk_gl_context_use_texture_rectangle (GdkGLContext *context); gboolean gdk_gl_context_use_texture_rectangle (GdkGLContext *context);

View File

@ -7,6 +7,7 @@
#include "gskgltextureatlasprivate.h" #include "gskgltextureatlasprivate.h"
#include "gdk/gdkglcontextprivate.h" #include "gdk/gdkglcontextprivate.h"
#include "gdk/gdkmemorytextureprivate.h"
#include <graphene.h> #include <graphene.h>
#include <cairo.h> #include <cairo.h>
@ -186,6 +187,10 @@ upload_glyph (GlyphCacheKey *key,
GskGLCachedGlyph *value) GskGLCachedGlyph *value)
{ {
GskImageRegion r; 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 (), gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Uploading glyph %d", "Uploading glyph %d",
@ -197,15 +202,27 @@ upload_glyph (GlyphCacheKey *key,
glBindTexture (GL_TEXTURE_2D, value->texture_id); glBindTexture (GL_TEXTURE_2D, value->texture_id);
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height, {
GL_RGBA, GL_UNSIGNED_BYTE, pixel_data = free_data = g_malloc (r.width * r.height * 4);
r.data); 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 else
glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height, {
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixel_data = r.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); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
g_free (r.data); g_free (r.data);
g_free (free_data);
} }
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ()); gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());

View File

@ -1,6 +1,7 @@
#include "gskgliconcacheprivate.h" #include "gskgliconcacheprivate.h"
#include "gskgltextureatlasprivate.h" #include "gskgltextureatlasprivate.h"
#include "gdk/gdktextureprivate.h" #include "gdk/gdktextureprivate.h"
#include "gdk/gdkmemorytextureprivate.h"
#include "gdk/gdkglcontextprivate.h" #include "gdk/gdkglcontextprivate.h"
#include <epoxy/gl.h> #include <epoxy/gl.h>
@ -134,7 +135,10 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
int packed_y = 0; int packed_y = 0;
cairo_surface_t *surface; cairo_surface_t *surface;
unsigned char *surface_data; unsigned char *surface_data;
unsigned char *pixel_data;
guchar *free_data = NULL;
guint gl_format; guint gl_format;
guint gl_type;
gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y); gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
@ -158,36 +162,47 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
"Uploading texture"); "Uploading texture");
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
gl_format = GL_RGBA; {
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 else
gl_format = GL_BGRA; {
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); glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1, packed_x + 1, packed_y + 1,
width, height, width, height,
gl_format, gl_format, gl_type,
GL_UNSIGNED_BYTE, pixel_data);
surface_data);
/* Padding top */ /* Padding top */
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y, packed_x + 1, packed_y,
width, 1, width, 1,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Padding left */ /* Padding left */
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1, packed_x, packed_y + 1,
1, height, 1, height,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Padding top left */ /* Padding top left */
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y, packed_x, packed_y,
1, 1, 1, 1,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Padding right */ /* Padding right */
glPixelStorei (GL_UNPACK_ROW_LENGTH, width); glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
@ -195,14 +210,14 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y + 1, packed_x + width + 1, packed_y + 1,
1, height, 1, height,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Padding top right */ /* Padding top right */
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y, packed_x + width + 1, packed_y,
1, 1, 1, 1,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Padding bottom */ /* Padding bottom */
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
@ -210,22 +225,22 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1 + height, packed_x + 1, packed_y + 1 + height,
width, 1, width, 1,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Padding bottom left */ /* Padding bottom left */
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1 + height, packed_x, packed_y + 1 + height,
1, 1, 1, 1,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Padding bottom right */ /* Padding bottom right */
glPixelStorei (GL_UNPACK_ROW_LENGTH, width); glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
glTexSubImage2D (GL_TEXTURE_2D, 0, glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1 + width, packed_y + 1 + height, packed_x + 1 + width, packed_y + 1 + height,
1, 1, 1, 1,
gl_format, GL_UNSIGNED_BYTE, gl_format, gl_type,
surface_data); pixel_data);
/* Reset this */ /* Reset this */
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
@ -236,6 +251,7 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
*out_icon_data = icon_data; *out_icon_data = icon_data;
cairo_surface_destroy (surface); cairo_surface_destroy (surface);
g_free (free_data);
#if 0 #if 0
{ {