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.
This commit is contained in:
Benjamin Otte 2022-02-25 03:40:57 +01:00 committed by Matthias Clasen
parent 0ea0fc9c7b
commit 50554bb92b
7 changed files with 90 additions and 4 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

View File

@ -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 33333.3 135.7;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

View File

@ -58,6 +58,8 @@ compare_render_tests = [
'empty-shadow',
'empty-texture',
'empty-transform',
'huge-height',
'huge-width',
'inset-shadow-multiple',
'invalid-transform',
'issue-3615',