Compare commits

...

1 Commits

Author SHA1 Message Date
Matthias Clasen
f502743621 gpu: Never create empty images
Creating a Vulkan image with width or height zero is an error,
so check at the time when we compute the device pixel rect.

The node file in the included test was devised by Benjamin Otte.

Fixes: #6691
2024-05-06 22:10:38 -04:00
4 changed files with 78 additions and 18 deletions

View File

@@ -311,7 +311,7 @@ gsk_gpu_node_processor_add_images (GskGpuNodeProcessor *self,
while (desc != self->desc);
}
static void
static gboolean G_GNUC_WARN_UNUSED_RESULT
rect_round_to_pixels (const graphene_rect_t *src,
const graphene_vec2_t *pixel_scale,
const graphene_point_t *pixel_offset,
@@ -331,6 +331,8 @@ rect_round_to_pixels (const graphene_rect_t *src,
y * inv_yscale - pixel_offset->y,
(ceilf ((src->origin.x + pixel_offset->x + src->size.width) * xscale) - x) * inv_xscale,
(ceilf ((src->origin.y + pixel_offset->y + src->size.height) * yscale) - y) * inv_yscale);
return ceilf (xscale * dest->size.width) > 0 && ceilf (yscale * dest->size.height) > 0;
}
static GskGpuImage *
@@ -963,7 +965,9 @@ gsk_gpu_node_processor_get_node_as_image (GskGpuNodeProcessor *self,
if (!gsk_rect_intersection (clip_bounds, &node->bounds, &clip))
return NULL;
}
rect_round_to_pixels (&clip, &self->scale, &self->offset, &clip);
if (!rect_round_to_pixels (&clip, &self->scale, &self->offset, &clip))
return NULL;
image = gsk_gpu_get_node_as_image (self->frame,
&clip,
@@ -1020,7 +1024,8 @@ gsk_gpu_node_processor_blur_op (GskGpuNodeProcessor *self,
if (!gsk_rect_intersection (rect, &clip_rect, &intermediate_rect))
return;
rect_round_to_pixels (&intermediate_rect, &self->scale, &self->offset, &intermediate_rect);
if (!rect_round_to_pixels (&intermediate_rect, &self->scale, &self->offset, &intermediate_rect))
return;
intermediate = gsk_gpu_node_processor_init_draw (&other,
self->frame,
@@ -1083,7 +1088,8 @@ gsk_gpu_node_processor_add_fallback_node (GskGpuNodeProcessor *self,
if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &clipped_bounds))
return;
rect_round_to_pixels (&clipped_bounds, &self->scale, &self->offset, &clipped_bounds);
if (!rect_round_to_pixels (&clipped_bounds, &self->scale, &self->offset, &clipped_bounds))
return;
gsk_gpu_node_processor_sync_globals (self, 0);
@@ -1439,7 +1445,9 @@ gsk_gpu_node_processor_add_rounded_clip_node_with_mask (GskGpuNodeProcessor *sel
if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &clip_bounds))
return;
rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds);
if (!rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds))
return;
child_image = gsk_gpu_node_processor_get_node_as_image (self,
0,
@@ -2068,7 +2076,9 @@ gsk_gpu_node_processor_add_texture_scale_node (GskGpuNodeProcessor *self,
gsk_gpu_node_processor_get_clip_bounds (self, &clip_bounds);
/* first round to pixel boundaries, so we make sure the full pixels are covered */
rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds);
if (!rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds))
return;
/* then expand by half a pixel so that pixels needed for eventual linear
* filtering are available */
graphene_rect_inset (&clip_bounds, -0.5, -0.5);
@@ -2276,7 +2286,9 @@ gsk_gpu_node_processor_add_gradient_node (GskGpuNodeProcessor *self,
if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &bounds))
return;
rect_round_to_pixels (&bounds, &self->scale, &self->offset, &bounds);
if (!rect_round_to_pixels (&bounds, &self->scale, &self->offset, &bounds))
return;
image = gsk_gpu_node_processor_init_draw (&other,
self->frame,
@@ -3581,7 +3593,9 @@ gsk_gpu_node_processor_add_fill_node (GskGpuNodeProcessor *self,
if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &clip_bounds))
return;
rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds);
if (!rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds))
return;
child = gsk_fill_node_get_child (node);
@@ -3678,7 +3692,9 @@ gsk_gpu_node_processor_add_stroke_node (GskGpuNodeProcessor *self,
if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &clip_bounds))
return;
rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds);
if (!rect_round_to_pixels (&clip_bounds, &self->scale, &self->offset, &clip_bounds))
return;
child = gsk_stroke_node_get_child (node);
@@ -4111,15 +4127,17 @@ gsk_gpu_node_processor_create_node_pattern (GskGpuPatternWriter *self,
gsk_gpu_descriptors_set_size (self->desc, images_before, buffers_before);
}
rect_round_to_pixels (&GRAPHENE_RECT_INIT (
self->bounds.origin.x - self->offset.x,
self->bounds.origin.y - self->offset.y,
self->bounds.size.width,
self->bounds.size.height
),
&self->scale,
&self->offset,
&bounds);
if (!rect_round_to_pixels (&GRAPHENE_RECT_INIT (
self->bounds.origin.x - self->offset.x,
self->bounds.origin.y - self->offset.y,
self->bounds.size.width,
self->bounds.size.height
),
&self->scale,
&self->offset,
&bounds))
return TRUE;
image = gsk_gpu_get_node_as_image (self->frame,
&bounds,
&self->scale,

View File

@@ -0,0 +1,41 @@
clip {
/* Numbers that trigger rounding errors at 125%:
9, 13, 18, 21, 26, 31, 36, 42, 47, 52, 57, 62, 67, 72, 77, 84, 89, 94, 99, ...
*/
clip: 0 0 9 13;
child: transform {
/* This is a typical fractional scale factor. It is 5/4.
The inverse is 4/5, and that number cannot be represented as
a float.
The renderer does however operate in the scaled coordinate system,
So this will translate the clip rect above to the scaled clip rect
0 0 7.20000029 10.4000006
*/
transform: scale(1.25);
child: container {
/* This color node exists just so that the bounds of the clipped node
are large enough. Otherwise the node bounds computation might end
up detecting that the clip is actually empty, because it scales
the other way: It multiplies by 1.25, not by 0.8.
*/
color {
color: black;
bounds: 0 0 50 50;
}
/* This node has bounds of 7.2 0 10 10 - which gets translated to
7.19999981 0 10.000001 10 during parsing of the path.
And because 7.19999981 < 7.20000029 this node might not be considered
fully clipped, even though it really should be.
*/
fill {
path: "M 7.2 0 l 10 0 l 0 10 l -10 0 z";
child: color {
color: red;
bounds: 0 0 50 50;
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 B

View File

@@ -73,6 +73,7 @@ compare_render_tests = [
'empty-shadow',
'empty-texture',
'empty-transform',
'empty-fallback',
'fill',
'fill2',
'fill-clipped-nogl',