/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-drm-i915-private.h" /* Operates in either immediate or retained mode. * When given a clip region we record the sequence of vbo and then * replay them for each clip rectangle, otherwise we simply emit * the vbo straight into the command stream. */ typedef struct _i915_spans i915_spans_t; typedef float * (*i915_get_rectangle_func_t) (i915_spans_t *spans); typedef void (*i915_span_func_t) (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha); struct _i915_spans { cairo_span_renderer_t renderer; i915_device_t *device; int xmin, xmax; cairo_bool_t is_bounded; const cairo_rectangle_int_t *extents; i915_get_rectangle_func_t get_rectangle; i915_span_func_t span; i915_shader_t shader; cairo_region_t *clip_region; cairo_bool_t need_clip_surface; struct vbo { struct vbo *next; intel_bo_t *bo; unsigned int count; } head, *tail; unsigned int vbo_offset; float *vbo_base; }; static float * i915_emit_rectangle (i915_spans_t *spans) { return i915_add_rectangle (spans->device); } static float * i915_accumulate_rectangle (i915_spans_t *spans) { float *vertices; uint32_t size; size = spans->device->rectangle_size; if (unlikely (spans->vbo_offset + size > I915_VBO_SIZE)) { struct vbo *vbo; vbo = malloc (sizeof (struct vbo)); if (unlikely (vbo == NULL)) { /* throw error! */ } spans->tail->next = vbo; spans->tail = vbo; vbo->next = NULL; vbo->bo = intel_bo_create (&spans->device->intel, I915_VBO_SIZE, I915_VBO_SIZE, FALSE, I915_TILING_NONE, 0); vbo->count = 0; spans->vbo_offset = 0; spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo); } vertices = spans->vbo_base + spans->vbo_offset; spans->vbo_offset += size; spans->tail->count += 3; return vertices; } static void i915_span_zero (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; *vertices++ = x0; *vertices++ = y1; *vertices++ = x0; *vertices++ = y0; } static void i915_span_constant (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; *vertices++ = a; *vertices++ = x0; *vertices++ = y1; *vertices++ = a; *vertices++ = x0; *vertices++ = y0; *vertices++ = a; } static void i915_span_linear (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; double s, t; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; s = x0, t = y0; *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y1; s = x1, t = y0; *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y0; s = x1, t = y1; *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); *vertices++ = a; } static void i915_span_texture (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; double s, t; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; *vertices++ = a; *vertices++ = x0; *vertices++ = y1; s = x1, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; *vertices++ = a; *vertices++ = x0; *vertices++ = y0; s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; *vertices++ = a; } static void i915_span_texture16 (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; double s, t; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y1; s = x1, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y0; s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); *vertices++ = a; } static void i915_span_generic (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { double s, t; float *vertices; float a = alpha / 255.; /* Each vertex is: * 2 vertex coordinates * [0-2] source texture coordinates * 1 alpha value. * [0,2] clip mask coordinates */ vertices = spans->get_rectangle (spans); /* bottom right */ *vertices++ = x1; *vertices++ = y1; s = x1, t = y1; switch (spans->shader.source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); break; } *vertices++ = a; if (spans->need_clip_surface) { s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } if (spans->shader.need_combine) { s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } /* bottom left */ *vertices++ = x0; *vertices++ = y1; s = x0, t = y1; switch (spans->shader.source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); break; } *vertices++ = a; if (spans->need_clip_surface) { s = x0, t = y1; cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } if (spans->shader.need_combine) { s = x0, t = y1; cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } /* top left */ *vertices++ = x0; *vertices++ = y0; s = x0, t = y0; switch (spans->shader.source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); break; } *vertices++ = a; if (spans->need_clip_surface) { s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } if (spans->shader.need_combine) { s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } } static cairo_status_t i915_zero_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; int x0, x1; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { while (num_spans && half[0].coverage < 128) half++, num_spans--; if (num_spans == 0) break; x0 = x1 = half[0].x; while (num_spans--) { half++; x1 = half[0].x; if (half[0].coverage < 128) break; } i915_span_zero (spans, x0, x1, y, y + height, 0); } while (num_spans); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_zero_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; int x0, x1; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { while (num_spans && half[0].coverage == 0) half++, num_spans--; if (num_spans == 0) break; x0 = x1 = half[0].x; while (num_spans--) { half++; x1 = half[0].x; if (half[0].coverage == 0) break; } i915_span_zero (spans, x0, x1, y, y + height, 0); } while (num_spans); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_bounded_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (half[0].coverage >= 128) { spans->span (spans, half[0].x, half[1].x, y, y + height, 255); } half++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_bounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (half[0].coverage) { spans->span (spans, half[0].x, half[1].x, y, y + height, half[0].coverage); } half++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_unbounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) { spans->span (spans, spans->xmin, spans->xmax, y, y + height, 0); return CAIRO_STATUS_SUCCESS; } if (half[0].x != spans->xmin) { spans->span (spans, spans->xmin, half[0].x, y, y + height, 0); } do { spans->span (spans, half[0].x, half[1].x, y, y + height, half[0].coverage); half++; } while (--num_spans > 1); if (half[0].x != spans->xmax) { spans->span (spans, half[0].x, spans->xmax, y, y + height, 0); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_unbounded_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) { spans->span (spans, spans->xmin, spans->xmax, y, y + height, 0); return CAIRO_STATUS_SUCCESS; } if (half[0].x != spans->xmin) { spans->span (spans, spans->xmin, half[0].x, y, y + height, 0); } do { int alpha = 0; if (half[0].coverage >= 128) alpha = 255; spans->span (spans, half[0].x, half[1].x, y, y + height, alpha); half++; } while (--num_spans > 1); if (half[0].x != spans->xmax) { spans->span (spans, half[0].x, spans->xmax, y, y + height, 0); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_spans_init (i915_spans_t *spans, i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, cairo_clip_t *clip, double opacity, const cairo_composite_rectangles_t *extents) { cairo_status_t status; spans->device = (i915_device_t *) dst->intel.drm.base.device; spans->is_bounded = extents->is_bounded; if (extents->is_bounded) { if (antialias == CAIRO_ANTIALIAS_NONE) spans->renderer.render_rows = i915_bounded_spans_mono; else spans->renderer.render_rows = i915_bounded_spans; spans->extents = &extents->bounded; } else { if (antialias == CAIRO_ANTIALIAS_NONE) spans->renderer.render_rows = i915_unbounded_spans_mono; else spans->renderer.render_rows = i915_unbounded_spans; spans->extents = &extents->unbounded; } spans->xmin = spans->extents->x; spans->xmax = spans->extents->x + spans->extents->width; spans->clip_region = NULL; spans->need_clip_surface = FALSE; if (clip != NULL) { cairo_region_t *clip_region = NULL; status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; spans->clip_region = clip_region; spans->need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; } spans->head.next = NULL; spans->head.bo = NULL; spans->head.count = 0; spans->tail = &spans->head; if (spans->clip_region == NULL) { spans->get_rectangle = i915_emit_rectangle; } else { assert (! extents->is_bounded); spans->get_rectangle = i915_accumulate_rectangle; spans->head.bo = intel_bo_create (&spans->device->intel, I915_VBO_SIZE, I915_VBO_SIZE, FALSE, I915_TILING_NONE, 0); if (unlikely (spans->head.bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo); } spans->vbo_offset = 0; i915_shader_init (&spans->shader, dst, op, opacity); if (spans->need_clip_surface) i915_shader_set_clip (&spans->shader, clip); status = i915_shader_acquire_pattern (&spans->shader, &spans->shader.source, pattern, &extents->bounded); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static void i915_spans_fini (i915_spans_t *spans) { i915_shader_fini (&spans->shader); if (spans->head.bo != NULL) { struct vbo *vbo, *next; intel_bo_destroy (&spans->device->intel, spans->head.bo); for (vbo = spans->head.next; vbo != NULL; vbo = next) { next = vbo->next; intel_bo_destroy (&spans->device->intel, vbo->bo); free (vbo); } } } cairo_status_t i915_clip_and_composite_spans (i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, i915_spans_func_t draw_func, void *draw_closure, const cairo_composite_rectangles_t*extents, cairo_clip_t *clip, double opacity) { i915_spans_t spans; i915_device_t *device; cairo_status_t status; struct vbo *vbo; if (i915_surface_needs_tiling (dst)) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } if (op == CAIRO_OPERATOR_CLEAR) { pattern = &_cairo_pattern_white.base; op = CAIRO_OPERATOR_DEST_OUT; } status = i915_spans_init (&spans, dst, op, pattern, antialias, clip, opacity, extents); if (unlikely (status)) return status; spans.shader.mask.base.texfmt = TEXCOORDFMT_1D; spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA; spans.shader.mask.type.fragment = FS_SPANS; status = cairo_device_acquire (dst->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SPANS; if (dst->deferred_clear) { status = i915_surface_clear (dst); if (unlikely (status)) goto CLEANUP_SPANS; } device = i915_device (dst); status = i915_shader_commit (&spans.shader, device); if (unlikely (status)) goto CLEANUP_DEVICE; if (! spans.shader.need_combine && ! spans.need_clip_surface) { switch (spans.shader.source.type.vertex) { case VS_ZERO: spans.span = i915_span_zero; if (extents->is_bounded) { if (antialias == CAIRO_ANTIALIAS_NONE) spans.renderer.render_rows = i915_zero_spans_mono; else spans.renderer.render_rows = i915_zero_spans; } break; case VS_CONSTANT: spans.span = i915_span_constant; break; case VS_LINEAR: spans.span = i915_span_linear; break; case VS_TEXTURE: spans.span = i915_span_texture; break; case VS_TEXTURE_16: spans.span = i915_span_texture16; break; default: spans.span = i915_span_generic; break; } } else { spans.span = i915_span_generic; } status = draw_func (draw_closure, &spans.renderer, spans.extents); if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS) { i915_vbo_finish (device); OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | ENABLE_SCISSOR_RECT); for (vbo = &spans.head; vbo != NULL; vbo = vbo->next) { int i, num_rectangles; /* XXX require_space & batch_flush */ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1); i915_batch_emit_reloc (device, vbo->bo, 0, I915_GEM_DOMAIN_VERTEX, 0, FALSE); OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT) | vbo->count); num_rectangles = cairo_region_num_rectangles (spans.clip_region); for (i = 0; i < num_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (spans.clip_region, i, &rect); OUT_DWORD (_3DSTATE_SCISSOR_RECT_0_CMD); OUT_DWORD (SCISSOR_RECT_0_XMIN (rect.x) | SCISSOR_RECT_0_YMIN (rect.y)); OUT_DWORD (SCISSOR_RECT_0_XMAX (rect.x + rect.width) | SCISSOR_RECT_0_YMAX (rect.y + rect.height)); OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | vbo->count); OUT_DWORD (0); } } OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT); } CLEANUP_DEVICE: cairo_device_release (dst->intel.drm.base.device); CLEANUP_SPANS: i915_spans_fini (&spans); return status; }