diff --git a/cogl/Makefile.am b/cogl/Makefile.am index f6c1bb222..43500a9c4 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -356,6 +356,8 @@ cogl_sources_c = \ $(srcdir)/gl-prototypes/cogl-in-gles2-core-functions.h \ $(srcdir)/gl-prototypes/cogl-fixed-functions.h \ $(srcdir)/gl-prototypes/cogl-glsl-functions.h \ + $(srcdir)/cogl-memory-stack-private.h \ + $(srcdir)/cogl-memory-stack.c \ $(NULL) if USE_GLIB diff --git a/cogl/cogl-memory-stack-private.h b/cogl/cogl-memory-stack-private.h new file mode 100644 index 000000000..858807ab5 --- /dev/null +++ b/cogl/cogl-memory-stack-private.h @@ -0,0 +1,51 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2011 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#ifndef __COGL_MEMORY_STACK__ +#define __COGL_MEMORY_STACK__ + +G_BEGIN_DECLS + +typedef struct _CoglMemoryStack CoglMemoryStack; + +CoglMemoryStack * +_cogl_memory_stack_new (size_t initial_size_bytes); + +void * +_cogl_memory_stack_alloc (CoglMemoryStack *stack, size_t bytes); + +void +_cogl_memory_stack_rewind (CoglMemoryStack *stack); + +void +_cogl_memory_stack_free (CoglMemoryStack *stack); + +G_END_DECLS + +#endif /* __COGL_MEMORY_STACK__ */ diff --git a/cogl/cogl-memory-stack.c b/cogl/cogl-memory-stack.c new file mode 100644 index 000000000..5a939ec86 --- /dev/null +++ b/cogl/cogl-memory-stack.c @@ -0,0 +1,188 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2011 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * + * CoglMemoryStack provides a really simple, but lightning fast + * memory stack allocation strategy: + * + * - The underlying pool of memory is grow-only. + * - The pool is considered to be a stack which may be comprised + * of multiple smaller stacks. Allocation is done as follows: + * - If there's enough memory in the current sub-stack then the + * stack-pointer will be returned as the allocation and the + * stack-pointer will be incremented by the allocation size. + * - If there isn't enough memory in the current sub-stack + * then a new sub-stack is allocated twice as big as the current + * sub-stack or twice as big as the requested allocation size if + * that's bigger and the stack-pointer is set to the start of the + * new sub-stack. + * - Allocations can't be freed in a random-order, you can only + * rewind the entire stack back to the start. There is no + * the concept of stack frames to allow partial rewinds. + * + * For example; we plan to use this in our tesselator which has to + * allocate lots of small vertex, edge and face structures because + * when tesselation has been finished we just want to free the whole + * lot in one go. + * + * + * Authors: + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-memory-stack-private.h" +#include "cogl-queue.h" + +#include + +#include + +typedef struct _CoglMemorySubStack CoglMemorySubStack; + +COGL_TAILQ_HEAD (CoglMemorySubStackList, CoglMemorySubStack); + +struct _CoglMemorySubStack +{ + COGL_TAILQ_ENTRY (CoglMemorySubStack) list_node; + size_t bytes; + uint8_t *data; +}; + +typedef struct _CoglMemoryStack +{ + CoglMemorySubStackList sub_stacks; + + CoglMemorySubStack *sub_stack; + size_t sub_stack_offset; +} CoglMemoryStack; + +static CoglMemorySubStack * +_cogl_memory_sub_stack_alloc (size_t bytes) +{ + CoglMemorySubStack *sub_stack = g_slice_new (CoglMemorySubStack); + sub_stack->bytes = bytes; + sub_stack->data = g_malloc (bytes); + return sub_stack; +} + +static void +_cogl_memory_stack_add_sub_stack (CoglMemoryStack *stack, + size_t sub_stack_bytes) +{ + CoglMemorySubStack *sub_stack = + _cogl_memory_sub_stack_alloc (sub_stack_bytes); + COGL_TAILQ_INSERT_TAIL (&stack->sub_stacks, sub_stack, list_node); + stack->sub_stack = sub_stack; + stack->sub_stack_offset = 0; +} + +CoglMemoryStack * +_cogl_memory_stack_new (size_t initial_size_bytes) +{ + CoglMemoryStack *stack = g_slice_new0 (CoglMemoryStack); + + COGL_TAILQ_INIT (&stack->sub_stacks); + + _cogl_memory_stack_add_sub_stack (stack, initial_size_bytes); + + return stack; +} + +void * +_cogl_memory_stack_alloc (CoglMemoryStack *stack, size_t bytes) +{ + CoglMemorySubStack *sub_stack; + void *ret; + + sub_stack = stack->sub_stack; + if (G_LIKELY (sub_stack->bytes - stack->sub_stack_offset >= bytes)) + { + ret = sub_stack->data + stack->sub_stack_offset; + stack->sub_stack_offset += bytes; + return ret; + } + + /* If the stack has been rewound and then a large initial allocation + * is made then we may need to skip over one or more of the + * sub-stacks that are too small for the requested allocation + * size... */ + for (sub_stack = sub_stack->list_node.tqe_next; + sub_stack; + sub_stack = sub_stack->list_node.tqe_next) + { + if (sub_stack->bytes >= bytes) + { + ret = sub_stack->data; + stack->sub_stack = sub_stack; + stack->sub_stack_offset = bytes; + return ret; + } + } + + /* Finally if we couldn't find a free sub-stack with enough space + * for the requested allocation we allocate another sub-stack that's + * twice as big as the last sub-stack or twice as big as the + * requested allocation if that's bigger. + */ + + sub_stack = COGL_TAILQ_LAST (&stack->sub_stacks, CoglMemorySubStackList); + + _cogl_memory_stack_add_sub_stack (stack, MAX (sub_stack->bytes, bytes) * 2); + + sub_stack = COGL_TAILQ_LAST (&stack->sub_stacks, CoglMemorySubStackList); + + stack->sub_stack_offset += bytes; + + return sub_stack->data; +} + +void +_cogl_memory_stack_rewind (CoglMemoryStack *stack) +{ + stack->sub_stack = COGL_TAILQ_FIRST (&stack->sub_stacks); + stack->sub_stack_offset = 0; +} + +static void +_cogl_memory_sub_stack_free (CoglMemorySubStack *sub_stack) +{ + g_free (sub_stack->data); + g_slice_free (CoglMemorySubStack, sub_stack); +} + +void +_cogl_memory_stack_free (CoglMemoryStack *stack) +{ + CoglMemorySubStack *sub_stack; + + for (sub_stack = stack->sub_stacks.tqh_first; + sub_stack; + sub_stack = sub_stack->list_node.tqe_next) + { + _cogl_memory_sub_stack_free (sub_stack); + } + + g_slice_free (CoglMemoryStack, stack); +}