cairo-drm-i915-spans.c [plain text]
#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"
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)) {
}
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.;
vertices = spans->get_rectangle (spans);
*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);
}
*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);
}
*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;
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;
}