gl/msaa: Wait to clip until compositing begins

The MSAA compositors clips in a unique way We'd like to
share this method with the text rendering path, so we move
it to cairo-gl-composite so that it works in a way very
similar to clipping with the spans and traps compositors.
This commit is contained in:
Martin Robinson 2012-02-21 09:36:52 -08:00
parent 29b243325c
commit e3f5b14fba
4 changed files with 181 additions and 151 deletions

View file

@ -115,6 +115,13 @@ _cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
setup->clip_region = clip_region;
}
void
_cairo_gl_composite_set_clip (cairo_gl_composite_t *setup,
cairo_clip_t *clip)
{
setup->clip = clip;
}
static void
_cairo_gl_composite_bind_to_shader (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup)
@ -467,6 +474,132 @@ _cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx,
return CAIRO_STATUS_SUCCESS;
}
static void
_scissor_to_box (cairo_gl_surface_t *surface,
const cairo_box_t *box)
{
double x1, y1, x2, y2, height;
_cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
height = y2 - y1;
if (_cairo_gl_surface_is_texture (surface) == FALSE)
y1 = surface->height - (y1 + height);
glScissor (x1, y1, x2 - x1, height);
glEnable (GL_SCISSOR_TEST);
}
static void
_cairo_gl_composite_setup_vbo (cairo_gl_context_t *ctx,
unsigned int size_per_vertex)
{
if (ctx->vertex_size != size_per_vertex)
_cairo_gl_composite_flush (ctx);
if (_cairo_gl_context_is_flushed (ctx)) {
ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, ctx->vbo);
ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2,
GL_FLOAT, GL_FALSE, size_per_vertex, NULL);
ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX);
}
ctx->vertex_size = size_per_vertex;
}
static void
_disable_stencil_buffer (void)
{
glDisable (GL_STENCIL_TEST);
glDepthMask (GL_FALSE);
}
static cairo_int_status_t
_cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup,
cairo_gl_context_t *ctx,
int vertex_size)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
cairo_gl_surface_t *dst = setup->dst;
cairo_clip_t *clip = setup->clip;
if (clip->num_boxes == 1 && clip->path == NULL) {
_scissor_to_box (dst, &clip->boxes[0]);
goto disable_stencil_buffer_and_return;
}
/* If we cannot reduce the clip to a rectangular region,
we clip using the stencil buffer. */
glDisable (GL_SCISSOR_TEST);
if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto disable_stencil_buffer_and_return;
}
glDepthMask (GL_TRUE);
glEnable (GL_STENCIL_TEST);
glClearStencil (0);
glClear (GL_STENCIL_BUFFER_BIT);
glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilFunc (GL_EQUAL, 1, 0xffffffff);
glColorMask (0, 0, 0, 0);
status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip);
if (unlikely (status)) {
glColorMask (1, 1, 1, 1);
goto disable_stencil_buffer_and_return;
}
/* We want to only render to the stencil buffer, so draw everything now.
Flushing also unbinds the VBO, which we want to rebind for regular
drawing. */
_cairo_gl_composite_flush (ctx);
_cairo_gl_composite_setup_vbo (ctx, vertex_size);
glColorMask (1, 1, 1, 1);
glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc (GL_EQUAL, 1, 0xffffffff);
return CAIRO_INT_STATUS_SUCCESS;
disable_stencil_buffer_and_return:
_disable_stencil_buffer ();
return status;
}
static cairo_int_status_t
_cairo_gl_composite_setup_clipping (cairo_gl_composite_t *setup,
cairo_gl_context_t *ctx,
int vertex_size)
{
if (! _cairo_gl_context_is_flushed (ctx) &&
(! cairo_region_equal (ctx->clip_region, setup->clip_region) ||
! _cairo_clip_equal (ctx->clip, setup->clip)))
_cairo_gl_composite_flush (ctx);
cairo_region_destroy (ctx->clip_region);
ctx->clip_region = cairo_region_reference (setup->clip_region);
_cairo_clip_destroy (ctx->clip);
ctx->clip = _cairo_clip_copy (setup->clip);
assert (!setup->clip_region || !setup->clip);
if (ctx->clip_region) {
_disable_stencil_buffer ();
glEnable (GL_SCISSOR_TEST);
return CAIRO_INT_STATUS_SUCCESS;
}
if (ctx->clip)
return _cairo_gl_composite_setup_painted_clipping (setup, ctx,
vertex_size);
_disable_stencil_buffer ();
glDisable (GL_SCISSOR_TEST);
return CAIRO_INT_STATUS_SUCCESS;
}
cairo_status_t
_cairo_gl_composite_begin_multisample (cairo_gl_composite_t *setup,
cairo_gl_context_t **ctx_out,
@ -484,6 +617,8 @@ _cairo_gl_composite_begin_multisample (cairo_gl_composite_t *setup,
if (unlikely (status))
return status;
_cairo_gl_context_set_destination (ctx, setup->dst, multisampling);
glEnable (GL_BLEND);
component_alpha =
@ -522,23 +657,12 @@ _cairo_gl_composite_begin_multisample (cairo_gl_composite_t *setup,
dst_size = 2 * sizeof (GLfloat);
src_size = _cairo_gl_operand_get_vertex_size (setup->src.type);
mask_size = _cairo_gl_operand_get_vertex_size (setup->mask.type);
vertex_size = dst_size + src_size + mask_size;
if (setup->spans)
vertex_size += sizeof (GLfloat);
if (ctx->vertex_size != vertex_size)
_cairo_gl_composite_flush (ctx);
_cairo_gl_context_set_destination (ctx, setup->dst, multisampling);
if (_cairo_gl_context_is_flushed (ctx)) {
ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, ctx->vbo);
ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2,
GL_FLOAT, GL_FALSE, vertex_size, NULL);
ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX);
}
_cairo_gl_composite_setup_vbo (ctx, vertex_size);
_cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src, vertex_size, dst_size);
_cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask, vertex_size, dst_size + src_size);
@ -549,8 +673,6 @@ _cairo_gl_composite_begin_multisample (cairo_gl_composite_t *setup,
_cairo_gl_set_operator (ctx, setup->op, component_alpha);
ctx->vertex_size = vertex_size;
if (_cairo_gl_context_is_flushed (ctx)) {
if (ctx->pre_shader) {
_cairo_gl_set_shader (ctx, ctx->pre_shader);
@ -560,15 +682,9 @@ _cairo_gl_composite_begin_multisample (cairo_gl_composite_t *setup,
_cairo_gl_composite_bind_to_shader (ctx, setup);
}
if (! _cairo_gl_context_is_flushed (ctx) &&
! cairo_region_equal (ctx->clip_region, setup->clip_region))
_cairo_gl_composite_flush (ctx);
cairo_region_destroy (ctx->clip_region);
ctx->clip_region = cairo_region_reference (setup->clip_region);
if (ctx->clip_region)
glEnable (GL_SCISSOR_TEST);
else
glDisable (GL_SCISSOR_TEST);
status = _cairo_gl_composite_setup_clipping (setup, ctx, vertex_size);
if (unlikely (status))
goto FAIL;
*ctx_out = ctx;

View file

@ -137,6 +137,7 @@ _gl_destroy (void *device)
_cairo_array_fini (&ctx->tristrip_indices);
cairo_region_destroy (ctx->clip_region);
_cairo_clip_destroy (ctx->clip);
free (ctx->vb_mem);

View file

@ -157,10 +157,10 @@ _draw_triangle_fan (cairo_gl_context_t *ctx,
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_draw_clip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_clip_t *clip)
cairo_int_status_t
_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_clip_t *clip)
{
cairo_int_status_t status;
cairo_traps_t traps;
@ -198,93 +198,6 @@ _draw_clip (cairo_gl_context_t *ctx,
return status;
}
static void
_disable_stencil_buffer (void)
{
glDisable (GL_STENCIL_TEST);
glDepthMask (GL_FALSE);
}
static cairo_int_status_t
_draw_clip_to_stencil_buffer (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_clip_t *clip)
{
cairo_int_status_t status;
assert (! _cairo_clip_is_all_clipped (clip));
if (! _cairo_gl_ensure_stencil (ctx, setup->dst))
return CAIRO_INT_STATUS_UNSUPPORTED;
glDepthMask (GL_TRUE);
glEnable (GL_STENCIL_TEST);
glClearStencil (0);
glClear (GL_STENCIL_BUFFER_BIT);
glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilFunc (GL_EQUAL, 1, 0xffffffff);
glColorMask (0, 0, 0, 0);
status = _draw_clip (ctx, setup, clip);
if (unlikely (status)) {
glColorMask (1, 1, 1, 1);
_disable_stencil_buffer ();
return status;
}
/* We want to only render to the stencil buffer, so draw everything now. */
_cairo_gl_composite_flush (ctx);
glColorMask (1, 1, 1, 1);
glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc (GL_EQUAL, 1, 0xffffffff);
return status;;
}
static void
_scissor_to_rectangle (cairo_gl_surface_t *surface,
const cairo_rectangle_int_t *r)
{
int y = r->y;
if (_cairo_gl_surface_is_texture (surface) == FALSE)
y = surface->height - (r->y + r->height);
glScissor (r->x, y, r->width, r->height);
glEnable (GL_SCISSOR_TEST);
}
static cairo_int_status_t
_scissor_and_clip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_composite_rectangles_t *composite,
cairo_bool_t *used_stencil_buffer)
{
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
cairo_rectangle_int_t *bounds = &composite->unbounded;
cairo_clip_t *clip = composite->clip;
cairo_rectangle_int_t r;
*used_stencil_buffer = FALSE;
if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) {
_scissor_to_rectangle (dst, bounds);
return CAIRO_INT_STATUS_SUCCESS;
}
/* If we cannot reduce the clip to a rectangular region,
we scissor and clip using the stencil buffer */
if (clip->num_boxes > 1 || clip->path != NULL) {
*used_stencil_buffer = TRUE;
_scissor_to_rectangle (dst, bounds);
return _draw_clip_to_stencil_buffer (ctx, setup, clip);
}
/* Always interpret the clip path as ANTIALIAS_NONE */
_cairo_box_round_to_rectangle (clip->boxes, &r);
_scissor_to_rectangle (dst, &r);
return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_bool_t
_should_use_unbounded_surface (cairo_composite_rectangles_t *composite)
{
@ -365,6 +278,15 @@ should_fall_back (cairo_gl_surface_t *surface,
return ! surface->supports_msaa;
}
static void
_cairo_gl_msaa_compositor_set_clip (cairo_composite_rectangles_t *composite,
cairo_gl_composite_t *setup)
{
if (_cairo_composite_rectangles_can_reduce_clip (composite, composite->clip))
return;
_cairo_gl_composite_set_clip (setup, composite->clip);
}
static cairo_int_status_t
_cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite)
@ -372,7 +294,6 @@ _cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor,
cairo_gl_composite_t setup;
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
cairo_gl_context_t *ctx = NULL;
cairo_bool_t used_stencil_buffer;
cairo_int_status_t status;
cairo_operator_t op = composite->op;
@ -447,27 +368,22 @@ _cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor,
if (unlikely (status))
goto finish;
_cairo_gl_msaa_compositor_set_clip (composite, &setup);
/* We always use multisampling here, because we do not yet have the smarts
to calculate when the clip or the source requires it. */
status = _cairo_gl_composite_begin_multisample (&setup, &ctx, TRUE);
if (unlikely (status))
goto finish;
status = _scissor_and_clip (ctx, &setup, composite, &used_stencil_buffer);
if (unlikely (status))
goto finish;
_draw_int_rect (ctx, &setup, &composite->bounded);
_cairo_gl_composite_flush (ctx);
finish:
_cairo_gl_composite_fini (&setup);
if (ctx) {
glDisable (GL_SCISSOR_TEST);
_disable_stencil_buffer ();
if (ctx)
status = _cairo_gl_context_release (ctx, status);
}
return status;
}
@ -512,8 +428,7 @@ _stroke_shaper_add_quad (void *closure,
static cairo_int_status_t
_prevent_overlapping_drawing (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_composite_rectangles_t *composite,
cairo_bool_t used_stencil_buffer_for_clip)
cairo_composite_rectangles_t *composite)
{
if (! _cairo_gl_ensure_stencil (ctx, setup->dst))
return CAIRO_INT_STATUS_UNSUPPORTED;
@ -522,9 +437,9 @@ _prevent_overlapping_drawing (cairo_gl_context_t *ctx,
&composite->source_sample_area))
return CAIRO_INT_STATUS_SUCCESS;
if (used_stencil_buffer_for_clip == FALSE) {
/* Enable the stencil buffer, even if we have no clip so that
we can use it below to prevent overlapping shapes. We initialize
if (glIsEnabled (GL_STENCIL_TEST) == FALSE) {
/* Enable the stencil buffer, even if we are not using it for clipping,
so we can use it below to prevent overlapping shapes. We initialize
it all to one here which represents infinite clip. */
glDepthMask (GL_TRUE);
glEnable (GL_STENCIL_TEST);
@ -586,7 +501,6 @@ _cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor,
cairo_int_status_t status;
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
struct _tristrip_composite_info info;
cairo_bool_t used_stencil_buffer_for_clip;
if (should_fall_back (dst, antialias))
return CAIRO_INT_STATUS_UNSUPPORTED;
@ -626,18 +540,14 @@ _cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor,
if (unlikely (status))
goto finish;
_cairo_gl_msaa_compositor_set_clip (composite, &info.setup);
status = _cairo_gl_composite_begin_multisample (&info.setup, &info.ctx,
antialias != CAIRO_ANTIALIAS_NONE);
if (unlikely (status))
goto finish;
status = _scissor_and_clip (info.ctx, &info.setup, composite,
&used_stencil_buffer_for_clip);
if (unlikely (status))
goto finish;
status = _prevent_overlapping_drawing (info.ctx, &info.setup, composite,
used_stencil_buffer_for_clip);
status = _prevent_overlapping_drawing (info.ctx, &info.setup, composite);
if (unlikely (status))
goto finish;
@ -658,11 +568,8 @@ _cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor,
finish:
_cairo_gl_composite_fini (&info.setup);
if (info.ctx) {
glDisable (GL_SCISSOR_TEST);
_disable_stencil_buffer ();
if (info.ctx)
status = _cairo_gl_context_release (info.ctx, status);
}
return status;
}
@ -680,7 +587,6 @@ _cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor,
cairo_gl_context_t *ctx = NULL;
cairo_int_status_t status;
cairo_traps_t traps;
cairo_bool_t used_stencil_buffer;
if (should_fall_back (dst, antialias))
return CAIRO_INT_STATUS_UNSUPPORTED;
@ -725,16 +631,13 @@ _cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor,
if (unlikely (status))
goto cleanup_setup;
_cairo_gl_msaa_compositor_set_clip (composite, &setup);
status = _cairo_gl_composite_begin_multisample (&setup, &ctx,
antialias != CAIRO_ANTIALIAS_NONE);
if (unlikely (status))
goto cleanup_setup;
status = _scissor_and_clip (ctx, &setup, composite,
&used_stencil_buffer);
if (unlikely (status))
goto cleanup_setup;
status = _draw_traps (ctx, &setup, &traps);
if (unlikely (status))
goto cleanup_setup;
@ -743,11 +646,9 @@ _cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor,
cleanup_setup:
_cairo_gl_composite_fini (&setup);
if (ctx) {
glDisable (GL_SCISSOR_TEST);
_disable_stencil_buffer ();
if (ctx)
status = _cairo_gl_context_release (ctx, status);
}
cleanup_traps:
_cairo_traps_fini (&traps);

View file

@ -335,6 +335,7 @@ struct _cairo_gl_context {
unsigned int vb_offset;
unsigned int vertex_size;
cairo_region_t *clip_region;
cairo_clip_t *clip;
cairo_array_t tristrip_indices;
cairo_bool_t has_mesa_pack_invert;
@ -361,6 +362,8 @@ typedef struct _cairo_gl_composite {
cairo_gl_operand_t src;
cairo_gl_operand_t mask;
cairo_bool_t spans;
cairo_clip_t *clip;
} cairo_gl_composite_t;
typedef struct _cairo_gl_font {
@ -489,6 +492,10 @@ cairo_private void
_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
cairo_region_t *clip_region);
cairo_private void
_cairo_gl_composite_set_clip(cairo_gl_composite_t *setup,
cairo_clip_t *clip);
cairo_private cairo_int_status_t
_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
const cairo_pattern_t *pattern,
@ -747,6 +754,11 @@ _cairo_gl_pattern_to_source (cairo_surface_t *dst,
const cairo_rectangle_int_t *sample,
int *src_x, int *src_y);
cairo_private cairo_int_status_t
_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_clip_t *clip);
cairo_private cairo_surface_t *
_cairo_gl_white_source (void);