From b28c3ef3d96edb17e3c819895177bb4293ac4790 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 25 Feb 2022 03:40:57 +0100 Subject: [PATCH] renderers: Handle large viewports When large viewports are passed to gsk_renderer_render_texture(), don't fail (or even return NULL). Instead, draw multiple tiles and assemble them into a memory texture. Tests added to the testsuite for this. --- gsk/gl/gskglrenderer.c | 40 +++++++++++++++++++++++-- gsk/gskcairorenderer.c | 38 ++++++++++++++++++++++- testsuite/gsk/compare/huge-height.node | 7 +++++ testsuite/gsk/compare/huge-height.png | Bin 0 -> 689 bytes testsuite/gsk/compare/huge-width.node | 7 +++++ testsuite/gsk/compare/huge-width.png | Bin 0 -> 657 bytes testsuite/gsk/meson.build | 2 ++ 7 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 testsuite/gsk/compare/huge-height.node create mode 100644 testsuite/gsk/compare/huge-height.png create mode 100644 testsuite/gsk/compare/huge-width.node create mode 100644 testsuite/gsk/compare/huge-width.png diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index fe244a7a4f..d1b40d225e 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -306,10 +306,9 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer, GskGLRenderer *self = (GskGLRenderer *)renderer; GskGLRenderTarget *render_target; GskGLRenderJob *job; - GdkTexture *texture = NULL; + GdkTexture *texture; guint texture_id; - int width; - int height; + int width, height, max_size; int format; g_assert (GSK_IS_GL_RENDERER (renderer)); @@ -317,6 +316,37 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer, width = ceilf (viewport->size.width); height = ceilf (viewport->size.height); + max_size = self->command_queue->max_texture_size; + if (width > max_size || height > max_size) + { + gsize x, y, size, stride; + GBytes *bytes; + guchar *data; + + stride = width * 4; + size = stride * height; + data = g_malloc_n (stride, height); + + for (y = 0; y < height; y += max_size) + { + for (x = 0; x < width; x += max_size) + { + texture = gsk_gl_renderer_render_texture (renderer, root, + &GRAPHENE_RECT_INIT (x, y, + MIN (max_size, viewport->size.width - x), + MIN (max_size, viewport->size.height - y))); + gdk_texture_download (texture, + data + stride * y + x * 4, + stride); + g_object_unref (texture); + } + } + + bytes = g_bytes_new_take (data, size); + texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride); + g_bytes_unref (bytes); + return texture; + } format = gsk_render_node_prefers_high_depth (root) ? GL_RGBA32F : GL_RGBA8; @@ -342,6 +372,10 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer, gsk_gl_driver_after_frame (self->driver); } + else + { + g_assert_not_reached (); + } return g_steal_pointer (&texture); } diff --git a/gsk/gskcairorenderer.c b/gsk/gskcairorenderer.c index 3135acfdb1..24675ce35f 100644 --- a/gsk/gskcairorenderer.c +++ b/gsk/gskcairorenderer.c @@ -107,8 +107,44 @@ gsk_cairo_renderer_render_texture (GskRenderer *renderer, GdkTexture *texture; cairo_surface_t *surface; cairo_t *cr; + int width, height; + /* limit from cairo's source code */ +#define MAX_IMAGE_SIZE 32767 - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ceil (viewport->size.width), ceil (viewport->size.height)); + width = ceil (viewport->size.width); + height = ceil (viewport->size.height); + if (width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE) + { + gsize x, y, size, stride; + GBytes *bytes; + guchar *data; + + stride = width * 4; + size = stride * height; + data = g_malloc_n (stride, height); + + for (y = 0; y < height; y += MAX_IMAGE_SIZE) + { + for (x = 0; x < width; x += MAX_IMAGE_SIZE) + { + texture = gsk_cairo_renderer_render_texture (renderer, root, + &GRAPHENE_RECT_INIT (x, y, + MIN (MAX_IMAGE_SIZE, viewport->size.width - x), + MIN (MAX_IMAGE_SIZE, viewport->size.height - y))); + gdk_texture_download (texture, + data + stride * y + x * 4, + stride); + g_object_unref (texture); + } + } + + bytes = g_bytes_new_take (data, size); + texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride); + g_bytes_unref (bytes); + return texture; + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create (surface); cairo_translate (cr, - viewport->origin.x, - viewport->origin.y); diff --git a/testsuite/gsk/compare/huge-height.node b/testsuite/gsk/compare/huge-height.node new file mode 100644 index 0000000000..ce33081ff8 --- /dev/null +++ b/testsuite/gsk/compare/huge-height.node @@ -0,0 +1,7 @@ +color { + color: transparent; + /* - more than 32k, to trip modern GPUs and Cairo + * - non-integer to test rounding code + */ + bounds: 0 0 135.7 33333.3; +} diff --git a/testsuite/gsk/compare/huge-height.png b/testsuite/gsk/compare/huge-height.png new file mode 100644 index 0000000000000000000000000000000000000000..0162f5307ba54a20143f54fc2b064efcab3d63d1 GIT binary patch literal 689 zcmeAS@N?(olHy`uVBq!ia0vp^9SjUjW{k{0mZj~E^FWF@z$e5NNH4Fly#=HgOM?7@ z862M7NMm4Nit%)D43Ut0d(e;(D0N8T_wqPi{sbU%6pV%dWg&2fTO*i(G4pF~FUTBE LS3j3^P6