diff -r 000000000000 -r bde4ae8d615e os/graphics/m3g/m3gcore11/src/m3g_tcache.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/graphics/m3g/m3gcore11/src/m3g_tcache.c Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,323 @@ +/* +* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Transformation cache implementation +* +*/ + + +/*! + * \internal + * \file + * \brief Transformation cache implementation + */ + +#ifndef M3G_CORE_INCLUDE +# error included by m3g_core.c; do not compile separately. +#endif + +#include "m3g_tcache.h" + +/* NOTE size MUST be a power of two! */ +#define TCACHE_COMPOSITES 128 +#define TCACHE_PATHS 128 + +/*---------------------------------------------------------------------- + * Data structures + *--------------------------------------------------------------------*/ + +/*! + * \internal + * \brief Transformation path cache element + */ +typedef struct +{ + M3Gfloat elem[12]; /* NOTE the bottom row is always 0 0 0 1 */ + M3Guint mask; + M3Guint classified : 1; + M3Guint complete : 1; + + const Node *from, *to; +} TCachePath; + +M3G_CT_ASSERT2(sizeof(TCachePath) == 64); + +/*! + * \internal + * \brief Transformation cache data implementation + */ +struct TCacheImpl +{ + Interface *m3g; + + TCachePath paths[TCACHE_PATHS]; + Matrix composites[TCACHE_COMPOSITES]; + const Transformable *compositeObjs[TCACHE_COMPOSITES]; + + M3Gbool pathsInvalid; +}; + +/*---------------------------------------------------------------------- + * Private functions + *--------------------------------------------------------------------*/ + +static M3G_INLINE M3Gint m3gTransformableHash(const Transformable *t) +{ + M3Guint a = (M3Guint) t; + M3Guint b = (M3Guint) t; + + a += (a >> 3) + (a >> 9) + (a >> 17); + b = (b >> 16) | (b << 16); + b += (b >> 5) + (b >> 10) + (b >> 20); + return (M3Gint)(a ^ b); +} + +static M3Gint m3gPathHash(const Node *from, const Node *to) +{ + M3Guint a = (M3Guint) from; + M3Guint b = (M3Guint) to; + + a += (a >> 3) + (a >> 9) + (a >> 17); + b = (b >> 16) | (b << 16); + b += (b >> 5) + (b >> 10) + (b >> 20); + return (M3Gint)(a ^ b); +} + +static M3G_INLINE M3Gint m3gGetTransformableSlot(const TCache *tc, const Transformable *t) +{ + M3G_UNREF(tc); + return m3gTransformableHash(t) & (TCACHE_COMPOSITES - 1); +} + +static M3G_INLINE M3Gint m3gGetPathSlot(const TCache *tc, const Node *from, const Node *to) +{ + M3G_UNREF(tc); + return m3gPathHash(from, to) & (TCACHE_PATHS - 1); +} + +/*---------------------------------------------------------------------- + * Internal functions + *--------------------------------------------------------------------*/ + +/*! + * \internal + * \brief + */ +static TCache* m3gCreateTransformCache(Interface *m3g) +{ + TCache *cache = m3gAllocZ(m3g, sizeof(TCache)); + if (cache) { + cache->m3g = m3g; + cache->pathsInvalid = M3G_TRUE; + } + return cache; +} + +/*! + * \internal + * \brief + */ +static void m3gDeleteTransformCache(TCache *cache) +{ + if (cache) { + m3gFree(cache->m3g, cache); + } +} + +/*! + * \internal + * \brief + */ +static void m3gCacheComposite(TCache *cache, const Transformable *t, const Matrix *m) +{ + M3Gint idx = m3gGetTransformableSlot(cache, t); + + /* If the matrix being added already exists in the cache, the + * cache is being used sub-optimally */ + + M3G_ASSERT(cache->compositeObjs[idx] != t); + + /* Just overwrite any existing entries, but keep track of + * collisions for profiling */ + + if (cache->compositeObjs[idx]) { + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_COMPOSITE_COLLISIONS, 1); + } + else { + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_COMPOSITE_LOAD, 1); + } + cache->composites[idx] = *m; + cache->compositeObjs[idx] = t; + + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_COMPOSITE_INSERTS, 1); +} + +/*! + * \internal + * \brief + */ +static void m3gCachePath(TCache *cache, const Node *from, const Node *to, const Matrix *m) +{ + M3G_ASSERT(m3gIsWUnity(m)); + /* These two asserts are not errors in a strict sense, but imply + * sub-optimal cache use */ + M3G_ASSERT(from || to); + M3G_ASSERT(from != to); + + M3G_BEGIN_PROFILE(cache->m3g, M3G_PROFILE_TCACHE); + + /* If the cache has been invalidated, wipe it clean before + * inserting anything */ + + if (cache->pathsInvalid) { + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_PATH_FLUSHES, 1); + m3gZero(cache->paths, TCACHE_PATHS * sizeof(cache->paths[0])); + cache->pathsInvalid = M3G_FALSE; + } + + /* Hash to the cache, then just overwrite anything previously + * there */ + { + M3Gint idx = m3gGetPathSlot(cache, from, to); + TCachePath *c = &cache->paths[idx]; + + /* If this assert is hit, the path being added already exists + * in the cache; this is not an error per se, but implies + * sub-optimal cache usage */ + M3G_ASSERT(c->from != from || c->to != to); + + /* Overwrite the matrix data */ + { + const M3Gfloat *src = &m->elem[0]; + M3Gfloat *dst = &c->elem[0]; + int row, col; + for (col = 0; col < 4; ++col) { + for (row = 0; row < 3; ++row) { + *dst++ = *src++; + } + ++src; + } + c->mask = m->mask; + c->classified = m->classified; + c->complete = m->complete; + } + + /* Register collisions for bookkeeping */ + + if (c->from || c->to) { + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_PATH_COLLISIONS, 1); + } + else { + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_PATH_LOAD, 1); + } + + c->from = from; + c->to = to; + } + M3G_END_PROFILE(cache->m3g, M3G_PROFILE_TCACHE); + + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_PATH_INSERTS, 1); +} + +/*! + * \internal + * \brief + */ +static M3Gbool m3gGetCachedComposite(const TCache *cache, const Transformable *t, Matrix *m) +{ + M3Gint idx = m3gGetTransformableSlot(cache, t); + if (cache->compositeObjs[idx] == t) { + *m = cache->composites[idx]; + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_COMPOSITE_HITS, 1); + return M3G_TRUE; + } + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_COMPOSITE_MISSES, 1); + return M3G_FALSE; +} + +/*! + * \internal + * \brief + */ +static M3Gbool m3gGetCachedPath(const TCache *cache, const Node *from, const Node *to, Matrix *m) +{ + M3G_BEGIN_PROFILE(cache->m3g, M3G_PROFILE_TCACHE); + + if (!cache->pathsInvalid) { + + /* Hash to the respective cache slot */ + + M3Gint idx = m3gGetPathSlot(cache, from, to); + const TCachePath *c = &cache->paths[idx]; + + /* If it matches, copy to the output matrix */ + + if (c->from == from && c->to == to) { + const M3Gfloat *src = &c->elem[0]; + M3Gfloat *dst = &m->elem[0]; + int col; + for (col = 0; col < 4; ++col) { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = 0.0f; + } + m->elem[15] = 1.0f; + m->mask = c->mask; + m->classified = c->classified; + m->complete = c->complete; + + M3G_END_PROFILE(cache->m3g, M3G_PROFILE_TCACHE); + + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_PATH_HITS, 1); + return M3G_TRUE; + } + } + M3G_END_PROFILE(cache->m3g, M3G_PROFILE_TCACHE); + + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_PATH_MISSES, 1); + return M3G_FALSE; +} + +/*! + * \internal + * \brief + */ +static void m3gInvalidateCachedPaths(TCache *cache, const Node *n) +{ + M3G_UNREF(n); + m3gResetStat(cache->m3g, M3G_STAT_TCACHE_PATH_LOAD); + cache->pathsInvalid = M3G_TRUE; +} + +/*! + * \internal + * \brief + */ +static void m3gInvalidateCachedTransforms(TCache *cache, const Transformable *t) +{ + /* Look for a composite entry and wipe if found */ + { + M3Gint idx = m3gGetTransformableSlot(cache, t); + if (cache->compositeObjs[idx] == t) { + cache->compositeObjs[idx] = NULL; + m3gIncStat(cache->m3g, M3G_STAT_TCACHE_COMPOSITE_LOAD, -1); + } + } + + /* NOTE We just cast the pointer to a Node below -- it's only used + * for hashing, and every object will still be unique regardless + * of the class */ + + m3gInvalidateCachedPaths(cache, (const Node*) t); +}