m3g/m3gcore11/src/m3g_tcache.c
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 16 Apr 2010 16:21:04 +0300
changeset 36 01a6848ebfd7
parent 0 5d03bc08d59c
permissions -rw-r--r--
Revision: 201009 Kit: 201015

/*
* 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);
}