[gtk: 5/7] gl backend: Avoid roundtripping via surface when updloading
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk: 5/7] gl backend: Avoid roundtripping via surface when updloading
- Date: Fri, 25 Sep 2020 14:53:20 +0000 (UTC)
commit 90fc671ef8ffcddaf7dac4a2a72c9094f7ef57d3
Author: Alexander Larsson <alexl redhat com>
Date: Thu Sep 24 16:58:46 2020 +0200
gl backend: Avoid roundtripping via surface when updloading
Do custom uploads rather than using gdk_cairo_surface_upload_to_gl(),
because this way we avoids a roundtrip (memcpy and possibly conversion)
to the cairo image surface format.
gdk/gdkmemorytexture.c | 2 +-
gdk/gdkmemorytextureprivate.h | 2 +
gsk/gl/gskgldriver.c | 107 ++++++++++++++++++++++++++++--------------
gsk/gl/gskgldriverprivate.h | 4 +-
gsk/gl/gskglrenderer.c | 13 +++--
5 files changed, 86 insertions(+), 42 deletions(-)
---
diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c
index 5ed0b988dc..663d0409f9 100644
--- a/gdk/gdkmemorytexture.c
+++ b/gdk/gdkmemorytexture.c
@@ -38,7 +38,7 @@ struct _GdkMemoryTextureClass
G_DEFINE_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK_TYPE_TEXTURE)
-static gsize
+gsize
gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
{
switch (format)
diff --git a/gdk/gdkmemorytextureprivate.h b/gdk/gdkmemorytextureprivate.h
index caa4b2014c..740a59da6e 100644
--- a/gdk/gdkmemorytextureprivate.h
+++ b/gdk/gdkmemorytextureprivate.h
@@ -31,6 +31,8 @@ G_BEGIN_DECLS
#define GDK_MEMORY_CAIRO_FORMAT_ARGB32 GDK_MEMORY_DEFAULT
+gsize gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format);
+
GdkMemoryFormat gdk_memory_texture_get_format (GdkMemoryTexture *self);
const guchar * gdk_memory_texture_get_data (GdkMemoryTexture *self);
gsize gdk_memory_texture_get_stride (GdkMemoryTexture *self);
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 8d101b93df..a383344eae 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -7,6 +7,7 @@
#include "gdk/gdkglcontextprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkgltextureprivate.h"
+#include "gdkmemorytextureprivate.h"
#include <gdk/gdk.h>
#include <epoxy/gl.h>
@@ -58,6 +59,54 @@ struct _GskGLDriver
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)
{
@@ -408,32 +457,15 @@ gsk_gl_driver_slice_texture (GskGLDriver *self,
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);
@@ -442,7 +474,7 @@ gsk_gl_driver_slice_texture (GskGLDriver *self,
#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);
+ 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);
@@ -451,9 +483,6 @@ gsk_gl_driver_slice_texture (GskGLDriver *self,
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;
}
@@ -486,7 +515,8 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
int mag_filter)
{
Texture *t;
- cairo_surface_t *surface;
+ GdkTexture *downloaded_texture = NULL;
+ GdkTexture *source_texture;
if (GDK_IS_GL_TEXTURE (texture))
{
@@ -494,14 +524,20 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
if (texture_context != self->gl_context)
{
+ 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
{
@@ -519,7 +555,7 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
return t->texture_id;
}
- surface = gdk_texture_download_surface (texture);
+ source_texture = texture;
}
t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
@@ -528,15 +564,16 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
t->user = texture;
gsk_gl_driver_bind_source_texture (self, t->texture_id);
- gsk_gl_driver_init_texture_with_surface (self,
- t->texture_id,
- surface,
- min_filter,
- mag_filter);
+ 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);
- cairo_surface_destroy (surface);
+ if (downloaded_texture)
+ g_object_unref (downloaded_texture);
return t->texture_id;
}
@@ -769,11 +806,11 @@ filter_uses_mipmaps (int filter)
}
void
-gsk_gl_driver_init_texture_with_surface (GskGLDriver *self,
- int texture_id,
- cairo_surface_t *surface,
- int min_filter,
- int mag_filter)
+gsk_gl_driver_init_texture (GskGLDriver *self,
+ int texture_id,
+ GdkTexture *texture,
+ int min_filter,
+ int mag_filter)
{
Texture *t;
@@ -794,7 +831,7 @@ gsk_gl_driver_init_texture_with_surface (GskGLDriver *self,
gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
- gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, t->width, t->height, NULL);
+ 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);
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index 406b959372..0bf9ca89d2 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -63,9 +63,9 @@ void gsk_gl_driver_init_texture_empty (GskGLDriver *driver
int texture_id,
int min_filter,
int max_filter);
-void gsk_gl_driver_init_texture_with_surface (GskGLDriver *driver,
+void gsk_gl_driver_init_texture (GskGLDriver *driver,
int texture_id,
- cairo_surface_t *surface,
+ GdkTexture *texture,
int min_filter,
int mag_filter);
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 96aecbd70b..7998cbfd34 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -564,6 +564,7 @@ render_fallback_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
+ GdkTexture *texture;
const float scale = ops_get_scale (builder);
const int surface_width = ceilf (node->bounds.size.width * scale);
const int surface_height = ceilf (node->bounds.size.height * scale);
@@ -645,15 +646,18 @@ render_fallback_node (GskGLRenderer *self,
#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);
- gsk_gl_driver_init_texture_with_surface (self->gl_driver,
- texture_id,
- surface,
- GL_NEAREST, GL_NEAREST);
+
+ 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,
@@ -661,6 +665,7 @@ render_fallback_node (GskGLRenderer *self,
g_type_name_from_instance ((GTypeInstance *) node),
texture_id);
+ g_object_unref (texture);
cairo_surface_destroy (surface);
cairo_surface_destroy (rendered_surface);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]