mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-19 06:14:03 +00:00
Bug 790810 - Nested layer groups lead to a deadlock with multithreading
In gimp_operation_buffer_source_validate_process(), align the ROI to the tile grid *before* intersecting it with the validate- handler's dirty region. This is necessary since, even though subsequent operations will only read data within the ROI, the entire tiles containing the ROI will be fetched, resulting in an area potentially greater than the ROI. We need to validate this area in advance, or else it will be validated as part of the subsequent operations, which can lead into the same deadlock we're trying to prevent.
This commit is contained in:
parent
e4d2de7dde
commit
caa3a98f04
|
@ -64,10 +64,6 @@ static void gimp_operation_buffer_source_validate_buffer_changed (Ge
|
|||
const GeglRectangle *rect,
|
||||
gpointer data);
|
||||
|
||||
static void gimp_operation_buffer_source_validate_buffer_validate (GimpOperationBufferSourceValidate *buffer_source_validate,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
gint level);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GimpOperationBufferSourceValidate, gimp_operation_buffer_source_validate,
|
||||
GEGL_TYPE_OPERATION_SOURCE)
|
||||
|
@ -255,43 +251,74 @@ gimp_operation_buffer_source_validate_process (GeglOperation *operation,
|
|||
*/
|
||||
if (n_threads > 1)
|
||||
{
|
||||
gint shift_x;
|
||||
gint shift_y;
|
||||
gint tile_width;
|
||||
gint tile_height;
|
||||
cairo_rectangle_int_t rect;
|
||||
cairo_region_overlap_t overlap;
|
||||
|
||||
rect.x = result->x;
|
||||
rect.y = result->y;
|
||||
rect.width = result->width;
|
||||
rect.height = result->height;
|
||||
g_object_get (buffer_source_validate->buffer,
|
||||
"shift-x", &shift_x,
|
||||
"shift-y", &shift_y,
|
||||
"tile-width", &tile_width,
|
||||
"tile-height", &tile_height,
|
||||
NULL);
|
||||
|
||||
/* align the rectangle to the tile grid */
|
||||
rect.x = (gint) floor ((gdouble) (result->x + shift_x) / tile_width) * tile_width;
|
||||
rect.y = (gint) floor ((gdouble) (result->y + shift_y) / tile_height) * tile_height;
|
||||
rect.width = (gint) ceil ((gdouble) (result->x + result->width + shift_x) / tile_width) * tile_width - rect.x;
|
||||
rect.height = (gint) ceil ((gdouble) (result->y + result->height + shift_y) / tile_height) * tile_height - rect.y;
|
||||
|
||||
/* check if the rectangle interescts with the dirty region */
|
||||
overlap = cairo_region_contains_rectangle (validate_handler->dirty_region,
|
||||
&rect);
|
||||
|
||||
if (overlap == CAIRO_REGION_OVERLAP_IN)
|
||||
if (overlap != CAIRO_REGION_OVERLAP_OUT)
|
||||
{
|
||||
gimp_operation_buffer_source_validate_buffer_validate (
|
||||
buffer_source_validate, &rect, level);
|
||||
}
|
||||
else if (overlap == CAIRO_REGION_OVERLAP_PART)
|
||||
{
|
||||
cairo_region_t *region;
|
||||
gint n_rectangles;
|
||||
gint i;
|
||||
GeglBufferIterator *iter;
|
||||
|
||||
region = cairo_region_copy (validate_handler->dirty_region);
|
||||
|
||||
cairo_region_intersect_rectangle (region, &rect);
|
||||
|
||||
n_rectangles = cairo_region_num_rectangles (region);
|
||||
|
||||
for (i = 0; i < n_rectangles; i++)
|
||||
/* if the rectangle is not entirely within the dirty
|
||||
* region ...
|
||||
*/
|
||||
if (overlap == CAIRO_REGION_OVERLAP_PART)
|
||||
{
|
||||
cairo_region_get_rectangle (region, i, &rect);
|
||||
cairo_region_t *region;
|
||||
|
||||
gimp_operation_buffer_source_validate_buffer_validate (
|
||||
buffer_source_validate, &rect, level);
|
||||
/* ... intersect it with region and use the result's
|
||||
* bounds
|
||||
*/
|
||||
region = cairo_region_copy (validate_handler->dirty_region);
|
||||
|
||||
cairo_region_intersect_rectangle (region, &rect);
|
||||
cairo_region_get_extents (region, &rect);
|
||||
|
||||
cairo_region_destroy (region);
|
||||
|
||||
/* realign the rectangle to the tile grid */
|
||||
rect.x = (gint) floor ((gdouble) (result->x + shift_x) / tile_width) * tile_width;
|
||||
rect.y = (gint) floor ((gdouble) (result->y + shift_y) / tile_height) * tile_height;
|
||||
rect.width = (gint) ceil ((gdouble) (result->x + result->width + shift_x) / tile_width) * tile_width - rect.x;
|
||||
rect.height = (gint) ceil ((gdouble) (result->y + result->height + shift_y) / tile_height) * tile_height - rect.y;
|
||||
}
|
||||
|
||||
cairo_region_destroy (region);
|
||||
rect.x -= shift_x;
|
||||
rect.y -= shift_y;
|
||||
|
||||
/* iterate over the rectangle -- this implicitly causes
|
||||
* validation
|
||||
*/
|
||||
iter = gegl_buffer_iterator_new (buffer,
|
||||
GEGL_RECTANGLE (rect.x,
|
||||
rect.y,
|
||||
rect.width,
|
||||
rect.height),
|
||||
level, NULL,
|
||||
GEGL_BUFFER_READ,
|
||||
GEGL_ABYSS_NONE);
|
||||
|
||||
while (gegl_buffer_iterator_next (iter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,46 +341,3 @@ gimp_operation_buffer_source_validate_buffer_changed (GeglBuffer *buffe
|
|||
gegl_operation_invalidate (GEGL_OPERATION (buffer_source_validate),
|
||||
rect, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_operation_buffer_source_validate_buffer_validate (GimpOperationBufferSourceValidate *buffer_source_validate,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
gint level)
|
||||
{
|
||||
gint shift_x;
|
||||
gint shift_y;
|
||||
gint tile_width;
|
||||
gint tile_height;
|
||||
GeglRectangle roi;
|
||||
GeglBufferIterator *iter;
|
||||
|
||||
g_object_get (buffer_source_validate->buffer,
|
||||
"shift-x", &shift_x,
|
||||
"shift-y", &shift_y,
|
||||
"tile-width", &tile_width,
|
||||
"tile-height", &tile_height,
|
||||
NULL);
|
||||
|
||||
/* align rectangle to tile grid */
|
||||
|
||||
roi.x = (gint) floor ((gdouble) (rect->x + shift_x) / tile_width) * tile_width;
|
||||
roi.y = (gint) floor ((gdouble) (rect->y + shift_y) / tile_height) * tile_height;
|
||||
roi.width = (gint) ceil ((gdouble) (rect->x + rect->width + shift_x) / tile_width) * tile_width - roi.x;
|
||||
roi.height = (gint) ceil ((gdouble) (rect->y + rect->height + shift_y) / tile_height) * tile_height - roi.y;
|
||||
|
||||
roi.x -= shift_x;
|
||||
roi.y -= shift_y;
|
||||
|
||||
/* intersect rectangle with abyss */
|
||||
|
||||
gegl_rectangle_intersect (&roi, &roi,
|
||||
gegl_buffer_get_abyss (buffer_source_validate->buffer));
|
||||
|
||||
/* iterate over rectangle -- this implicitly causes validation */
|
||||
|
||||
iter = gegl_buffer_iterator_new (buffer_source_validate->buffer,
|
||||
&roi, level, NULL,
|
||||
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
|
||||
|
||||
while (gegl_buffer_iterator_next (iter));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue