diff options
author | sanine <sanine.not@pm.me> | 2022-10-12 12:03:23 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-10-12 12:03:23 -0500 |
commit | 530ffd0b7d3c39757b20f00716e486b5caf89aff (patch) | |
tree | 76b35fdf57317038acf6b828871f6ae25fce2ebe /libs/cairo-1.16.0/src/drm/cairo-drm-i965-surface.c | |
parent | 3dbe9332e47c143a237db12440f134caebd1cfbe (diff) |
add cairo
Diffstat (limited to 'libs/cairo-1.16.0/src/drm/cairo-drm-i965-surface.c')
-rw-r--r-- | libs/cairo-1.16.0/src/drm/cairo-drm-i965-surface.c | 1926 |
1 files changed, 1926 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/src/drm/cairo-drm-i965-surface.c b/libs/cairo-1.16.0/src/drm/cairo-drm-i965-surface.c new file mode 100644 index 0000000..9d29e47 --- /dev/null +++ b/libs/cairo-1.16.0/src/drm/cairo-drm-i965-surface.c @@ -0,0 +1,1926 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Kristian Høgsberg + * Copyright © 2009 Chris Wilson + * 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 Kristian Høgsberg. + * + * Based on the xf86-intel-driver i965 render acceleration code, + * authored by: + * Wang Zhenyu <zhenyu.z.wang@intel.com> + * Eric Anholt <eric@anholt.net> + * Carl Worth <cworth@redhat.com> + * Keith Packard <keithp@keithp.com> + */ + +/* XXX + * + * FIXME: Use brw_PLN for [DevCTG-B+] + * + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-intel-private.h" +#include "cairo-drm-intel-command-private.h" +#include "cairo-drm-intel-ioctl-private.h" +#include "cairo-drm-i965-private.h" + +#include "cairo-boxes-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-region-private.h" +#include "cairo-surface-offset-private.h" + +#include <sys/ioctl.h> +#include <errno.h> + +#define I965_MAX_SIZE 8192 + +static const cairo_surface_backend_t i965_surface_backend; + +static void +i965_stream_init (i965_stream_t *stream, + uint8_t *data, uint32_t size, + struct i965_pending_relocation *pending, int max_pending, + struct drm_i915_gem_relocation_entry *relocations, int max_relocations) + +{ + stream->used = stream->committed = 0; + stream->data = data; + stream->size = size; + stream->serial = 1; + + stream->num_pending_relocations = 0; + stream->max_pending_relocations = max_pending; + stream->pending_relocations = pending; + + stream->num_relocations = 0; + stream->max_relocations = max_relocations; + stream->relocations = relocations; +} + +static void +i965_add_relocation (i965_device_t *device, + intel_bo_t *bo, + uint32_t read_domains, + uint32_t write_domain) +{ + if (bo->exec == NULL) { + int i; + + device->exec.gtt_size += bo->base.size; + + i = device->exec.count++; + assert (i < ARRAY_LENGTH (device->exec.exec)); + + device->exec.exec[i].handle = bo->base.handle; + device->exec.exec[i].relocation_count = 0; + device->exec.exec[i].relocs_ptr = 0; + device->exec.exec[i].alignment = 0; + device->exec.exec[i].offset = 0; + device->exec.exec[i].flags = 0; + device->exec.exec[i].rsvd1 = 0; + device->exec.exec[i].rsvd2 = 0; + + device->exec.bo[i] = intel_bo_reference (bo); + bo->exec = &device->exec.exec[i]; + } + + if (cairo_list_is_empty (&bo->link)) + cairo_list_add_tail (&device->flush, &bo->link); + + assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain); + bo->batch_read_domains |= read_domains; + bo->batch_write_domain |= write_domain; +} + +void +i965_emit_relocation (i965_device_t *device, + i965_stream_t *stream, + intel_bo_t *target, + uint32_t target_offset, + uint32_t read_domains, + uint32_t write_domain, + uint32_t offset) +{ + int n; + + assert (target_offset < target->base.size); + + i965_add_relocation (device, target, read_domains, write_domain); + + n = stream->num_relocations++; + assert (n < stream->max_relocations); + + stream->relocations[n].offset = offset; + stream->relocations[n].delta = target_offset; + stream->relocations[n].target_handle = target->base.handle; + stream->relocations[n].read_domains = read_domains; + stream->relocations[n].write_domain = write_domain; + stream->relocations[n].presumed_offset = target->offset; +} + +static void +i965_stream_reset (i965_stream_t *stream) +{ + stream->used = stream->committed = 0; + stream->num_relocations = 0; + stream->num_pending_relocations = 0; + if (++stream->serial == 0) + stream->serial = 1; +} + +void +i965_stream_commit (i965_device_t *device, + i965_stream_t *stream) +{ + intel_bo_t *bo; + int n; + + assert (stream->used); + + bo = intel_bo_create (&device->intel, + stream->used, stream->used, + FALSE, I915_TILING_NONE, 0); + + /* apply pending relocations */ + for (n = 0; n < stream->num_pending_relocations; n++) { + struct i965_pending_relocation *p = &stream->pending_relocations[n]; + + i965_emit_relocation (device, &device->batch, bo, + p->delta, + p->read_domains, + p->write_domain, + p->offset); + if (bo->offset) + *(uint32_t *) (device->batch.data + p->offset) = bo->offset + p->delta; + } + + intel_bo_write (&device->intel, bo, 0, stream->used, stream->data); + + if (stream->num_relocations) { + assert (bo->exec != NULL); + bo->exec->relocs_ptr = (uintptr_t) stream->relocations; + bo->exec->relocation_count = stream->num_relocations; + } + + intel_bo_destroy (&device->intel, bo); + + i965_stream_reset (stream); +} + +static void +sf_states_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->sf_states, entry); + _cairo_freelist_free (&device->sf_freelist, entry); +} + +static void +cc_offsets_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->cc_states, entry); + _cairo_freelist_free (&device->cc_freelist, entry); +} + +static void +wm_kernels_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->wm_kernels, entry); + _cairo_freelist_free (&device->wm_kernel_freelist, entry); +} + +static void +wm_states_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->wm_states, entry); + _cairo_freelist_free (&device->wm_state_freelist, entry); +} + +static void +wm_bindings_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->wm_bindings, entry); + _cairo_freelist_free (&device->wm_binding_freelist, entry); +} + +static void +samplers_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->samplers, entry); + _cairo_freelist_free (&device->sampler_freelist, entry); +} + +void +i965_general_state_reset (i965_device_t *device) +{ + _cairo_hash_table_foreach (device->sf_states, + sf_states_pluck, + device); + + _cairo_hash_table_foreach (device->cc_states, + cc_offsets_pluck, + device); + + _cairo_hash_table_foreach (device->wm_kernels, + wm_kernels_pluck, + device); + + _cairo_hash_table_foreach (device->wm_states, + wm_states_pluck, + device); + + _cairo_hash_table_foreach (device->wm_bindings, + wm_bindings_pluck, + device); + + _cairo_hash_table_foreach (device->samplers, + samplers_pluck, + device); + + device->vs_offset = (uint32_t) -1; + device->border_color_offset = (uint32_t) -1; + + if (device->general_state != NULL) { + intel_bo_destroy (&device->intel, device->general_state); + device->general_state = NULL; + } +} + +static void +i965_device_reset (i965_device_t *device) +{ + device->exec.count = 0; + device->exec.gtt_size = I965_VERTEX_SIZE + + I965_SURFACE_SIZE + + I965_GENERAL_SIZE + + I965_BATCH_SIZE; + + device->sf_state.entry.hash = (uint32_t) -1; + device->wm_state.entry.hash = (uint32_t) -1; + device->wm_binding.entry.hash = (uint32_t) -1; + device->cc_state.entry.hash = (uint32_t) -1; + + device->target = NULL; + device->source = NULL; + device->mask = NULL; + device->clip = NULL; + + device->draw_rectangle = (uint32_t) -1; + + device->vertex_type = (uint32_t) -1; + device->vertex_size = 0; + device->rectangle_size = 0; + device->last_vertex_size = 0; + + device->constants = NULL; + device->constants_size = 0; + + device->have_urb_fences = FALSE; +} + +static cairo_status_t +i965_exec (i965_device_t *device, uint32_t offset) +{ + struct drm_i915_gem_execbuffer2 execbuf; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int ret, i; + + execbuf.buffers_ptr = (uintptr_t) device->exec.exec; + execbuf.buffer_count = device->exec.count; + execbuf.batch_start_offset = offset; + execbuf.batch_len = device->batch.used; + execbuf.DR1 = 0; + execbuf.DR4 = 0; + execbuf.num_cliprects = 0; + execbuf.cliprects_ptr = 0; + execbuf.flags = I915_GEM_3D_PIPELINE; + execbuf.rsvd1 = 0; + execbuf.rsvd2 = 0; + +#if 0 + printf ("exec: offset=%d, length=%d, buffers=%d\n", + offset, device->batch.used, device->exec.count); + intel_dump_batchbuffer ((uint32_t *) device->batch.data, + device->batch.used, + device->intel.base.chip_id); +#endif + + ret = 0; + do { + ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + } while (ret != 0 && errno == EINTR); + if (unlikely (ret)) { + if (errno == ENOMEM) + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + else + status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + + fprintf (stderr, "Batch submission failed: %d\n", errno); + fprintf (stderr, " gtt size: %zd/%zd\n", + device->exec.gtt_size, device->intel.gtt_avail_size); + + fprintf (stderr, " %d buffers:\n", + device->exec.count); + for (i = 0; i < device->exec.count; i++) { + fprintf (stderr, " exec[%d] = %d\n", + i, device->exec.bo[i]->base.size); + } + + intel_dump_batchbuffer ((uint32_t *) device->batch.data, + device->batch.used, + device->intel.base.chip_id); + } + + /* XXX any write target within the batch should now be in error */ + for (i = 0; i < device->exec.count; i++) { + intel_bo_t *bo = device->exec.bo[i]; + cairo_bool_t ret; + + bo->offset = device->exec.exec[i].offset; + bo->exec = NULL; + bo->batch_read_domains = 0; + bo->batch_write_domain = 0; + + if (bo->virtual) + intel_bo_unmap (bo); + bo->cpu = FALSE; + + if (bo->purgeable) + ret = intel_bo_madvise (&device->intel, bo, I915_MADV_DONTNEED); + /* ignore immediate notification of purging */ + + cairo_list_del (&bo->cache_list); + cairo_list_init (&bo->link); + intel_bo_destroy (&device->intel, bo); + } + cairo_list_init (&device->flush); + + device->exec.count = 0; + + return status; +} + +static inline uint32_t +next_bo_size (uint32_t v) +{ + v = (v + 8191) / 8192; + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v * 8192; +} + +static void +_copy_to_bo_and_apply_relocations (i965_device_t *device, + intel_bo_t *bo, + i965_stream_t *stream, + uint32_t offset) +{ + int n; + + intel_bo_write (&device->intel, bo, + offset, stream->used, + stream->data); + + for (n = 0; n < stream->num_pending_relocations; n++) { + struct i965_pending_relocation *p = &stream->pending_relocations[n]; + + i965_emit_relocation (device, &device->batch, bo, + p->delta + offset, + p->read_domains, + p->write_domain, + p->offset); + + if (bo->offset) { + *(uint32_t *) (device->batch.data + p->offset) = + bo->offset + p->delta + offset; + } + } +} + +cairo_status_t +i965_device_flush (i965_device_t *device) +{ + cairo_status_t status; + uint32_t aligned, max; + intel_bo_t *bo; + int n; + + if (device->batch.used == 0) + return CAIRO_STATUS_SUCCESS; + + i965_flush_vertices (device); + + OUT_BATCH (MI_BATCH_BUFFER_END); + /* Emit a padding dword if we aren't going to be quad-word aligned. */ + if (device->batch.used & 4) + OUT_BATCH (MI_NOOP); + +#if 0 + printf ("device flush: vertex=%d, constant=%d, surface=%d, general=%d, batch=%d\n", + device->vertex.used, + device->constant.used, + device->surface.used, + device->general.used, + device->batch.used); +#endif + + /* can we pack the surface state into the tail of the general state? */ + if (device->general.used == device->general.committed) { + if (device->general.used) { + assert (device->general.num_pending_relocations == 1); + assert (device->general_state != NULL); + i965_emit_relocation (device, &device->batch, + device->general_state, + device->general.pending_relocations[0].delta, + device->general.pending_relocations[0].read_domains, + device->general.pending_relocations[0].write_domain, + device->general.pending_relocations[0].offset); + + if (device->general_state->offset) { + *(uint32_t *) (device->batch.data + + device->general.pending_relocations[0].offset) = + device->general_state->offset + + device->general.pending_relocations[0].delta; + } + } + } else { + assert (device->general.num_pending_relocations == 1); + if (device->general_state != NULL) { + intel_bo_destroy (&device->intel, device->general_state); + device->general_state = NULL; + } + + bo = intel_bo_create (&device->intel, + device->general.used, + device->general.used, + FALSE, I915_TILING_NONE, 0); + if (unlikely (bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + aligned = (device->general.used + 31) & -32; + if (device->surface.used && + aligned + device->surface.used <= bo->base.size) + { + _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0); + _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned); + + if (device->surface.num_relocations) { + for (n = 0; n < device->surface.num_relocations; n++) + device->surface.relocations[n].offset += aligned; + + assert (bo->exec != NULL); + bo->exec->relocs_ptr = (uintptr_t) device->surface.relocations; + bo->exec->relocation_count = device->surface.num_relocations; + } + + i965_stream_reset (&device->surface); + } + else + { + _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0); + } + + /* Note we don't reset the general state, just mark what data we've committed. */ + device->general.committed = device->general.used; + device->general_state = bo; + } + device->general.num_pending_relocations = 0; + + /* Combine vertex+constant+surface+batch streams? */ + max = aligned = device->vertex.used; + if (device->surface.used) { + aligned = (aligned + 63) & -64; + aligned += device->surface.used; + if (device->surface.used > max) + max = device->surface.used; + } + aligned = (aligned + 63) & -64; + aligned += device->batch.used; + if (device->batch.used > max) + max = device->batch.used; + if (aligned <= next_bo_size (max)) { + int batch_num_relocations; + + if (aligned <= 8192) + max = aligned; + + bo = intel_bo_create (&device->intel, + max, max, + FALSE, I915_TILING_NONE, 0); + if (unlikely (bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + assert (aligned <= bo->base.size); + + if (device->vertex.used) + _copy_to_bo_and_apply_relocations (device, bo, &device->vertex, 0); + + aligned = device->vertex.used; + + batch_num_relocations = device->batch.num_relocations; + if (device->surface.used) { + aligned = (aligned + 63) & -64; + _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned); + + batch_num_relocations = device->batch.num_relocations; + if (device->surface.num_relocations) { + assert (device->batch.num_relocations + device->surface.num_relocations < device->batch.max_relocations); + + memcpy (device->batch.relocations + device->batch.num_relocations, + device->surface.relocations, + sizeof (device->surface.relocations[0]) * device->surface.num_relocations); + + for (n = 0; n < device->surface.num_relocations; n++) + device->batch.relocations[device->batch.num_relocations + n].offset += aligned; + + device->batch.num_relocations += device->surface.num_relocations; + } + + aligned += device->surface.used; + } + + aligned = (aligned + 63) & -64; + intel_bo_write (&device->intel, bo, + aligned, device->batch.used, + device->batch.data); + + for (n = 0; n < batch_num_relocations; n++) + device->batch.relocations[n].offset += aligned; + + if (device->exec.bo[device->exec.count-1] == bo) { + assert (bo->exec == &device->exec.exec[device->exec.count-1]); + + bo->exec->relocation_count = device->batch.num_relocations; + bo->exec->relocs_ptr = (uintptr_t) device->batch.relocations; + intel_bo_destroy (&device->intel, bo); + } else { + assert (bo->exec == NULL); + + n = device->exec.count++; + device->exec.exec[n].handle = bo->base.handle; + device->exec.exec[n].relocation_count = device->batch.num_relocations; + device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations; + device->exec.exec[n].alignment = 0; + device->exec.exec[n].offset = 0; + device->exec.exec[n].flags = 0; + device->exec.exec[n].rsvd1 = 0; + device->exec.exec[n].rsvd2 = 0; + + /* transfer ownership to the exec */ + device->exec.bo[n] = bo; + } + } else { + i965_stream_commit (device, &device->vertex); + if (device->surface.used) + i965_stream_commit (device, &device->surface); + + bo = intel_bo_create (&device->intel, + device->batch.used, device->batch.used, + FALSE, I915_TILING_NONE, 0); + if (unlikely (bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + intel_bo_write (&device->intel, bo, + 0, device->batch.used, + device->batch.data); + + n = device->exec.count++; + device->exec.exec[n].handle = bo->base.handle; + device->exec.exec[n].relocation_count = device->batch.num_relocations; + device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations; + device->exec.exec[n].alignment = 0; + device->exec.exec[n].offset = 0; + device->exec.exec[n].flags = 0; + device->exec.exec[n].rsvd1 = 0; + device->exec.exec[n].rsvd2 = 0; + + /* transfer ownership to the exec */ + device->exec.bo[n] = bo; + aligned = 0; + } + + status = i965_exec (device, aligned); + + i965_stream_reset (&device->vertex); + i965_stream_reset (&device->surface); + i965_stream_reset (&device->batch); + + intel_glyph_cache_unpin (&device->intel); + intel_snapshot_cache_thaw (&device->intel); + + i965_device_reset (device); + + return status; +} + +static cairo_surface_t * +i965_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, int height) +{ + i965_surface_t *other; + cairo_format_t format; + + if (width > 8192 || height > 8192) + return NULL; + + other = abstract_other; + if (content == other->intel.drm.base.content) + format = other->intel.drm.format; + else + format = _cairo_format_from_content (content); + + return i965_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device, + format, + width, height, + I965_TILING_DEFAULT, TRUE); +} + +static cairo_status_t +i965_surface_finish (void *abstract_surface) +{ + i965_surface_t *surface = abstract_surface; + + return intel_surface_finish (&surface->intel); +} + +static cairo_status_t +i965_surface_flush (void *abstract_surface, unsigned flags) +{ + i965_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->intel.drm.fallback != NULL) + return intel_surface_flush (abstract_surface); + + /* Forgo flushing on finish as the user cannot access the surface directly. */ + if (! surface->intel.drm.base.finished && + to_intel_bo (surface->intel.drm.bo)->exec != NULL) + { + status = cairo_device_acquire (surface->intel.drm.base.device); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + i965_device_t *device; + + device = i965_device (surface); + status = i965_device_flush (device); + cairo_device_release (&device->intel.base.base); + } + } + + return status; +} + +/* rasterisation */ + +static cairo_status_t +_composite_boxes_spans (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents) +{ + cairo_boxes_t *boxes = closure; + cairo_rectangular_scan_converter_t converter; + struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + + _cairo_rectangular_scan_converter_init (&converter, extents); + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + int i; + + for (i = 0; i < chunk->count; i++) { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto CLEANUP; + } + } + + status = converter.base.generate (&converter.base, renderer); + + CLEANUP: + converter.base.destroy (&converter.base); + return status; +} + +cairo_status_t +i965_fixup_unbounded (i965_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + i965_shader_t shader; + i965_device_t *device; + cairo_status_t status; + + i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR); + + if (clip != NULL) { + cairo_region_t *clip_region = NULL; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + assert (clip_region == NULL); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i965_shader_set_clip (&shader, clip); + } else { + if (extents->bounded.width == extents->unbounded.width && + extents->bounded.height == extents->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_clear.base, + &extents->unbounded); + if (unlikely (status)) { + i965_shader_fini (&shader); + return status; + } + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + return status; + + status = i965_shader_commit (&shader, device); + if (unlikely (status)) { + goto BAIL; + } + + if (extents->bounded.width == 0 || extents->bounded.height == 0) { + i965_shader_add_rectangle (&shader, + extents->unbounded.x, + extents->unbounded.y, + extents->unbounded.width, + extents->unbounded.height); + } else { /* top */ + if (extents->bounded.y != extents->unbounded.y) { + cairo_rectangle_int_t rect; + + rect.x = extents->unbounded.x; + rect.y = extents->unbounded.y; + rect.width = extents->unbounded.width; + rect.height = extents->bounded.y - rect.y; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + cairo_rectangle_int_t rect; + + rect.x = extents->unbounded.x; + rect.y = extents->bounded.y; + rect.width = extents->bounded.x - extents->unbounded.x; + rect.height = extents->bounded.height; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + cairo_rectangle_int_t rect; + + rect.x = extents->bounded.x + extents->bounded.width; + rect.y = extents->bounded.y; + rect.width = extents->unbounded.x + extents->unbounded.width - rect.x; + rect.height = extents->bounded.height; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + cairo_rectangle_int_t rect; + + rect.x = extents->unbounded.x; + rect.y = extents->bounded.y + extents->bounded.height; + rect.width = extents->unbounded.width; + rect.height = extents->unbounded.y + extents->unbounded.height - rect.y; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + } + + i965_shader_fini (&shader); + BAIL: + cairo_device_release (&device->intel.base.base); + return status; +} + +static cairo_status_t +i965_fixup_unbounded_boxes (i965_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip, + cairo_boxes_t *boxes) +{ + cairo_boxes_t clear; + cairo_box_t box; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + i965_shader_t shader; + int i; + + if (boxes->num_boxes <= 1) + return i965_fixup_unbounded (dst, extents, clip); + + i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR); + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i965_shader_set_clip (&shader, clip); + } + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_clear.base, + &extents->unbounded); + if (unlikely (status)) { + i965_shader_fini (&shader); + return status; + } + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip_region == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + pixman_box32_t *pbox; + + pbox = pixman_region32_rectangles (&clip_region->rgn, &i); + _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); + + status = _cairo_boxes_add (&clear, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) { + i965_device_t *device; + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); + int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); + int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); + int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); + + i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); + } + } + +err_device: + cairo_device_release (&device->intel.base.base); +err_shader: + i965_shader_fini (&shader); + } + + _cairo_boxes_fini (&clear); + + return status; +} + +static cairo_status_t +_composite_boxes (i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_bool_t need_clip_surface = FALSE; + cairo_region_t *clip_region = NULL; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + i965_shader_t shader; + i965_device_t *device; + int i; + + /* If the boxes are not pixel-aligned, we will need to compute a real mask */ + if (antialias != CAIRO_ANTIALIAS_NONE) { + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + i965_shader_init (&shader, dst, op); + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + pattern, + &extents->bounded); + if (unlikely (status)) + return status; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_surface) + i965_shader_set_clip (&shader, clip); + } + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_commit (&shader, i965_device (dst)); + if (unlikely (status)) + goto err_device; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x2 > x1 && y2 > y1) + i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); + } + } + + if (! extents->is_bounded) + status = i965_fixup_unbounded_boxes (dst, extents, clip, boxes); + + err_device: + cairo_device_release (&device->intel.base.base); + err_shader: + i965_shader_fini (&shader); + + return status; +} + +static cairo_status_t +_clip_and_composite_boxes (i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (boxes->num_boxes == 0) { + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + return i965_fixup_unbounded (dst, extents, clip); + } + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Otherwise render the boxes via an implicit mask and composite in the usual + * fashion. + */ + return i965_clip_and_composite_spans (dst, op, src, antialias, + _composite_boxes_spans, boxes, + extents, clip); +} + +static cairo_int_status_t +i965_surface_paint (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + cairo_boxes_t boxes; + cairo_box_t *clip_boxes = boxes.boxes_embedded; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded); + cairo_status_t status; + + /* XXX unsupported operators? use pixel shader blending, eventually */ + + status = _cairo_composite_rectangles_init_for_paint (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, + clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _clip_and_composite_boxes (dst, op, source, + &boxes, CAIRO_ANTIALIAS_DEFAULT, + &extents, clip); + if (clip_boxes != boxes.boxes_embedded) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i965_surface_mask (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + i965_shader_t shader; + i965_device_t *device; + cairo_clip_t local_clip; + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_surface = FALSE; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) { + _cairo_clip_fini (&local_clip); + return status; + } + + have_clip = TRUE; + } + + i965_shader_init (&shader, dst, op); + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + source, + &extents.bounded); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_acquire_pattern (&shader, + &shader.mask, + mask, + &extents.bounded); + if (unlikely (status)) + goto err_shader; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_surface) + i965_shader_set_clip (&shader, clip); + } + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + if (clip_region != NULL) { + unsigned int n, num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rectangles; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + } else { + i965_shader_add_rectangle (&shader, + extents.bounded.x, + extents.bounded.y, + extents.bounded.width, + extents.bounded.height); + } + + if (! extents.is_bounded) + status = i965_fixup_unbounded (dst, &extents, clip); + + err_device: + cairo_device_release (&device->intel.base.base); + err_shader: + i965_shader_fini (&shader); + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +typedef struct { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_polygon_info_t; + +static cairo_status_t +_composite_polygon_spans (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents) +{ + composite_polygon_info_t *info = closure; + cairo_botor_scan_converter_t converter; + cairo_status_t status; + cairo_box_t box; + + box.p1.x = _cairo_fixed_from_int (extents->x); + box.p1.y = _cairo_fixed_from_int (extents->y); + box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); + box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); + + _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); + + status = converter.base.add_polygon (&converter.base, &info->polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = converter.base.generate (&converter.base, renderer); + + converter.base.destroy (&converter.base); + + return status; +} + +static cairo_int_status_t +i965_surface_stroke (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + composite_polygon_info_t info; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, + path, stroke_style, ctm, + clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + stroke_style, + ctm, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (dst, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto CLEANUP_BOXES; + } + + _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &info.polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (extents.is_bounded) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); + if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) + goto CLEANUP_POLYGON; + } + + if (info.polygon.num_edges == 0) { + if (! extents.is_bounded) + status = i965_fixup_unbounded (dst, &extents, clip); + } else { + info.fill_rule = CAIRO_FILL_RULE_WINDING; + info.antialias = antialias; + status = i965_clip_and_composite_spans (dst, op, source, antialias, + _composite_polygon_spans, &info, + &extents, clip); + } + +CLEANUP_POLYGON: + _cairo_polygon_fini (&info.polygon); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i965_surface_fill (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + composite_polygon_info_t info; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + assert (! _cairo_path_fixed_fill_is_empty (path)); + + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (dst, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto CLEANUP_BOXES; + } + + _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (extents.is_bounded) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); + if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) + goto CLEANUP_POLYGON; + } + + if (info.polygon.num_edges == 0) { + if (! extents.is_bounded) + status = i965_fixup_unbounded (dst, &extents, clip); + } else { + info.fill_rule = fill_rule; + info.antialias = antialias; + status = i965_clip_and_composite_spans (dst, op, source, antialias, + _composite_polygon_spans, &info, + &extents, clip); + } + +CLEANUP_POLYGON: + _cairo_polygon_fini (&info.polygon); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static const cairo_surface_backend_t i965_surface_backend = { + CAIRO_SURFACE_TYPE_DRM, + _cairo_default_context_create, + + i965_surface_create_similar, + i965_surface_finish, + + NULL, + intel_surface_acquire_source_image, + intel_surface_release_source_image, + + NULL, NULL, NULL, + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_drm_surface_get_extents, + NULL, /* old-glyphs */ + _cairo_drm_surface_get_font_options, + + i965_surface_flush, + NULL, /* mark_dirty */ + intel_scaled_font_fini, + intel_scaled_glyph_fini, + + i965_surface_paint, + i965_surface_mask, + i965_surface_stroke, + i965_surface_fill, + i965_surface_glyphs, +}; + +static void +i965_surface_init (i965_surface_t *surface, + cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + intel_surface_init (&surface->intel, &i965_surface_backend, device, + format, width, height); + surface->stream = 0; +} + +static inline int cairo_const +i965_tiling_stride (uint32_t tiling, int stride) +{ + if (tiling == I915_TILING_NONE) + return stride; + + return (stride + 127) & -128; +} + +static inline int cairo_const +i965_tiling_height (uint32_t tiling, int height) +{ + switch (tiling) { + default: + case I915_TILING_NONE: return (height + 1) & -2; + case I915_TILING_X: return (height + 7) & -8; + case I915_TILING_Y: return (height + 31) & -32; + } +} + +cairo_surface_t * +i965_surface_create_internal (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height, + uint32_t tiling, + cairo_bool_t gpu_target) +{ + i965_surface_t *surface; + cairo_status_t status_ignored; + + surface = _cairo_malloc (sizeof (i965_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + i965_surface_init (surface, base_dev, format, width, height); + + if (width && height) { + uint32_t size, stride; + intel_bo_t *bo; + + width = (width + 3) & -4; + stride = cairo_format_stride_for_width (surface->intel.drm.format, width); + stride = (stride + 63) & ~63; + stride = i965_tiling_stride (tiling, stride); + surface->intel.drm.stride = stride; + + height = i965_tiling_height (tiling, height); + assert (height <= I965_MAX_SIZE); + + size = stride * height; + bo = intel_bo_create (to_intel_device (&base_dev->base), + size, size, + gpu_target, tiling, stride); + if (bo == NULL) { + status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + bo->tiling = tiling; + bo->stride = stride; + surface->intel.drm.bo = &bo->base; + + assert (bo->base.size >= (size_t) stride*height); + } + + return &surface->intel.drm.base; +} + +static cairo_surface_t * +i965_surface_create (cairo_drm_device_t *device, + cairo_format_t format, int width, int height) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_INVALID: + default: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + return i965_surface_create_internal (device, format, width, height, + I965_TILING_DEFAULT, TRUE); +} + +static cairo_surface_t * +i965_surface_create_for_name (cairo_drm_device_t *base_dev, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + i965_device_t *device; + i965_surface_t *surface; + cairo_status_t status_ignored; + int min_stride; + + min_stride = cairo_format_stride_for_width (format, (width + 3) & -4); + if (stride < min_stride || stride & 63) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + + if (format == CAIRO_FORMAT_A1) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_INVALID: + default: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + surface = _cairo_malloc (sizeof (i965_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + i965_surface_init (surface, base_dev, format, width, height); + + device = (i965_device_t *) base_dev; + surface->intel.drm.bo = &intel_bo_create_for_name (&device->intel, name)->base; + if (unlikely (surface->intel.drm.bo == NULL)) { + status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + surface->intel.drm.stride = stride; + + return &surface->intel.drm.base; +} + +static cairo_status_t +i965_surface_enable_scan_out (void *abstract_surface) +{ + i965_surface_t *surface = abstract_surface; + intel_bo_t *bo; + + if (unlikely (surface->intel.drm.bo == NULL)) + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); + + bo = to_intel_bo (surface->intel.drm.bo); + if (bo->tiling != I915_TILING_X) { + i965_device_t *device = i965_device (surface); + cairo_surface_pattern_t pattern; + cairo_surface_t *clone; + cairo_status_t status; + + clone = i965_surface_create_internal (&device->intel.base, + surface->intel.drm.base.content, + surface->intel.drm.width, + surface->intel.drm.height, + I915_TILING_X, + TRUE); + if (unlikely (clone->status)) + return clone->status; + + /* 2D blit? */ + _cairo_pattern_init_for_surface (&pattern, &surface->intel.drm.base); + pattern.base.filter = CAIRO_FILTER_NEAREST; + + status = _cairo_surface_paint (clone, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (clone); + return status; + } + + /* swap buffer objects */ + surface->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo; + ((cairo_drm_surface_t *) clone)->bo = &bo->base; + bo = to_intel_bo (surface->intel.drm.bo); + + cairo_surface_destroy (clone); + } + + if (unlikely (bo->tiling == I915_TILING_Y)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */ + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_i965_device_flush (cairo_drm_device_t *device) +{ + cairo_status_t status; + + if (unlikely (device->base.finished)) + return CAIRO_STATUS_SUCCESS; + + status = cairo_device_acquire (&device->base); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = i965_device_flush ((i965_device_t *) device); + + cairo_device_release (&device->base); + + return status; +} + +static cairo_int_status_t +_i965_device_throttle (cairo_drm_device_t *device) +{ + cairo_status_t status; + + status = cairo_device_acquire (&device->base); + if (unlikely (status)) + return status; + + status = i965_device_flush ((i965_device_t *) device); + intel_throttle ((intel_device_t *) device); + + cairo_device_release (&device->base); + + return status; +} + +static void +_i965_device_destroy (void *base) +{ + i965_device_t *device = base; + + i965_device_reset (device); + i965_general_state_reset (device); + + _cairo_hash_table_destroy (device->sf_states); + _cairo_hash_table_destroy (device->samplers); + _cairo_hash_table_destroy (device->cc_states); + _cairo_hash_table_destroy (device->wm_kernels); + _cairo_hash_table_destroy (device->wm_states); + _cairo_hash_table_destroy (device->wm_bindings); + + _cairo_freelist_fini (&device->sf_freelist); + _cairo_freelist_fini (&device->cc_freelist); + _cairo_freelist_fini (&device->wm_kernel_freelist); + _cairo_freelist_fini (&device->wm_state_freelist); + _cairo_freelist_fini (&device->wm_binding_freelist); + _cairo_freelist_fini (&device->sampler_freelist); + + intel_device_fini (&device->intel); + free (device); +} + +static cairo_bool_t +hash_equal (const void *A, const void *B) +{ + const cairo_hash_entry_t *a = A, *b = B; + return a->hash == b->hash; +} + +cairo_drm_device_t * +_cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id) +{ + i965_device_t *device; + uint64_t gtt_size; + cairo_status_t status; + + if (! intel_info (fd, >t_size)) + return NULL; + + device = _cairo_malloc (sizeof (i965_device_t)); + if (unlikely (device == NULL)) + return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + status = intel_device_init (&device->intel, fd); + if (unlikely (status)) + goto CLEANUP; + + device->is_g4x = IS_G4X (chip_id); + //device->is_g5x = IS_G5X (chip_id); + + device->intel.base.surface.create = i965_surface_create; + device->intel.base.surface.create_for_name = i965_surface_create_for_name; + device->intel.base.surface.create_from_cacheable_image = NULL; + device->intel.base.surface.enable_scan_out = i965_surface_enable_scan_out; + + device->intel.base.device.flush = _i965_device_flush; + device->intel.base.device.throttle = _i965_device_throttle; + device->intel.base.device.destroy = _i965_device_destroy; + + device->sf_states = _cairo_hash_table_create (i965_sf_state_equal); + if (unlikely (device->sf_states == NULL)) + goto CLEANUP_INTEL; + + _cairo_freelist_init (&device->sf_freelist, + sizeof (struct i965_sf_state)); + + + device->cc_states = _cairo_hash_table_create (i965_cc_state_equal); + if (unlikely (device->cc_states == NULL)) + goto CLEANUP_SF; + + _cairo_freelist_init (&device->cc_freelist, + sizeof (struct i965_cc_state)); + + + device->wm_kernels = _cairo_hash_table_create (hash_equal); + if (unlikely (device->wm_kernels == NULL)) + goto CLEANUP_CC; + + _cairo_freelist_init (&device->wm_kernel_freelist, + sizeof (struct i965_wm_kernel)); + + device->wm_states = _cairo_hash_table_create (i965_wm_state_equal); + if (unlikely (device->wm_states == NULL)) + goto CLEANUP_WM_KERNEL; + + _cairo_freelist_init (&device->wm_state_freelist, + sizeof (struct i965_wm_state)); + + + device->wm_bindings = _cairo_hash_table_create (i965_wm_binding_equal); + if (unlikely (device->wm_bindings == NULL)) + goto CLEANUP_WM_STATE; + + _cairo_freelist_init (&device->wm_binding_freelist, + sizeof (struct i965_wm_binding)); + + device->samplers = _cairo_hash_table_create (hash_equal); + if (unlikely (device->samplers == NULL)) + goto CLEANUP_WM_BINDING; + + _cairo_freelist_init (&device->sampler_freelist, + sizeof (struct i965_sampler)); + + i965_stream_init (&device->batch, + device->batch_base, sizeof (device->batch_base), + NULL, 0, + device->batch_relocations, + ARRAY_LENGTH (device->batch_relocations)); + + i965_stream_init (&device->surface, + device->surface_base, sizeof (device->surface_base), + device->surface_pending_relocations, + ARRAY_LENGTH (device->surface_pending_relocations), + device->surface_relocations, + ARRAY_LENGTH (device->surface_relocations)); + + i965_stream_init (&device->general, + device->general_base, sizeof (device->general_base), + device->general_pending_relocations, + ARRAY_LENGTH (device->general_pending_relocations), + NULL, 0); + + i965_stream_init (&device->vertex, + device->vertex_base, sizeof (device->vertex_base), + device->vertex_pending_relocations, + ARRAY_LENGTH (device->vertex_pending_relocations), + NULL, 0); + + cairo_list_init (&device->flush); + i965_device_reset (device); + device->vs_offset = (uint32_t) -1; + device->border_color_offset = (uint32_t) -1; + device->general_state = NULL; + + return _cairo_drm_device_init (&device->intel.base, + fd, dev, vendor_id, chip_id, + I965_MAX_SIZE); + + CLEANUP_WM_BINDING: + _cairo_hash_table_destroy (device->wm_bindings); + CLEANUP_WM_STATE: + _cairo_hash_table_destroy (device->wm_states); + CLEANUP_WM_KERNEL: + _cairo_hash_table_destroy (device->wm_kernels); + CLEANUP_CC: + _cairo_hash_table_destroy (device->cc_states); + CLEANUP_SF: + _cairo_hash_table_destroy (device->sf_states); + CLEANUP_INTEL: + intel_device_fini (&device->intel); + CLEANUP: + free (device); + return (cairo_drm_device_t *) _cairo_device_create_in_error (status); +} |