recording: Perform an explicit during snapshot

In order to avoid recursing upon our source mutex when doing a snapshot,
we can perform an explicit copy of the command array. This should also
be faster than performing a replay as well.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=50443
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2012-10-03 17:01:41 +01:00
parent 797441093a
commit 8020e0bc8c

View file

@ -1088,6 +1088,316 @@ CLEANUP_COMPOSITE:
return status;
}
static void
_command_init_copy (cairo_recording_surface_t *surface,
cairo_command_header_t *dst,
const cairo_command_header_t *src)
{
dst->type = src->type;
dst->op = src->op;
dst->region = CAIRO_RECORDING_REGION_ALL;
dst->extents = src->extents;
dst->chain = NULL;
dst->index = surface->commands.num_elements;
dst->clip = _cairo_clip_copy (src->clip);
}
static cairo_status_t
_cairo_recording_surface_copy__paint (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_paint_t *command;
cairo_status_t status;
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
_command_init_copy (surface, &command->header, &src->header);
status = _cairo_pattern_init_copy (&command->source.base,
&src->paint.source.base);
if (unlikely (status))
goto err_command;
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_source;
return CAIRO_STATUS_SUCCESS;
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
static cairo_status_t
_cairo_recording_surface_copy__mask (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_mask_t *command;
cairo_status_t status;
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
_command_init_copy (surface, &command->header, &src->header);
status = _cairo_pattern_init_copy (&command->source.base,
&src->mask.source.base);
if (unlikely (status))
goto err_command;
status = _cairo_pattern_init_copy (&command->mask.base,
&src->mask.source.base);
if (unlikely (status))
goto err_source;
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_mask;
return CAIRO_STATUS_SUCCESS;
err_mask:
_cairo_pattern_fini (&command->mask.base);
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
static cairo_status_t
_cairo_recording_surface_copy__stroke (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_stroke_t *command;
cairo_status_t status;
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
_command_init_copy (surface, &command->header, &src->header);
status = _cairo_pattern_init_copy (&command->source.base,
&src->stroke.source.base);
if (unlikely (status))
goto err_command;
status = _cairo_path_fixed_init_copy (&command->path, &src->stroke.path);
if (unlikely (status))
goto err_source;
status = _cairo_stroke_style_init_copy (&command->style,
&src->stroke.style);
if (unlikely (status))
goto err_path;
command->ctm = src->stroke.ctm;
command->ctm_inverse = src->stroke.ctm_inverse;
command->tolerance = src->stroke.tolerance;
command->antialias = src->stroke.antialias;
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_style;
return CAIRO_STATUS_SUCCESS;
err_style:
_cairo_stroke_style_fini (&command->style);
err_path:
_cairo_path_fixed_fini (&command->path);
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
static cairo_status_t
_cairo_recording_surface_copy__fill (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_fill_t *command;
cairo_status_t status;
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
_command_init_copy (surface, &command->header, &src->header);
status = _cairo_pattern_init_copy (&command->source.base,
&src->fill.source.base);
if (unlikely (status))
goto err_command;
status = _cairo_path_fixed_init_copy (&command->path, &src->fill.path);
if (unlikely (status))
goto err_source;
command->fill_rule = src->fill.fill_rule;
command->tolerance = src->fill.tolerance;
command->antialias = src->fill.antialias;
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_path;
return CAIRO_STATUS_SUCCESS;
err_path:
_cairo_path_fixed_fini (&command->path);
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
static cairo_status_t
_cairo_recording_surface_copy__glyphs (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_show_text_glyphs_t *command;
cairo_status_t status;
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
_command_init_copy (surface, &command->header, &src->header);
status = _cairo_pattern_init_copy (&command->source.base,
&src->show_text_glyphs.source.base);
if (unlikely (status))
goto err_command;
command->utf8 = NULL;
command->utf8_len = src->show_text_glyphs.utf8_len;
command->glyphs = NULL;
command->num_glyphs = src->show_text_glyphs.num_glyphs;
command->clusters = NULL;
command->num_clusters = src->show_text_glyphs.num_clusters;
if (command->utf8_len) {
command->utf8 = malloc (command->utf8_len);
if (unlikely (command->utf8 == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err_arrays;
}
memcpy (command->utf8, src->show_text_glyphs.utf8, command->utf8_len);
}
if (command->num_glyphs) {
command->glyphs = _cairo_malloc_ab (command->num_glyphs,
sizeof (command->glyphs[0]));
if (unlikely (command->glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err_arrays;
}
memcpy (command->glyphs, src->show_text_glyphs.glyphs,
sizeof (command->glyphs[0]) * command->num_glyphs);
}
if (command->num_clusters) {
command->clusters = _cairo_malloc_ab (command->num_clusters,
sizeof (command->clusters[0]));
if (unlikely (command->clusters == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err_arrays;
}
memcpy (command->clusters, src->show_text_glyphs.clusters,
sizeof (command->clusters[0]) * command->num_clusters);
}
command->cluster_flags = src->show_text_glyphs.cluster_flags;
command->scaled_font =
cairo_scaled_font_reference (src->show_text_glyphs.scaled_font);
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_arrays;
return CAIRO_STATUS_SUCCESS;
err_arrays:
free (command->utf8);
free (command->glyphs);
free (command->clusters);
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
static cairo_status_t
_cairo_recording_surface_copy (cairo_recording_surface_t *dst,
cairo_recording_surface_t *src)
{
cairo_command_t **elements;
int i, num_elements;
cairo_status_t status;
elements = _cairo_array_index (&src->commands, 0);
num_elements = src->commands.num_elements;
for (i = 0; i < num_elements; i++) {
const cairo_command_t *command = elements[i];
switch (command->header.type) {
case CAIRO_COMMAND_PAINT:
status = _cairo_recording_surface_copy__paint (dst, command);
break;
case CAIRO_COMMAND_MASK:
status = _cairo_recording_surface_copy__mask (dst, command);
break;
case CAIRO_COMMAND_STROKE:
status = _cairo_recording_surface_copy__stroke (dst, command);
break;
case CAIRO_COMMAND_FILL:
status = _cairo_recording_surface_copy__fill (dst, command);
break;
case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
status = _cairo_recording_surface_copy__glyphs (dst, command);
break;
default:
ASSERT_NOT_REACHED;
}
if (unlikely (status))
return status;
}
return CAIRO_STATUS_SUCCESS;
}
/**
* _cairo_recording_surface_snapshot:
* @surface: a #cairo_surface_t which must be a recording surface
@ -1131,7 +1441,7 @@ _cairo_recording_surface_snapshot (void *abstract_other)
surface->optimize_clears = TRUE;
_cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
status = _cairo_recording_surface_replay (&other->base, &surface->base);
status = _cairo_recording_surface_copy (other, surface);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);