gl: Rectangular fast path

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2011-07-29 15:06:20 +01:00
parent 7fbe430e2a
commit c2150b3441
5 changed files with 443 additions and 68 deletions

View file

@ -183,17 +183,8 @@
#endif
#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
#define _CAIRO_BOOLEAN_EXPR(expr) \
__extension__ ({ \
int _cairo_boolean_var_; \
if (expr) \
_cairo_boolean_var_ = 1; \
else \
_cairo_boolean_var_ = 0; \
_cairo_boolean_var_; \
})
#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1))
#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0))
#define likely(expr) (__builtin_expect (!!(expr), 1))
#define unlikely(expr) (__builtin_expect (!!(expr), 0))
#else
#define likely(expr) (expr)
#define unlikely(expr) (expr)

View file

@ -42,6 +42,8 @@
#include "cairoint.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-gl-private.h"
@ -1226,3 +1228,332 @@ _cairo_gl_composite_init (cairo_gl_composite_t *setup,
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
cairo_boxes_for_each_box (cairo_boxes_t *boxes,
cairo_bool_t (*func) (cairo_box_t *box,
void *data),
void *data)
{
struct _cairo_boxes_chunk *chunk;
int i;
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++)
if (! func (&chunk->base[i], data))
return FALSE;
}
return TRUE;
}
struct image_contains_box {
int width, height;
int tx, ty;
};
static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure)
{
struct image_contains_box *data = closure;
return
_cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 &&
_cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 &&
_cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width &&
_cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height;
}
struct image_upload_box {
cairo_gl_surface_t *surface;
cairo_image_surface_t *image;
int tx, ty;
};
static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure)
{
const struct image_upload_box *iub = closure;
int x = _cairo_fixed_integer_part (box->p1.x);
int y = _cairo_fixed_integer_part (box->p1.y);
int w = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
int h = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
return _cairo_gl_surface_draw_image (iub->surface,
iub->image,
x + iub->tx, y + iub->ty,
w, h,
x, y) == CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_upload_image_inplace (cairo_gl_surface_t *surface,
const cairo_pattern_t *source,
cairo_boxes_t *boxes)
{
const cairo_surface_pattern_t *pattern;
struct image_contains_box icb;
struct image_upload_box iub;
cairo_image_surface_t *image;
int tx, ty;
if (! boxes->is_pixel_aligned)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
return CAIRO_INT_STATUS_UNSUPPORTED;
pattern = (const cairo_surface_pattern_t *) source;
if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
return CAIRO_INT_STATUS_UNSUPPORTED;
image = (cairo_image_surface_t *) pattern->surface;
if (image->format == CAIRO_FORMAT_INVALID)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
/* Check that the data is entirely within the image */
icb.width = image->width;
icb.height = image->height;
icb.tx = tx;
icb.ty = ty;
if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb))
return CAIRO_INT_STATUS_UNSUPPORTED;
iub.surface = surface;
iub.image = image;
iub.tx = tx;
iub.ty = ty;
cairo_boxes_for_each_box (boxes, image_upload_box, &iub);
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t composite_box (cairo_box_t *box, void *closure)
{
_cairo_gl_composite_emit_rect (closure,
_cairo_fixed_integer_part (box->p1.x),
_cairo_fixed_integer_part (box->p1.y),
_cairo_fixed_integer_part (box->p2.x),
_cairo_fixed_integer_part (box->p2.y),
0);
return TRUE;
}
static cairo_status_t
_composite_boxes (cairo_gl_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
const cairo_composite_rectangles_t *extents)
{
cairo_bool_t need_clip_mask = FALSE;
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_surface_pattern_t mask;
cairo_status_t status;
/* If the boxes are not pixel-aligned, we will need to compute a real mask */
if (! boxes->is_pixel_aligned)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (extents->clip->path &&
(! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
if (! extents->is_bounded)
return CAIRO_INT_STATUS_UNSUPPORTED;
status = _cairo_gl_composite_init (&setup, op, dst, FALSE,
&extents->bounded);
if (unlikely (status))
goto CLEANUP;
status = _cairo_gl_composite_set_source (&setup, src,
extents->bounded.x,
extents->bounded.y,
extents->bounded.x,
extents->bounded.y,
extents->bounded.width,
extents->bounded.height);
if (unlikely (status))
goto CLEANUP;
need_clip_mask = extents->clip->path != NULL;
if (need_clip_mask) {
cairo_surface_t *clip_surface;
int clip_x, clip_y;
clip_surface = _cairo_clip_get_surface (extents->clip,
&dst->base,
&clip_x, &clip_y);
if (unlikely (clip_surface->status)) {
status = clip_surface->status;
need_clip_mask = FALSE;
goto CLEANUP;
}
_cairo_pattern_init_for_surface (&mask, clip_surface);
mask.base.filter = CAIRO_FILTER_NEAREST;
cairo_matrix_init_translate (&mask.base.matrix,
-clip_x,
-clip_y);
cairo_surface_destroy (clip_surface);
if (op == CAIRO_OPERATOR_CLEAR) {
src = NULL;
op = CAIRO_OPERATOR_DEST_OUT;
}
status = _cairo_gl_composite_set_mask (&setup, &mask.base,
extents->bounded.x,
extents->bounded.y,
extents->bounded.x,
extents->bounded.y,
extents->bounded.width,
extents->bounded.height);
if (unlikely (status))
goto CLEANUP;
}
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto CLEANUP;
cairo_boxes_for_each_box (boxes, composite_box, ctx);
status = _cairo_gl_context_release (ctx, status);
CLEANUP:
if (need_clip_mask)
_cairo_pattern_fini (&mask.base);
_cairo_gl_composite_fini (&setup);
return status;
}
/* XXX _cairo_gl_clip_and_composite_polygon() */
cairo_int_status_t
_cairo_gl_surface_polygon (cairo_gl_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *extents)
{
cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
if (! _cairo_clip_is_region (extents->clip))
return UNSUPPORTED ("a clip surface would be required");
if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias))
return UNSUPPORTED ("no span renderer");
if (op == CAIRO_OPERATOR_SOURCE)
return UNSUPPORTED ("SOURCE compositing doesn't work in GL");
if (op == CAIRO_OPERATOR_CLEAR) {
op = CAIRO_OPERATOR_DEST_OUT;
src = &_cairo_pattern_white.base;
}
return _cairo_surface_composite_polygon (&dst->base,
op,
src,
fill_rule,
antialias,
extents,
polygon,
clip_region);
}
cairo_int_status_t
_cairo_gl_clip_and_composite_boxes (cairo_gl_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
cairo_composite_rectangles_t *extents)
{
cairo_int_status_t status;
if (boxes->num_boxes == 0 && extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) &&
(op == CAIRO_OPERATOR_SOURCE ||
(dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))))
{
if (boxes->num_boxes == 1 &&
extents->bounded.width == dst->width &&
extents->bounded.height == dst->height)
{
op = CAIRO_OPERATOR_SOURCE;
#if 0
dst->deferred_clear = FALSE;
#endif
}
status = _upload_image_inplace (dst, src, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
/* Can we reduce drawing through a clip-mask to simply drawing the clip? */
if (extents->clip->path != NULL && extents->is_bounded) {
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
cairo_clip_t *clip;
clip = _cairo_clip_copy (extents->clip);
clip = _cairo_clip_intersect_boxes (clip, boxes);
status = _cairo_clip_get_polygon (clip, &polygon,
&fill_rule, &antialias);
_cairo_clip_path_destroy (clip->path);
clip->path = NULL;
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *saved_clip = extents->clip;
extents->clip = clip;
status = _cairo_gl_surface_polygon (dst, op, src,
&polygon,
fill_rule,
antialias,
extents);
if (extents->clip != clip)
clip = NULL;
extents->clip = saved_clip;
_cairo_polygon_fini (&polygon);
}
if (clip)
_cairo_clip_destroy (clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
#if 0
if (dst->deferred_clear) {
status = _cairo_gl_surface_clear (dst);
if (unlikely (status))
return status;
}
#endif
if (boxes->is_pixel_aligned &&
_cairo_clip_is_region (extents->clip) &&
op == CAIRO_OPERATOR_SOURCE) {
status = _upload_image_inplace (dst, src, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
/* Use a fast path if the boxes are pixel aligned */
status = _composite_boxes (dst, op, src, boxes, extents);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
/* Otherwise XXX */
return CAIRO_INT_STATUS_UNSUPPORTED;
}

View file

@ -594,6 +594,26 @@ _cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand);
cairo_private cairo_extend_t
_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand);
cairo_private cairo_bool_t
_cairo_gl_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle);
cairo_private cairo_int_status_t
_cairo_gl_surface_polygon (cairo_gl_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *extents);
cairo_private cairo_int_status_t
_cairo_gl_clip_and_composite_boxes (cairo_gl_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
cairo_composite_rectangles_t *extents);
slim_hidden_proto (cairo_gl_surface_create);
slim_hidden_proto (cairo_gl_surface_create_for_texture);

View file

@ -1581,7 +1581,7 @@ FAIL:
return _cairo_span_renderer_create_in_error (status);
}
cairo_private cairo_bool_t
cairo_bool_t
_cairo_gl_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
@ -1632,6 +1632,12 @@ _cairo_gl_surface_paint (void *abstract_surface,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t unbounded;
cairo_boxes_t boxes;
cairo_status_t status;
/* simplify the common case of clearing the surface */
if (clip == NULL) {
if (op == CAIRO_OPERATOR_CLEAR)
@ -1644,41 +1650,22 @@ _cairo_gl_surface_paint (void *abstract_surface,
}
}
return CAIRO_INT_STATUS_UNSUPPORTED;
}
_cairo_gl_surface_get_extents (surface, &unbounded);
status = _cairo_composite_rectangles_init_for_paint (&extents, &unbounded,
op, source,
clip);
if (unlikely (status))
return status;
static cairo_int_status_t
_cairo_gl_surface_polygon (cairo_gl_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *extents)
{
cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
if (! _cairo_clip_is_region (extents->clip))
return UNSUPPORTED ("a clip surface would be required");
if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias))
return UNSUPPORTED ("no span renderer");
if (op == CAIRO_OPERATOR_SOURCE)
return UNSUPPORTED ("SOURCE compositing doesn't work in GL");
if (op == CAIRO_OPERATOR_CLEAR) {
op = CAIRO_OPERATOR_DEST_OUT;
src = &_cairo_pattern_white.base;
status = _cairo_clip_to_boxes(extents.clip, &boxes);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _cairo_gl_clip_and_composite_boxes (surface, op, source,
&boxes, &extents);
}
return _cairo_surface_composite_polygon (&dst->base,
op,
src,
fill_rule,
antialias,
extents,
polygon,
clip_region);
_cairo_composite_rectangles_fini (&extents);
return status;
}
static cairo_int_status_t
@ -1696,8 +1683,7 @@ _cairo_gl_surface_stroke (void *abstract_surface,
cairo_gl_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t unbounded;
cairo_polygon_t polygon;
cairo_status_t status;
cairo_int_status_t status;
_cairo_gl_surface_get_extents (surface, &unbounded);
status = _cairo_composite_rectangles_init_for_stroke (&extents,
@ -1708,18 +1694,40 @@ _cairo_gl_surface_stroke (void *abstract_surface,
if (unlikely (status))
return status;
_cairo_polygon_init_with_clip (&polygon, extents.clip);
status = _cairo_path_fixed_stroke_to_polygon (path,
style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
CAIRO_FILL_RULE_WINDING, antialias,
&extents);
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
_cairo_boxes_init_with_clip (&boxes, extents.clip);
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_gl_clip_and_composite_boxes (surface, op, source,
&boxes, &extents);
}
_cairo_boxes_fini (&boxes);
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
_cairo_polygon_init_with_clip (&polygon, extents.clip);
status = _cairo_path_fixed_stroke_to_polygon (path,
style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
CAIRO_FILL_RULE_WINDING, antialias,
&extents);
}
_cairo_polygon_fini (&polygon);
}
_cairo_polygon_fini (&polygon);
_cairo_composite_rectangles_fini (&extents);
@ -1739,8 +1747,7 @@ _cairo_gl_surface_fill (void *abstract_surface,
cairo_gl_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t unbounded;
cairo_polygon_t polygon;
cairo_status_t status;
cairo_int_status_t status;
_cairo_gl_surface_get_extents (surface, &unbounded);
status = _cairo_composite_rectangles_init_for_fill (&extents,
@ -1750,14 +1757,34 @@ _cairo_gl_surface_fill (void *abstract_surface,
if (unlikely (status))
return status;
_cairo_polygon_init_with_clip (&polygon, extents.clip);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
fill_rule, antialias,
&extents);
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
_cairo_boxes_init_with_clip (&boxes, extents.clip);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_gl_clip_and_composite_boxes (surface, op, source,
&boxes, &extents);
}
_cairo_boxes_fini (&boxes);
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
_cairo_polygon_init_with_clip (&polygon, extents.clip);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
fill_rule, antialias,
&extents);
}
_cairo_polygon_fini (&polygon);
}
_cairo_polygon_fini (&polygon);
_cairo_composite_rectangles_fini (&extents);

View file

@ -585,8 +585,14 @@ _command_init (cairo_recording_surface_t *surface,
command->index = surface->commands.num_elements;
/* steal the clip */
command->clip = composite->clip;
composite->clip = NULL;
command->clip = NULL;
if (! _cairo_clip_is_region (composite->clip) ||
cairo_region_contains_rectangle (_cairo_clip_get_region (composite->clip),
&composite->unbounded) != CAIRO_REGION_OVERLAP_IN)
{
command->clip = composite->clip;
composite->clip = NULL;
}
return status;
}