[gl] Switch to using the common rtree implementation.

This commit is contained in:
Chris Wilson 2009-07-27 17:44:10 +01:00 committed by Eric Anholt
parent 37bf06d66e
commit e0b7979a30
3 changed files with 89 additions and 408 deletions

View file

@ -33,7 +33,7 @@
#include "cairoint.h"
#include "cairo-gl-private.h"
#include "cairo-freelist-private.h"
#include "cairo-rtree-private.h"
#define GLYPH_CACHE_WIDTH 1024
#define GLYPH_CACHE_HEIGHT 1024
@ -41,314 +41,18 @@
#define GLYPH_CACHE_MAX_SIZE 128
typedef struct _cairo_gl_glyph_private {
rtree_node_t node;
void **owner;
cairo_rtree_node_t node;
cairo_gl_glyph_cache_t *cache;
struct { float x, y; } p1, p2;
} cairo_gl_glyph_private_t;
static void
_rtree_node_evict (rtree_t *rtree, rtree_node_t *node)
{
rtree->evict (node);
node->state = RTREE_NODE_AVAILABLE;
}
static rtree_node_t *
_rtree_node_create (rtree_t *rtree,
rtree_node_t *parent,
int x,
int y,
int width,
int height)
{
rtree_node_t *node;
/* XXX chunked freelist */
node = _cairo_freelist_alloc (&rtree->node_freelist);
if (node == NULL) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
memset (node->children, 0, sizeof (node->children));
node->parent = parent;
node->state = RTREE_NODE_AVAILABLE;
node->locked = FALSE;
node->x = x;
node->y = y;
node->width = width;
node->height = height;
return node;
}
static void
_rtree_node_destroy (rtree_t *rtree, rtree_node_t *node)
{
int i;
if (node == NULL)
return;
if (node->state == RTREE_NODE_OCCUPIED) {
_rtree_node_evict (rtree, node);
} else {
for (i = 0; i < 4 && node->children[i] != NULL; i++)
_rtree_node_destroy (rtree, node->children[i]);
}
_cairo_freelist_free (&rtree->node_freelist, node);
}
static cairo_int_status_t
_rtree_insert (rtree_t *rtree,
rtree_node_t *node,
int width,
int height,
rtree_node_t **out)
{
cairo_status_t status;
int i;
switch (node->state) {
case RTREE_NODE_DIVIDED:
for (i = 0; i < 4 && node->children[i] != NULL; i++) {
if (node->children[i]->width >= width &&
node->children[i]->height >= height)
{
status = _rtree_insert (rtree, node->children[i],
width, height,
out);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
}
default:
case RTREE_NODE_OCCUPIED:
return CAIRO_INT_STATUS_UNSUPPORTED;
case RTREE_NODE_AVAILABLE:
if (node->width - width > GLYPH_CACHE_MIN_SIZE ||
node->height - height > GLYPH_CACHE_MIN_SIZE)
{
int w, h;
w = node->width - width;
h = node->height - height;
i = 0;
node->children[i] = _rtree_node_create (rtree, node,
node->x, node->y,
width, height);
if (unlikely (node->children[i] == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
i++;
if (w > GLYPH_CACHE_MIN_SIZE) {
node->children[i] = _rtree_node_create (rtree, node,
node->x + width,
node->y,
w, height);
if (unlikely (node->children[i] == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
i++;
}
if (h > GLYPH_CACHE_MIN_SIZE) {
node->children[i] = _rtree_node_create (rtree, node,
node->x,
node->y + height,
width, h);
if (unlikely (node->children[i] == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
i++;
if (w > GLYPH_CACHE_MIN_SIZE) {
node->children[i] = _rtree_node_create (rtree, node,
node->x + width,
node->y + height,
w, h);
if (unlikely (node->children[i] == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
i++;
}
}
node->state = RTREE_NODE_DIVIDED;
node = node->children[0];
}
node->state = RTREE_NODE_OCCUPIED;
*out = node;
return CAIRO_STATUS_SUCCESS;
}
}
static cairo_int_status_t
_rtree_add_evictable_nodes (rtree_t *rtree,
rtree_node_t *node,
int width,
int height,
cairo_array_t *evictable_nodes)
{
cairo_int_status_t status;
cairo_bool_t child_added = FALSE;
int i;
switch (node->state) {
case RTREE_NODE_DIVIDED:
for (i = 0; i < 4 && node->children[i] != NULL; i++) {
if (node->children[i]->width >= width &&
node->children[i]->height >= height)
{
status = _rtree_add_evictable_nodes (rtree, node->children[i],
width, height,
evictable_nodes);
if (_cairo_status_is_error (status))
return status;
child_added |= status == CAIRO_STATUS_SUCCESS;
}
}
if (child_added)
return CAIRO_STATUS_SUCCESS;
/* fall through */
case RTREE_NODE_AVAILABLE:
case RTREE_NODE_OCCUPIED:
if (node->locked)
return CAIRO_INT_STATUS_UNSUPPORTED;
}
return _cairo_array_append (evictable_nodes, &node);
}
static uint32_t
hars_petruska_f54_1_random (void)
{
#define rol(x,k) ((x << k) | (x >> (32-k)))
static uint32_t x;
return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
#undef rol
}
static cairo_int_status_t
_rtree_evict_random (rtree_t *rtree,
rtree_node_t *root,
int width,
int height,
rtree_node_t **out)
{
cairo_array_t evictable_nodes;
cairo_status_t status;
int i;
_cairo_array_init (&evictable_nodes, sizeof (rtree_node_t *));
status = _rtree_add_evictable_nodes (rtree, root,
width, height,
&evictable_nodes);
if (status == CAIRO_STATUS_SUCCESS) {
rtree_node_t *node;
node = *(rtree_node_t **)
_cairo_array_index (&evictable_nodes,
hars_petruska_f54_1_random () % evictable_nodes.num_elements);
if (node->state == RTREE_NODE_OCCUPIED) {
_rtree_node_evict (rtree, node);
} else {
for (i = 0; i < 4 && node->children[i] != NULL; i++) {
_rtree_node_destroy (rtree, node->children[i]);
node->children[i] = NULL;
}
}
node->state = RTREE_NODE_AVAILABLE;
*out = node;
}
_cairo_array_fini (&evictable_nodes);
return status;
}
static void *
_rtree_lock (rtree_t *rtree, rtree_node_t *node)
{
void *ptr = node;
while (node != NULL && ! node->locked) {
node->locked = TRUE;
node = node->parent;
}
return ptr;
}
static void
_rtree_unlock (rtree_t *rtree, rtree_node_t *node)
{
int i;
if (! node->locked)
return;
node->locked = FALSE;
if (node->state == RTREE_NODE_DIVIDED) {
for (i = 0; i < 4 && node->children[i] != NULL; i++)
_rtree_unlock (rtree, node->children[i]);
}
}
static void
_rtree_init (rtree_t *rtree,
int width,
int height,
int node_size,
void (*evict) (void *node))
{
rtree->evict = evict;
assert (node_size >= (int) sizeof (rtree_node_t));
_cairo_freelist_init (&rtree->node_freelist, node_size);
memset (&rtree->root, 0, sizeof (rtree->root));
rtree->root.width = width;
rtree->root.height = height;
}
static void
_rtree_fini (rtree_t *rtree)
{
int i;
if (rtree->root.state == RTREE_NODE_OCCUPIED) {
_rtree_node_evict (rtree, &rtree->root);
} else {
for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
_rtree_node_destroy (rtree, rtree->root.children[i]);
}
_cairo_freelist_fini (&rtree->node_freelist);
}
static void
_glyph_evict (void *node)
{
cairo_gl_glyph_private_t *glyph_private = node;
if (glyph_private->owner != NULL)
*glyph_private->owner = NULL;
}
static cairo_status_t
_cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
cairo_gl_glyph_private_t *glyph_private;
rtree_node_t *node = NULL;
cairo_rtree_node_t *node = NULL;
cairo_status_t status;
int width, height;
GLenum internal_format, format, type;
@ -370,14 +74,15 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
height = GLYPH_CACHE_MIN_SIZE;
/* search for an available slot */
status = _rtree_insert (&cache->rtree, &cache->rtree.root,
width, height, &node);
status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
/* search for an unlocked slot */
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _rtree_evict_random (&cache->rtree, &cache->rtree.root,
width, height, &node);
if (status == CAIRO_STATUS_SUCCESS)
status = _rtree_insert (&cache->rtree, node, width, height, &node);
status = _cairo_rtree_evict_random (&cache->rtree,
width, height, &node);
if (status == CAIRO_STATUS_SUCCESS) {
status = _cairo_rtree_node_insert (&cache->rtree,
node, width, height, &node);
}
}
if (status)
return status;
@ -394,9 +99,10 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
scaled_glyph->surface_private = node;
node->owner = &scaled_glyph->surface_private;
glyph_private = (cairo_gl_glyph_private_t *) node;
glyph_private->owner = &scaled_glyph->surface_private;
glyph_private->cache = cache;
/* compute tex coords */
glyph_private->p1.x = node->x / (double) cache->width;
@ -413,59 +119,14 @@ static cairo_gl_glyph_private_t *
_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
cairo_scaled_glyph_t *scaled_glyph)
{
return _rtree_lock (&cache->rtree, scaled_glyph->surface_private);
return _cairo_rtree_pin (&cache->rtree, scaled_glyph->surface_private);
}
static cairo_status_t
cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache,
cairo_gl_context_t *ctx,
cairo_format_t format,
int width, int height)
{
cairo_content_t content;
GLenum internal_format;
assert ((width & 3) == 0);
assert ((height & 1) == 0);
cache->width = width;
cache->height = height;
switch (format) {
case CAIRO_FORMAT_A1:
case CAIRO_FORMAT_RGB24:
ASSERT_NOT_REACHED;
case CAIRO_FORMAT_ARGB32:
content = CAIRO_CONTENT_COLOR_ALPHA;
internal_format = GL_RGBA;
break;
case CAIRO_FORMAT_A8:
content = CAIRO_CONTENT_ALPHA;
internal_format = GL_ALPHA;
break;
}
glGenTextures (1, &cache->tex);
glBindTexture (GL_TEXTURE_2D, cache->tex);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D (GL_TEXTURE_2D, 0, internal_format,
width, height, 0, internal_format, GL_FLOAT, NULL);
_rtree_init (&cache->rtree,
width, height,
sizeof (cairo_gl_glyph_private_t),
_glyph_evict);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
static cairo_gl_glyph_cache_t *
cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
cairo_format_t format,
cairo_gl_glyph_cache_t **out)
cairo_format_t format)
{
cairo_gl_glyph_cache_t *cache;
cairo_status_t status;
switch (format) {
case CAIRO_FORMAT_ARGB32:
@ -481,15 +142,33 @@ cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
}
if (unlikely (cache->tex == 0)) {
status = cairo_gl_glyph_cache_init (cache, ctx, format,
GLYPH_CACHE_WIDTH,
GLYPH_CACHE_HEIGHT);
if (unlikely (status))
return status;
GLenum internal_format;
cache->width = GLYPH_CACHE_WIDTH;
cache->height = GLYPH_CACHE_HEIGHT;
switch (format) {
case CAIRO_FORMAT_A1:
case CAIRO_FORMAT_RGB24:
ASSERT_NOT_REACHED;
case CAIRO_FORMAT_ARGB32:
internal_format = GL_RGBA;
break;
case CAIRO_FORMAT_A8:
internal_format = GL_ALPHA;
break;
}
glGenTextures (1, &cache->tex);
glBindTexture (GL_TEXTURE_2D, cache->tex);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D (GL_TEXTURE_2D, 0, internal_format,
GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT, 0,
internal_format, GL_FLOAT, NULL);
}
*out = cache;
return CAIRO_STATUS_SUCCESS;
return cache;
}
static cairo_bool_t
@ -516,8 +195,13 @@ _cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
cairo_gl_glyph_private_t *glyph_private;
glyph_private = scaled_glyph->surface_private;
if (glyph_private != NULL)
glyph_private->owner = NULL;
if (glyph_private != NULL) {
glyph_private->node.owner = NULL;
if (! glyph_private->node.pinned) {
_cairo_rtree_node_collapse (&glyph_private->cache->rtree,
glyph_private->node.parent);
}
}
}
typedef struct _cairo_gl_glyphs_setup
@ -576,10 +260,8 @@ _cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
}
}
for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++) {
_rtree_unlock (&ctx->glyph_cache[i].rtree,
&ctx->glyph_cache[i].rtree.root);
}
for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++)
_cairo_rtree_unpin (&ctx->glyph_cache[i].rtree);
}
static void
@ -848,11 +530,8 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
_cairo_gl_flush_glyphs (ctx, &setup);
glActiveTexture (GL_TEXTURE1);
status = cairo_gl_context_get_glyph_cache (ctx,
scaled_glyph->surface->format,
&cache);
if (unlikely (status))
goto FINISH;
cache = cairo_gl_context_get_glyph_cache (ctx,
scaled_glyph->surface->format);
glBindTexture (GL_TEXTURE_2D, cache->tex);
@ -929,5 +608,18 @@ _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache)
glDeleteTextures (1, &cache->tex);
_rtree_fini (&cache->rtree);
_cairo_rtree_fini (&cache->rtree);
}
void
_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
{
cache->tex = 0;
_cairo_rtree_init (&cache->rtree,
GLYPH_CACHE_WIDTH,
GLYPH_CACHE_HEIGHT,
GLYPH_CACHE_MIN_SIZE,
sizeof (cairo_gl_glyph_private_t),
NULL);
}

View file

@ -40,7 +40,7 @@
#define CAIRO_GL_PRIVATE_H
#include "cairoint.h"
#include "cairo-freelist-private.h"
#include "cairo-rtree-private.h"
#include <GL/glew.h>
@ -61,27 +61,8 @@ typedef struct _cairo_gl_surface {
GLuint fb; /* GL framebuffer object wrapping our data. */
} cairo_gl_surface_t;
enum {
RTREE_NODE_AVAILABLE,
RTREE_NODE_DIVIDED,
RTREE_NODE_OCCUPIED,
};
typedef struct _rtree_node {
struct _rtree_node *children[4], *parent;
uint16_t state;
uint16_t locked;
uint16_t x, y;
uint16_t width, height;
} rtree_node_t;
typedef struct _rtree {
rtree_node_t root;
void (*evict) (void *node);
cairo_freelist_t node_freelist;
} rtree_t;
typedef struct cairo_gl_glyph_cache {
rtree_t rtree;
cairo_rtree_t rtree;
GLuint tex;
unsigned int width, height;
} cairo_gl_glyph_cache_t;
@ -133,7 +114,7 @@ typedef struct _cairo_gl_composite_setup {
cairo_gl_composite_operand_t mask;
} cairo_gl_composite_setup_t;
extern const cairo_surface_backend_t _cairo_gl_surface_backend;
cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend;
cairo_private cairo_gl_context_t *
_cairo_gl_context_create_in_error (cairo_status_t status);
@ -147,14 +128,14 @@ _cairo_gl_surface_init (cairo_gl_context_t *ctx,
cairo_content_t content,
int width, int height);
cairo_status_t
cairo_private cairo_status_t
_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
cairo_image_surface_t *src,
int src_x, int src_y,
int width, int height,
int dst_x, int dst_y);
cairo_int_status_t
cairo_private cairo_int_status_t
_cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
const cairo_pattern_t *pattern,
cairo_gl_surface_t *dst,
@ -162,35 +143,38 @@ _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
int dst_x, int dst_y,
int width, int height);
cairo_gl_context_t *
cairo_private cairo_gl_context_t *
_cairo_gl_context_acquire (cairo_gl_context_t *ctx);
void
cairo_private void
_cairo_gl_context_release (cairo_gl_context_t *ctx);
void
cairo_private void
_cairo_gl_set_destination (cairo_gl_surface_t *surface);
int
cairo_private int
_cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op);
void
cairo_private void
_cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
cairo_gl_composite_setup_t *setup);
void
cairo_private void
_cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand);
cairo_status_t
cairo_private cairo_status_t
_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
GLenum *internal_format, GLenum *format,
GLenum *type, cairo_bool_t *has_alpha);
void
cairo_private void
_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font);
cairo_int_status_t
cairo_private void
_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache);
cairo_private cairo_int_status_t
_cairo_gl_surface_show_glyphs (void *abstract_dst,
cairo_operator_t op,
const cairo_pattern_t *source,
@ -200,7 +184,7 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
cairo_clip_t *clip,
int *remaining_glyphs);
void
cairo_private void
_cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache);
#endif /* CAIRO_GL_PRIVATE_H */

View file

@ -82,6 +82,8 @@ _cairo_gl_context_create_in_error (cairo_status_t status)
cairo_status_t
_cairo_gl_context_init (cairo_gl_context_t *ctx)
{
int n;
ctx->status = CAIRO_STATUS_SUCCESS;
CAIRO_REFERENCE_COUNT_INIT (&ctx->ref_count, 1);
CAIRO_MUTEX_INIT (ctx->mutex);
@ -121,6 +123,9 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
ctx->max_texture_size = 0;
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
_cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
return CAIRO_STATUS_SUCCESS;
}