Commit 0bfd2acd authored by Chris Wilson's avatar Chris Wilson 🤔
Browse files

xlib: Implement SHM fallbacks and fast upload paths


Signed-off-by: Chris Wilson's avatarChris Wilson <chris@chris-wilson.co.uk>
parent 140fafed
......@@ -63,7 +63,7 @@ AM_CONDITIONAL(CAIRO_HAS_DLSYM, test "x$have_dlsym" = "xyes")
dnl ===========================================================================
CAIRO_ENABLE_SURFACE_BACKEND(xlib, Xlib, auto, [
xlib_REQUIRES="x11"
xlib_REQUIRES="x11 xext"
PKG_CHECK_MODULES(xlib, $xlib_REQUIRES, ,
[AC_MSG_RESULT(no)
xlib_REQUIRES=""
......@@ -71,7 +71,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(xlib, Xlib, auto, [
if test "x$no_x" = xyes; then
use_xlib="no (requires X development libraries)"
else
xlib_NONPKGCONFIG_LIBS="$X_PRE_LIBS $X_LIBS -lX11 $X_EXTRA_LIBS"
xlib_NONPKGCONFIG_LIBS="$X_PRE_LIBS $X_LIBS -lX11 -lXext $X_EXTRA_LIBS"
xlib_NONPKGCONFIG_CFLAGS=$X_CFLAGS
fi])
])
......
......@@ -87,6 +87,7 @@ cairo_private = \
cairo-list-inline.h \
cairo-list-private.h \
cairo-malloc-private.h \
cairo-mempool-private.h \
cairo-mutex-impl-private.h \
cairo-mutex-list-private.h \
cairo-mutex-private.h \
......@@ -178,6 +179,7 @@ cairo_sources = \
cairo-matrix.c \
cairo-mask-compositor.c \
cairo-mesh-pattern-rasterizer.c \
cairo-mempool.c \
cairo-misc.c \
cairo-mono-scan-converter.c \
cairo-mutex.c \
......@@ -311,6 +313,7 @@ cairo_xlib_sources = \
cairo-xlib-screen.c \
cairo-xlib-source.c \
cairo-xlib-surface.c \
cairo-xlib-surface-shm.c \
cairo-xlib-visual.c \
cairo-xlib-xcb-surface.c \
$(NULL)
......
......@@ -929,10 +929,14 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
}
static cairo_status_t
_cairo_cogl_surface_flush (void *abstract_surface)
_cairo_cogl_surface_flush (void *abstract_surface,
unsigned flags)
{
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
if (flags)
return CAIRO_STATUS_SUCCESS;
_cairo_cogl_journal_flush (surface);
return CAIRO_STATUS_SUCCESS;
......@@ -1331,7 +1335,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t *reference_surface,
if (abstract_surface->device == reference_surface->base.device) {
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
_cairo_cogl_surface_flush (surface);
_cairo_cogl_surface_flush (surface, 0);
return surface->texture ? cogl_object_ref (surface->texture) : NULL;
}
......
......@@ -95,7 +95,6 @@ _cairo_damage_add_boxes(cairo_damage_t *damage,
if (damage->status)
return damage;
damage->dirty += count;
n = count;
......
......@@ -209,10 +209,14 @@ _cairo_dfb_surface_unmap_image (void *abstract_surface,
}
static cairo_status_t
_cairo_dfb_surface_flush (void *abstract_surface)
_cairo_dfb_surface_flush (void *abstract_surface,
unsigned flags)
{
cairo_dfb_surface_t *surface = abstract_surface;
if (flags)
return CAIRO_STATUS_SUCCESS;
if (surface->image.pixman_image) {
surface->dfb_surface->Unlock (surface->dfb_surface);
......
......@@ -52,7 +52,7 @@
static const cairo_surface_backend_t _cairo_gl_surface_backend;
static cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface);
_cairo_gl_surface_flush (void *abstract_surface, unsigned flags);
static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
{
......@@ -834,7 +834,7 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
status = _cairo_gl_surface_flush (&dst->base);
status = _cairo_gl_surface_flush (&dst->base, 0);
if (unlikely (status))
goto FAIL;
......@@ -1200,12 +1200,15 @@ _cairo_gl_surface_get_extents (void *abstract_surface,
}
static cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface)
_cairo_gl_surface_flush (void *abstract_surface, unsigned flags)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_status_t status;
cairo_gl_context_t *ctx;
if (flags)
return CAIRO_STATUS_SUCCESS;
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
......
......@@ -750,6 +750,16 @@ composite_tristrip (void *_dst,
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
return CAIRO_STATUS_SUCCESS;
}
#if HAS_PIXMAN_GLYPHS
static pixman_glyph_cache_t *global_glyph_cache;
static inline pixman_glyph_cache_t *
......@@ -776,16 +786,6 @@ _cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
}
static cairo_int_status_t
check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
return CAIRO_STATUS_SUCCESS;
}
#if HAS_PIXMAN_GLYPHS
static cairo_int_status_t
composite_glyphs (void *_dst,
cairo_operator_t op,
......@@ -896,6 +896,12 @@ out_unlock:
return status;
}
#else
void
_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph)
{
}
static cairo_int_status_t
composite_one_glyph (void *_dst,
cairo_operator_t op,
......
......@@ -73,7 +73,7 @@ _cairo_image_surface_is_clone (cairo_image_surface_t *image)
static inline cairo_bool_t
_cairo_surface_is_image (const cairo_surface_t *surface)
{
return surface->backend == &_cairo_image_surface_backend;
return surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE;
}
/**
......
......@@ -151,6 +151,12 @@ _cairo_image_surface_init (cairo_image_surface_t *surface,
pixman_image_t *pixman_image,
pixman_format_code_t pixman_format);
cairo_private cairo_surface_t *
_cairo_image_surface_create_similar (void *abstract_other,
cairo_content_t content,
int width,
int height);
cairo_private cairo_image_surface_t *
_cairo_image_surface_map_to_image (void *abstract_other,
const cairo_rectangle_int_t *extents);
......
......@@ -723,7 +723,7 @@ _cairo_format_bits_per_pixel (cairo_format_t format)
}
}
static cairo_surface_t *
cairo_surface_t *
_cairo_image_surface_create_similar (void *abstract_other,
cairo_content_t content,
int width,
......
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2007 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 Red Hat, Inc.
*
* Contributors(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
#ifndef CAIRO_MEMPOOL_PRIVATE_H
#define CAIRO_MEMPOOL_PRIVATE_H
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
CAIRO_BEGIN_DECLS
typedef struct _cairo_mempool cairo_mempool_t;
struct _cairo_mempool {
char *base;
struct _cairo_memblock {
int bits;
cairo_list_t link;
} *blocks;
cairo_list_t free[32];
unsigned char *map;
unsigned int num_blocks;
int min_bits; /* Minimum block size is 1 << min_bits */
int num_sizes;
int max_free_bits;
size_t free_bytes;
size_t max_bytes;
};
cairo_private cairo_status_t
_cairo_mempool_init (cairo_mempool_t *pool,
void *base,
size_t bytes,
int min_bits,
int num_sizes);
cairo_private void *
_cairo_mempool_alloc (cairo_mempool_t *pi, size_t bytes);
cairo_private void
_cairo_mempool_free (cairo_mempool_t *pi, void *storage);
cairo_private void
_cairo_mempool_fini (cairo_mempool_t *pool);
CAIRO_END_DECLS
#endif /* CAIRO_MEMPOOL_PRIVATE_H */
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2007 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 recipoolent 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.
*
* Contributors(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
#include "cairoint.h"
#include "cairo-mempool-private.h"
#include "cairo-list-inline.h"
/* a simple buddy allocator for memory pools
* XXX fragmentation? use Doug Lea's malloc?
*/
#define BITTEST(p, n) ((p)->map[(n) >> 3] & (128 >> ((n) & 7)))
#define BITSET(p, n) ((p)->map[(n) >> 3] |= (128 >> ((n) & 7)))
#define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7)))
static void
clear_bits (cairo_mempool_t *pool, size_t first, size_t last)
{
size_t i, n = last;
size_t first_full = (first + 7) & ~7;
size_t past_full = last & ~7;
size_t bytes;
if (n > first_full)
n = first_full;
for (i = first; i < n; i++)
BITCLEAR (pool, i);
if (past_full > first_full) {
bytes = past_full - first_full;
bytes = bytes >> 3;
memset (pool->map + (first_full >> 3), 0, bytes);
}
if (past_full < n)
past_full = n;
for (i = past_full; i < last; i++)
BITCLEAR (pool, i);
}
static void
free_bits (cairo_mempool_t *pool, size_t start, int bits, cairo_bool_t clear)
{
struct _cairo_memblock *block;
if (clear)
clear_bits (pool, start, start + (1 << bits));
block = pool->blocks + start;
block->bits = bits;
cairo_list_add (&block->link, &pool->free[bits]);
pool->free_bytes += 1 << (bits + pool->min_bits);
if (bits > pool->max_free_bits)
pool->max_free_bits = bits;
}
/* Add a chunk to the free list */
static void
free_blocks (cairo_mempool_t *pool,
size_t first,
size_t last,
cairo_bool_t clear)
{
size_t i, len;
int bits = 0;
for (i = first, len = 1; i < last; i += len) {
/* To avoid cost quadratic in the number of different
* blocks produced from this chunk of store, we have to
* use the size of the previous block produced from this
* chunk as the starting point to work out the size of the
* next block we can produce. If you look at the binary
* representation of the starting points of the blocks
* produced, you can see that you first of all increase the
* size of the blocks produced up to some maximum as the
* address dealt with gets offsets added on which zap out
* low order bits, then decrease as the low order bits of the
* final block produced get added in. E.g. as you go from
* 001 to 0111 you generate blocks
* of size 001 at 001 taking you to 010
* of size 010 at 010 taking you to 100
* of size 010 at 100 taking you to 110
* of size 001 at 110 taking you to 111
* So the maximum total cost of the loops below this comment
* is one trip from the lowest blocksize to the highest and
* back again.
*/
while (bits < pool->num_sizes - 1) {
size_t next_bits = bits + 1;
size_t next_len = len << 1;
if (i + next_bits > last) {
/* off end of chunk to be freed */
break;
}
if (i & (next_len - 1)) /* block would not be on boundary */
break;
bits = next_bits;
len = next_len;
}
do {
if (i + len <= last && /* off end of chunk to be freed */
(i & (len - 1)) == 0) /* block would not be on boundary */
break;
bits--; len >>=1;
} while (len);
if (len == 0)
break;
free_bits (pool, i, bits, clear);
}
}
static struct _cairo_memblock *
get_buddy (cairo_mempool_t *pool, size_t offset, int bits)
{
struct _cairo_memblock *block;
assert (offset + (1 << bits) <= pool->num_blocks);
if (BITTEST (pool, offset + (1 << bits) - 1))
return NULL; /* buddy is allocated */
block = pool->blocks + offset;
if (block->bits != bits)
return NULL; /* buddy is partially allocated */
return block;
}
static void
merge_buddies (cairo_mempool_t *pool,
struct _cairo_memblock *block,
int max_bits)
{
size_t block_offset = block - pool->blocks;
int bits = block->bits;
while (bits < max_bits - 1) {
/* while you can, merge two blocks and get a legal block size */
size_t buddy_offset = block_offset ^ (1 << bits);
block = get_buddy (pool, buddy_offset, bits);
if (block == NULL)
break;
cairo_list_del (&block->link);
/* Merged block starts at buddy */
if (buddy_offset < block_offset)
block_offset = buddy_offset;
bits++;
}
block = pool->blocks + block_offset;
block->bits = bits;
cairo_list_add (&block->link, &pool->free[bits]);
if (bits > pool->max_free_bits)
pool->max_free_bits = bits;
}
/* attempt to merge all available buddies up to a particular size */
static int
merge_bits (cairo_mempool_t *pool, int max_bits)
{
struct _cairo_memblock *block, *buddy, *next;
int bits;
for (bits = 0; bits < max_bits - 1; bits++) {
cairo_list_foreach_entry_safe (block, next,
struct _cairo_memblock,
&pool->free[bits],
link)
{
size_t buddy_offset = (block - pool->blocks) ^ (1 << bits);
buddy = get_buddy (pool, buddy_offset, bits);
if (buddy == NULL)
continue;
if (buddy == next) {
next = cairo_container_of (buddy->link.next,
struct _cairo_memblock,
link);
}
cairo_list_del (&block->link);
merge_buddies (pool, block, max_bits);
}
}
return pool->max_free_bits;
}
/* find store for 1 << bits blocks */
static void *
buddy_malloc (cairo_mempool_t *pool, int bits)
{
size_t past, offset;
struct _cairo_memblock *block;
int b;
if (bits > pool->max_free_bits && bits > merge_bits (pool, bits))
return NULL;
/* Find a list with blocks big enough on it */
block = NULL;
for (b = bits; b <= pool->max_free_bits; b++) {
if (! cairo_list_is_empty (&pool->free[b])) {
block = cairo_list_first_entry (&pool->free[b],
struct _cairo_memblock,
link);
break;
}
}
assert (block != NULL);
cairo_list_del (&block->link);
while (cairo_list_is_empty (&pool->free[pool->max_free_bits])) {
if (--pool->max_free_bits == -1)
break;
}
/* Mark end of allocated area */
offset = block - pool->blocks;
past = offset + (1 << bits);
BITSET (pool, past - 1);
block->bits = bits;
/* If we used a larger free block than we needed, free the rest */
pool->free_bytes -= 1 << (b + pool->min_bits);
free_blocks (pool, past, offset + (1 << b), 0);
return pool->base + ((block - pool->blocks) << pool->min_bits);
}
cairo_status_t
_cairo_mempool_init (cairo_mempool_t *pool,
void *base, size_t bytes,
int min_bits, int num_sizes)
{
int num_blocks;
int i;
assert ((((unsigned long) base) & ((1 << min_bits) - 1)) == 0);
assert (num_sizes < ARRAY_LENGTH (pool->free));
pool->base = base;
pool->free_bytes = 0;
pool->max_bytes = bytes;
pool->max_free_bits = -1;
num_blocks = bytes >> min_bits;
pool->blocks = calloc (num_blocks, sizeof (struct _cairo_memblock));
if (pool->blocks == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
pool->num_blocks = num_blocks;
pool->min_bits = min_bits;
pool->num_sizes = num_sizes;
for (i = 0; i < ARRAY_LENGTH (pool->free); i++)
cairo_list_init (&pool->free[i]);
pool->map = malloc ((num_blocks + 7) >> 3);
if (pool->map == NULL) {
free (pool->blocks);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
memset (pool->map, -1, (num_blocks + 7) >> 3);
clear_bits (pool, 0, num_blocks);
/* Now add all blocks to the free list */
free_blocks (pool, 0, num_blocks, 1);
return CAIRO_STATUS_SUCCESS;
}
void *
_cairo_mempool_alloc (cairo_mempool_t *pool, size_t bytes)
{
size_t size;
int bits;
size = 1 << pool->min_bits;