Every time we assign or return a hard-coded error status wrap that value
with a call to _cairo_error(). So the idiom becomes:
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
or
return _cairo_error (CAIRO_STATUS_INVALID_DASH);
This ensures that a breakpoint placed on _cairo_error() will trigger
immediately cairo detects the error.
Since the objects can be shared and may be in use simultaneously across
multiple threads, setting the status needs to be atomic. We use a
locked compare and exchange in order to avoid overwriting an existing
error - that is we use an atomic operation that only sets the new status
value if the current value is CAIRO_STATUS_SUCCESS.
Introduce an opaque cairo_reference_count_t and define operations on it
in terms of atomic ops. Update all users of reference counters to use
the new opaque type.
Introduce cairo_gradient_stop_t, and remove pixman dependency
for core pattern types. Perform conversion from cairo types
to pixman types as necessary in fallback code.
This patch introduces three macros: _cairo_malloc_ab,
_cairo_malloc_abc, _cairo_malloc_ab_plus_c and replaces various calls
to malloc(a*b), malloc(a*b*c), and malloc(a*b+c) with them. The macros
return NULL if int overflow would occur during the allocation. See
CODING_STYLE for more information.
_cairo_pattern_fini depends on the pattern being correctly initialised,
for example when calling _cairo_user_data_array_fini(), so we need to
initialize the whole pattern and not just set the type to SOLID when
creating a pattern for a surface in error.
cairo_surface_create_similar() does not return NULL as was being checked
for, but the nil surface on error. Also ensure that the returned surface
is destroyed if we encounter an error whilst compositing.
During the copy, allocation of the gradient may fail and so the callers
need to check for a pattern that returned in an error state. No callers
did so and in order to force all callers to check the error status,
the status return was added to _cairo_pattern_init_copy(). The early
error checking may appear redundant for an object with an embedded
structure, however it does fix an error where an uninitialised pattern
was being used:
==1922== Process terminating with default action of signal 11 (SIGSEGV)
==1922== Access not within mapped region at address 0x55555555
==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208)
==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370)
==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188)
==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266)
==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331)
==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195)
==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331)
==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822)
==1922== by 0x402B2D1: cairo_paint (cairo.c:1879)
==1922== by 0x804A4F7: draw (radial-gradient.c:73)
==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326)
==1922== by 0x804A57C: main (radial-gradient.c:109)
==1922== Injected fault at:
==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207)
==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136)
==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153)
==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328)
==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195)
==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331)
==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822)
==1922== by 0x402B2D1: cairo_paint (cairo.c:1879)
==1922== by 0x804A4F7: draw (radial-gradient.c:73)
==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326)
==1922== by 0x804A57C: main (radial-gradient.c:109)
Simply return without writing to potentially read-only members of an
invalid pattern rather than assert. This is cleaner than tracking down
all the error paths that may call into cairo_pattern_transform()...
_cairo_surface_create_similar_solid() may return an image surface,
should the backend not support the required content or should it
encounter an error whilst creating the surface. In those circumstances
we choose not to cache the fallback surface.
Original work by Jorn Baayen <jorn@openedhand.com>,
2715f20981
We use a small cache of size 16 for surfaces created for solid patterns.
This mainly helps with the X backends where we don't have to create a
pattern for every operation, so we save a lot on X traffic. Xft uses a
similar cache, so cairo's text rendering traffic with the xlib backend
now completely matches that of Xft.
The cache uses an static index variable, which itself acts like a cache of
size 1, remembering the most recently used solid pattern. So repeated
lookups for the same pattern hit immediately. If that fails, the cache is
searched linearly, and if that fails too, a new surface is created and a
random member of the cache is evicted.
A cached surface can only be reused if it is similar to the destination.
In order to check for similar surfaces a new test is introduced for the
backends to determine that the cached surface is as would be returned by
a _create_similar() call for the destination and content.
As surfaces are in general complex encapsulation of graphics state we
only return unshared cached surfaces and reset them (to clear any error
conditions and graphics state). In practice this makes little difference
to the efficacy of the cache during various benchmarks. However, in order
to transparently share solid surfaces it would be possible to implement a
COW scheme.
Cache hit rates: (hit same index + hit in cache) / lookups
cairo-perf: (42346 + 28480) / 159600 = 44.38%
gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95%
gtk-perf: (8270 + 3190) / 21504 = 53.29%
This translates into a reduction of about 25% of the XRENDER traffic during
cairo-perf.
This allows for the surface acquired from the pattern to have the
same content. In particular, in a case such as cairo_paint_with_alpha
we can now acquire an A8 mask surface instead of an ARGB32 mask
surface which can be rendered much more efficiently. This results
in a 4x speedup when using the OVER operator with the recently
added paint-with-alpha test:
Speedups
========
image-rgb paint-with-alpha_image_rgb_over-256 2.25 -> 0.60: 4.45x speedup
███▌
It does slowdown the same test when using the SOURCE operator, but
I don't think we care. Performing SOURCE with a mask is already a very
slow operation, (hitting compositeGeneral), so the slowdown here is
likely from having to convert from A8 back to ARGB32 before the
generalized compositing. So if someone cares about this slowdown,
(though SOURCE with cairo_paint_with_alpha doesn't seem extremely
useful), they will probably be motivated enough to contribute a
customized compositing function to replace compositeGeneral in which
case this slowdown should go away:
image-rgba paint-with-alpha_image_rgb_source-256 3.84 -> 8.86%: 1.94x slowdown
█
_cairo_surface_create_similar_solid() creates a fresh pattern to wrap
color, however sometimes the caller already has that pattern available.
In those circumstances we can pass the pattern as well as the color and
avoid the extra allocation.
For opaque surfaces the backends may use simpler code paths - for
example, the xlib backend may be able to use the Core protocol rather
than Render. So we only generate a surface with an alpha component if
the color is not opaque.
The so-attributed-to-X-server bug was that cairo maps the drawing
region to the pattern space, rounds the box, and uploads only that
part of the source surface to the X server. Well, this only works for
NEAREST filter as any more sophisticated filter needs to sneak a peek
at the neighboring pixels around the edges too.
The right fix involves taking into account the filter used, and the
pattern matrix, but for most cases, a single pixel should be enough.
Not sure about scaling down...
Anyway, this is just a workaround to get 1.4.4 out of the door. I'll
commit a proper fix soon.
Confusion had been introduced as to who provided the fixup after
the malloc failed which resulted in a NULL deference whilst checking for
an erroneous pattern in _cairo_pattern_create_in_error.
Previously, the convention was that static ones started with cairo_, but
renamed to start with _cairo_ when they were needed from other files and
became cairo_private instead of static...
This is error prune indeed, and two symbols were already violating. Now
all nil objects start with _cairo_.
Frequently cairo_set_source_rgb[a]() is used to replace the current
solid-pattern source with a new one of a different colour. The current
pattern is very likely to be unshared and unmodified and so it is likely
just to be immediately freed [or rather simply moved to recently freed
cache]. However as the last active pattern it is likely to cache-warm and
suitable to satisfy the forthcoming allocation. So by setting the current
pattern to 'none' we can move the pattern to the freed list before we
create the new pattern and hopefully immediately reuse it.
Unfortunately one cannot cache live patterns and return a fresh reference
instead of creating new ones as patterns can be modified by the user and
so cannot be transparently shared between different users. However,
solid colour allocation is still a frequent operation, so we maintain a
small cache of recently freed patterns to reduce the malloc pressure.
We use a small cache of size 16 for patterns created from solid colors,
e.g. cairo_set_source_rgb(). This helps with toolkits that draw many
widgets using the same colour scheme.
The cache uses a static index variable, which itself acts like a cache
of size 1, remembering the most recently used colour. So repeated
lookups for the same colour hit immediately. If that fails, the cache
is searched linearly, and if that fails too, a new pattern is created
and a random member of the cache is evicted.
All mutex declarations have been moved to cairo-mutex-list.h.
This should avoid breaking of less frequently tested backends,
when mutexes are introduced or when existing mutexes are renamed.
Instead of initializing mutexes on library startup, mutexes are
lazily initialized within the few entry points of now by calling
CAIRO_MUTEX_INITIALIZE(). Currently only the OS/2 backend takes
care about releasing global mutexes. Therefore there is no counter
part of that macro for finalizing all global mutexes yet - but
as cairo-backend-os2.c shows such a function would be quite
easy to implement.