--- /dev/null
+#include "u_surfaces.h"
+#include "util/u_hash_table.h"
+#include "util/u_inlines.h"
+#include "util/u_memory.h"
+
+/* TODO: ouch, util_hash_table should do these by default when passed a null function pointer
+ * this indirect function call is quite bad
+ */
+static unsigned
+hash(void *key)
+{
+ return (unsigned)key;
+}
+
+static int
+compare(void *key1, void *key2)
+{
+ return (unsigned)key1 - (unsigned)key2;
+}
+
+struct pipe_surface *
+util_surfaces_do_get(struct util_surfaces *us, unsigned surface_struct_size, struct pipe_screen *pscreen, struct pipe_resource *pt, unsigned face, unsigned level, unsigned zslice, unsigned flags)
+{
+ struct pipe_surface *ps;
+ void *key;
+
+ if(pt->target == PIPE_TEXTURE_3D || pt->target == PIPE_TEXTURE_CUBE)
+ { /* or 2D array */
+ if(!us->u.table)
+ us->u.table = util_hash_table_create(hash, compare);
+ key = (void *)(((zslice + face) << 8) | level);
+ /* TODO: ouch, should have a get-reference function...
+ * also, shouldn't allocate a two-pointer structure for each item... */
+ ps = util_hash_table_get(us->u.table, key);
+ }
+ else
+ {
+ if(!us->u.array)
+ us->u.array = CALLOC(pt->last_level + 1, sizeof(struct pipe_surface *));
+ ps = us->u.array[level];
+ }
+
+ if(ps)
+ {
+ p_atomic_inc(&ps->reference.count);
+ return ps;
+ }
+
+ ps = (struct pipe_surface *)CALLOC(1, surface_struct_size);
+ if(!ps)
+ return NULL;
+
+ pipe_reference_init(&ps->reference, 1);
+ pipe_surface_init(ps, pt, face, level, zslice, flags);
+ ps->offset = ~0;
+
+ if(pt->target == PIPE_TEXTURE_3D || pt->target == PIPE_TEXTURE_CUBE)
+ util_hash_table_set(us->u.table, key, ps);
+ else
+ us->u.array[level] = ps;
+
+ return ps;
+}
+
+void
+util_surfaces_do_detach(struct util_surfaces *us, struct pipe_surface *ps)
+{
+ struct pipe_resource *pt = ps->texture;
+ if(pt->target == PIPE_TEXTURE_3D || pt->target == PIPE_TEXTURE_CUBE)
+ { /* or 2D array */
+ unsigned key = ((ps->zslice + ps->face) << 8) | ps->level;
+ util_hash_table_remove(us->u.table, key);
+ }
+ else
+ us->u.array[ps->level] = 0;
+}
+
+static enum pipe_error
+util_surfaces_destroy_callback(void *key, void *value, void *data)
+{
+ void (*destroy_surface) (struct pipe_surface * ps) = data;
+ destroy_surface((struct pipe_surface *)value);
+ return PIPE_OK;
+}
+
+void
+util_surfaces_destroy(struct util_surfaces *us, struct pipe_resource *pt, void (*destroy_surface) (struct pipe_surface *))
+{
+ if(pt->target == PIPE_TEXTURE_3D || pt->target == PIPE_TEXTURE_CUBE)
+ { /* or 2D array */
+ if(us->u.table)
+ {
+ util_hash_table_foreach(us->u.table, util_surfaces_destroy_callback, destroy_surface);
+ util_hash_table_destroy(us->u.table);
+ us->u.table = NULL;
+ }
+ }
+ else
+ {
+ if(us->u.array)
+ {
+ for(unsigned i = 0; i < pt->last_level; ++i)
+ {
+ struct pipe_surface *ps = us->u.array[i];
+ if(ps)
+ destroy_surface(ps);
+ }
+ free(us->u.array);
+ us->u.array = NULL;
+ }
+ }
+}
--- /dev/null
+#ifndef U_SURFACES_H_
+#define U_SURFACES_H_
+
+#include "pipe/p_compiler.h"
+#include "pipe/p_state.h"
+#include "util/u_atomic.h"
+
+struct util_hash_table;
+
+struct util_surfaces
+{
+ union
+ {
+ struct util_hash_table *table;
+ struct pipe_surface **array;
+ } u;
+};
+
+struct pipe_surface *util_surfaces_do_get(struct util_surfaces *us, unsigned surface_struct_size, struct pipe_screen *pscreen, struct pipe_resource *pt, unsigned face, unsigned level, unsigned zslice, unsigned flags);
+
+/* fast inline path for the very common case */
+static INLINE struct pipe_surface *
+util_surfaces_get(struct util_surfaces *us, unsigned surface_struct_size, struct pipe_screen *pscreen, struct pipe_resource *pt, unsigned face, unsigned level, unsigned zslice, unsigned flags)
+{
+ if(likely(pt->target == PIPE_TEXTURE_2D && us->u.array))
+ {
+ struct pipe_surface *ps = us->u.array[level];
+ if(ps)
+ {
+ p_atomic_inc(&ps->reference.count);
+ return ps;
+ }
+ }
+
+ return util_surfaces_do_get(us, surface_struct_size, pscreen, pt, face, level, zslice, flags);
+}
+
+void util_surfaces_do_detach(struct util_surfaces *us, struct pipe_surface *ps);
+
+static INLINE void
+util_surfaces_detach(struct util_surfaces *us, struct pipe_surface *ps)
+{
+ if(likely(ps->texture->target == PIPE_TEXTURE_2D))
+ {
+ us->u.array[ps->level] = 0;
+ return;
+ }
+
+ return util_surfaces_do_detach(us, ps);
+}
+
+void util_surfaces_destroy(struct util_surfaces *us, struct pipe_resource *pt, void (*destroy_surface) (struct pipe_surface *));
+
+#endif