diff options
Diffstat (limited to 'libs/cairo-1.16.0/src/drm/cairo-drm-gallium-surface.c')
-rw-r--r-- | libs/cairo-1.16.0/src/drm/cairo-drm-gallium-surface.c | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/src/drm/cairo-drm-gallium-surface.c b/libs/cairo-1.16.0/src/drm/cairo-drm-gallium-surface.c new file mode 100644 index 0000000..ca18f73 --- /dev/null +++ b/libs/cairo-1.16.0/src/drm/cairo-drm-gallium-surface.c @@ -0,0 +1,826 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * Copyright © 2009 Eric Anholt + * + * 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 Chris Wilson. + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" + +#include <dlfcn.h> + +#include <state_tracker/drm_api.h> +#include <pipe/p_format.h> +#include <pipe/p_screen.h> +#include <pipe/p_context.h> +#include <pipe/p_state.h> + +#include <util/u_inlines.h> + +typedef struct _gallium_surface gallium_surface_t; +typedef struct _gallium_device gallium_device_t; + +struct _gallium_device { + cairo_drm_device_t drm; + + void *dlhandle; + struct drm_api *api; + + struct pipe_screen *screen; + struct pipe_context *pipe; + + int max_size; +}; + +struct _gallium_surface { + cairo_drm_surface_t drm; + + enum pipe_format pipe_format; + + struct pipe_resource *texture; + struct pipe_transfer *map_transfer; + + cairo_surface_t *fallback; +}; + +static cairo_surface_t * +gallium_surface_create_internal (gallium_device_t *device, + enum pipe_format format, + int width, int height); + +static inline gallium_device_t * +gallium_device (gallium_surface_t *surface) +{ + return (gallium_device_t *) surface->drm.base.device; +} + +static cairo_format_t +_cairo_format_from_pipe_format (enum pipe_format format) +{ + switch ((int) format) { + case PIPE_FORMAT_A8_UNORM: + return CAIRO_FORMAT_A8; + case PIPE_FORMAT_A8R8G8B8_UNORM: + return CAIRO_FORMAT_ARGB32; + default: + return CAIRO_FORMAT_INVALID; + } +} + +static enum pipe_format +pipe_format_from_format (cairo_format_t format) +{ + switch ((int) format) { + case CAIRO_FORMAT_A8: + return PIPE_FORMAT_A8_UNORM; + case CAIRO_FORMAT_ARGB32: + return PIPE_FORMAT_A8R8G8B8_UNORM; + default: + return (enum pipe_format) -1; + } +} + +static enum pipe_format +pipe_format_from_content (cairo_content_t content) +{ + if (content == CAIRO_CONTENT_ALPHA) + return PIPE_FORMAT_A8_UNORM; + else + return PIPE_FORMAT_A8R8G8B8_UNORM; +} + +static cairo_bool_t +format_is_supported_destination (gallium_device_t *device, + enum pipe_format format) +{ + if (format == (enum pipe_format) -1) + return FALSE; + + return device->screen->is_format_supported (device->screen, + format, + 0, + PIPE_BIND_RENDER_TARGET, + 0); +} + +#if 0 +static cairo_bool_t +format_is_supported_source (gallium_device_t *device, + enum pipe_format format) +{ + return device->screen->is_format_supported (device->screen, + format, + 0, + PIPE_BIND_SAMPLER_VIEW, + 0); +} +#endif + +static cairo_surface_t * +gallium_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + gallium_surface_t *other = abstract_src; + gallium_device_t *device = gallium_device (other); + enum pipe_format pipe_format; + cairo_surface_t *surface = NULL; + cairo_status_t status; + + status = cairo_device_acquire (&device->drm.base); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (MAX (width, height) > device->max_size) + goto RELEASE; + + if (content == other->drm.base.content) + pipe_format = other->pipe_format; + else + pipe_format = pipe_format_from_content (content); + + if (! format_is_supported_destination (device, pipe_format)) + goto RELEASE; + + surface = gallium_surface_create_internal (device, + pipe_format, + width, height); + +RELEASE: + cairo_device_release (&device->drm.base); + + return surface; +} + +static cairo_status_t +gallium_surface_finish (void *abstract_surface) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device = gallium_device (surface); + cairo_status_t status; + + status = cairo_device_acquire (&device->drm.base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + pipe_resource_reference (&surface->texture, NULL); + cairo_device_release (&device->drm.base); + } + + return _cairo_drm_surface_finish (&surface->drm); +} + +static cairo_surface_t * +gallium_surface_map_to_image (gallium_surface_t *surface) +{ + gallium_device_t *device = gallium_device (surface); + cairo_status_t status; + void *ptr = NULL; + + status = cairo_device_acquire (&device->drm.base); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface->map_transfer = + pipe_get_transfer (device->pipe, + surface->texture, 0, 0, 0, + PIPE_TRANSFER_MAP_DIRECTLY | + PIPE_TRANSFER_READ_WRITE, + 0, 0, + surface->drm.width, + surface->drm.height); + if (likely (surface->map_transfer != NULL)) + ptr = device->pipe->transfer_map (device->pipe, surface->map_transfer); + + cairo_device_release (&device->drm.base); + + if (unlikely (ptr == NULL)) { + if (surface->map_transfer != NULL) { + device->pipe->transfer_destroy (device->pipe, + surface->map_transfer); + surface->map_transfer = NULL; + } + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + return cairo_image_surface_create_for_data (ptr, + surface->drm.format, + surface->drm.width, + surface->drm.height, + surface->map_transfer->stride); +} + +static cairo_status_t +gallium_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device = gallium_device (surface); + cairo_format_t format; + cairo_surface_t *image; + cairo_status_t status; + struct pipe_transfer *transfer; + void *ptr; + + if (surface->fallback != NULL) { + *image_out = (cairo_image_surface_t *) + cairo_surface_reference (surface->fallback); + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + if (unlikely (surface->drm.width == 0 || surface->drm.height == 0)) { + image = cairo_image_surface_create (surface->drm.format, 0, 0); + if (unlikely (image->status)) + return image->status; + + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + format = _cairo_format_from_pipe_format (surface->pipe_format); + if (format == CAIRO_FORMAT_INVALID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = cairo_device_acquire (&device->drm.base); + if (unlikely (status)) + return status; + + transfer = pipe_get_transfer (device->pipe, + surface->texture, 0, 0, 0, + PIPE_TRANSFER_READ, + 0, 0, + surface->drm.width, + surface->drm.height); + ptr = device->pipe->transfer_map (device->pipe, transfer); + cairo_device_release (&device->drm.base); + + image = cairo_image_surface_create_for_data (ptr, format, + surface->drm.width, + surface->drm.height, + surface->drm.stride); + if (unlikely (image->status)) + return image->status; + + *image_out = (cairo_image_surface_t *) image; + *image_extra = transfer; + return CAIRO_STATUS_SUCCESS; +} + +static void +gallium_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); + + if (image_extra != NULL) { + gallium_device_t *device = gallium_device (abstract_surface); + + device->pipe->transfer_unmap (device->pipe, image_extra); + device->pipe->transfer_destroy (device->pipe, image_extra); + } +} + +static cairo_status_t +gallium_surface_flush (void *abstract_surface, + unsigned flags) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device = gallium_device (surface); + cairo_status_t status; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->fallback == NULL) { + device->pipe->flush (device->pipe, + PIPE_FLUSH_RENDER_CACHE, + NULL); + return CAIRO_STATUS_SUCCESS; + } + + /* kill any outstanding maps */ + cairo_surface_finish (surface->fallback); + + status = cairo_device_acquire (&device->drm.base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + device->pipe->transfer_unmap (device->pipe, + surface->map_transfer); + device->pipe->transfer_destroy (device->pipe, + surface->map_transfer); + surface->map_transfer = NULL; + cairo_device_release (&device->drm.base); + } + + status = cairo_surface_status (surface->fallback); + cairo_surface_destroy (surface->fallback); + surface->fallback = NULL; + + return status; +} + +static cairo_int_status_t +gallium_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_paint (surface->fallback, op, source, clip); +} + +static cairo_int_status_t +gallium_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_mask (surface->fallback, + op, source, mask, + clip); +} + +static cairo_int_status_t +gallium_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_stroke (surface->fallback, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +gallium_surface_fill (void *abstract_surface, + 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) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_fill (surface->fallback, + op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +gallium_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *num_remaining) +{ + gallium_surface_t *surface = abstract_surface; + + *num_remaining = 0; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_show_text_glyphs (surface->fallback, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); +} + +static const cairo_surface_backend_t gallium_surface_backend = { + CAIRO_SURFACE_TYPE_DRM, + _cairo_default_context_create, + + gallium_surface_create_similar, + gallium_surface_finish, + + NULL, + gallium_surface_acquire_source_image, + gallium_surface_release_source_image, + + NULL, //gallium_surface_acquire_dest_image, + NULL, //gallium_surface_release_dest_image, + NULL, //gallium_surface_clone_similar, + NULL, //gallium_surface_composite, + NULL, //gallium_surface_fill_rectangles, + NULL, //gallium_surface_composite_trapezoids, + NULL, //gallium_surface_create_span_renderer, + NULL, //gallium_surface_check_span_renderer, + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_drm_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_drm_surface_get_font_options, + gallium_surface_flush, + NULL, /* mark_dirty_rectangle */ + NULL, //gallium_surface_scaled_font_fini, + NULL, //gallium_surface_scaled_glyph_fini, + + gallium_surface_paint, + gallium_surface_mask, + gallium_surface_stroke, + gallium_surface_fill, + gallium_surface_glyphs, + + NULL, /* snapshot */ + + NULL, /* is_similar */ + + NULL, /* reset */ +}; + +static int +gallium_format_stride_for_width (enum pipe_format format, int width) +{ + int stride; + + stride = 1024; /* XXX fugly */ + while (stride < width) + stride *= 2; + + if (format == PIPE_FORMAT_A8R8G8B8_UNORM) + stride *= 4; + + return stride; +} + +static cairo_drm_bo_t * +_gallium_fake_bo_create (uint32_t size, uint32_t name) +{ + cairo_drm_bo_t *bo; + + /* XXX integrate with winsys handle */ + + bo = _cairo_malloc (sizeof (cairo_drm_bo_t)); + + CAIRO_REFERENCE_COUNT_INIT (&bo->ref_count, 1); + bo->name = name; + bo->handle = 0; + bo->size = size; + + return bo; +} + +static void +_gallium_fake_bo_release (void *dev, void *bo) +{ + free (bo); +} + +static cairo_surface_t * +gallium_surface_create_internal (gallium_device_t *device, + enum pipe_format pipe_format, + int width, int height) +{ + gallium_surface_t *surface; + struct pipe_resource template; + cairo_status_t status; + cairo_format_t format; + int stride, size; + + surface = _cairo_malloc (sizeof (gallium_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + format = _cairo_format_from_pipe_format (pipe_format); + _cairo_surface_init (&surface->drm.base, + &gallium_surface_backend, + &device->drm.base, + _cairo_content_from_format (format)); + _cairo_drm_surface_init (&surface->drm, format, width, height); + + stride = gallium_format_stride_for_width (pipe_format, width); + size = stride * height; + + surface->drm.stride = stride; + surface->drm.bo = _gallium_fake_bo_create (size, 0); + + memset(&template, 0, sizeof(template)); + template.target = PIPE_TEXTURE_2D; + template.format = pipe_format; + template.width0 = width; + template.height0 = height; + template.depth0 = 1; + template.last_level = 0; + template.bind = PIPE_BIND_RENDER_TARGET; + surface->texture = device->screen->resource_create (device->screen, + &template); + + if (unlikely (surface->texture == NULL)) { + status = _cairo_drm_surface_finish (&surface->drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + surface->pipe_format = pipe_format; + surface->texture = NULL; + + return &surface->drm.base; +} + +static cairo_surface_t * +gallium_surface_create (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height) +{ + gallium_device_t *device = (gallium_device_t *) base_dev; + cairo_surface_t *surface; + enum pipe_format pipe_format; + cairo_status_t status; + + status = cairo_device_acquire (&device->drm.base); + + if (MAX (width, height) > device->max_size) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + goto RELEASE; + } + + pipe_format = pipe_format_from_format (format); + if (! format_is_supported_destination (device, pipe_format)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + goto RELEASE; + } + + surface = gallium_surface_create_internal (device, + pipe_format, + width, height); + +RELEASE: + cairo_device_release (&device->drm.base); + + return surface; +} + +#if 0 +static cairo_surface_t * +gallium_surface_create_for_name (cairo_drm_device_t *base_dev, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + gallium_device_t *device; + gallium_surface_t *surface; + cairo_status_t status; + cairo_content_t content; + + surface = _cairo_malloc (sizeof (gallium_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_A8: + surface->pipe_format = PIPE_FORMAT_A8_UNORM; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + surface->pipe_format = PIPE_FORMAT_A8R8G8B8_UNORM; + break; + } + + status = cairo_device_acquire (&device->drm.base); + + if (MAX (width, height) > device->max_size) { + cairo_device_release (&device->drm.base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + if (! format_is_supported_destination (device, surface->pipe_format)) { + cairo_device_release (&device->drm.base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + content = _cairo_content_from_format (format); + _cairo_surface_init (&surface->drm.base, + &gallium_surface_backend, + content); + _cairo_drm_surface_init (&surface->drm, base_dev); + + surface->drm.bo = _gallium_fake_bo_create (height * stride, name); + + surface->drm.width = width; + surface->drm.height = height; + surface->drm.stride = stride; + +#if 0 + /* XXX screen->create_from_handle */ + surface->buffer = device->api->buffer_from_handle (device->api, + device->screen, + "cairo-gallium alien", + name); + if (unlikely (surface->buffer == NULL)) { + status = _cairo_drm_surface_finish (&surface->drm); + cairo_device_release (&device->drm.base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } +#endif + + surface->texture = NULL; + + surface->fallback = NULL; + + cairo_device_release (&device->drm.base); + + return &surface->drm.base; +} + +static cairo_int_status_t +gallium_surface_flink (void *abstract_surface) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + status = cairo_device_acquire (&device->drm.base); + if (! device->api->global_handle_from_buffer (device->api, + device->screen, + surface->buffer, + &surface->drm.bo->name)) + { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + cairo_device_release (&device->drm.base); + + return status; +} +#endif + +static void +gallium_device_destroy (void *abstract_device) +{ + gallium_device_t *device = abstract_device; + + device->pipe->destroy (device->pipe); + device->screen->destroy (device->screen); + device->api->destroy (device->api); + + dlclose (device->dlhandle); + free (device); +} + +cairo_drm_device_t * +_cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id) +{ + gallium_device_t *device; + cairo_status_t status; + void *handle; + const char *libdir; + char buf[4096]; + struct drm_api *(*ctor) (void); + + /* XXX need search path + probe */ + libdir = getenv ("CAIRO_GALLIUM_LIBDIR"); + if (libdir == NULL) + libdir = "/usr/lib/dri"; + buf[snprintf (buf, sizeof (buf)-1, "%s/i915_dri.so", libdir)] = '\0'; + + handle = dlopen (buf, RTLD_LAZY); + if (handle == NULL) + return NULL; + + ctor = dlsym (handle, "drm_api_create"); + if (ctor == NULL) { + dlclose (handle); + return NULL; + } + + device = _cairo_malloc (sizeof (gallium_device_t)); + if (device == NULL) { + dlclose (handle); + return _cairo_drm_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + device->dlhandle = handle; + + device->drm.surface.create = gallium_surface_create; + device->drm.surface.create_for_name = NULL; + //device->drm.surface.create_for_name = gallium_surface_create_for_name; + device->drm.surface.enable_scan_out = NULL; + //device->drm.surface.flink = gallium_surface_flink; + device->drm.surface.flink = NULL; + + device->drm.device.flush = NULL; + device->drm.device.throttle = NULL; + device->drm.device.destroy = gallium_device_destroy; + + device->drm.bo.release = _gallium_fake_bo_release; + + device->api = ctor (); + if (device->api == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + device->screen = device->api->create_screen (device->api, fd, NULL); + if (device->screen == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_API; + } + + device->max_size = 1 << device->screen->get_param (device->screen, + PIPE_CAP_MAX_TEXTURE_2D_LEVELS); + + device->pipe = device->screen->context_create (device->screen, device); + if (device->pipe == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_SCREEN; + } + + return _cairo_drm_device_init (&device->drm, + fd, dev, + 0, 0, + device->max_size); + +CLEANUP_SCREEN: + device->screen->destroy (device->screen); +CLEANUP_API: + device->api->destroy (device->api); +CLEANUP: + free (device); + dlclose (handle); + return _cairo_drm_device_create_in_error (status); +} |