os/graphics/m3g/m3gcore11/src/m3g_interface.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /*
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: Interface function implementation
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Interface function implementation
    23  */
    24 
    25 #ifndef M3G_CORE_INCLUDE
    26 #   error included by m3g_core.c; do not compile separately.
    27 #endif
    28 
    29 #include "m3g_array.h"
    30 #include "m3g_gl.h"
    31 
    32 #if defined(M3G_DEBUG_OUT_OF_MEMORY)
    33 #   include <stdlib.h> /* for getenv() and atoi() */
    34 #endif
    35 
    36 /*----------------------------------------------------------------------
    37  * Private data structure(s)
    38  *--------------------------------------------------------------------*/
    39 
    40 #define MAX_LOCKHEAP_SIZE 100
    41 
    42 typedef struct StartGuardRec HeapBlock;
    43 
    44 /*!
    45  * \internal
    46  * \brief "Interface" structure
    47  *
    48  * Holds global state for an M3G instance
    49  */
    50 struct M3GInterfaceImpl
    51 {
    52     /*!
    53      * \internal
    54      * \brief Interface function pointers
    55      *
    56      * Each interface can have a separate set of functions for memory
    57      * allocation.
    58      */
    59     struct {
    60         /*@shared@*/ m3gMallocFunc        *malloc;
    61         /*@shared@*/ m3gFreeFunc          *free;
    62         /*@shared@*/ m3gObjectAllocator   *objAlloc;
    63         /*@shared@*/ m3gObjectResolver    *objResolve;
    64         /*@shared@*/ m3gObjectDeallocator *objFree;
    65         /*@shared@*/ m3gErrorHandler      *error;
    66         /*@shared@*/ m3gBeginRenderFunc   *getFrameBuffer;
    67         /*@shared@*/ m3gEndRenderFunc     *releaseFrameBuffer;
    68         /*@shared@*/ m3gReleaseTargetFunc *releaseTarget;
    69     } func;
    70     
    71     /*! \internal \brief Latest error code for this interface */
    72     M3Genum error;
    73 
    74     /*! \internal \brief Associated user context data */
    75     void *userContext;
    76     
    77 #   if defined(M3G_DEBUG)
    78     /*! \internal \brief Number of memory lock requests in effect */
    79     M3Gint lockCount;
    80     struct {
    81         const char *file;
    82         int line;
    83     } lockHeap[MAX_LOCKHEAP_SIZE];
    84 #   endif
    85 
    86 #   if !defined(M3G_NGL_CONTEXT_API)
    87     /*! \internal \brief Number of GL activation requests */
    88     M3Gint glRefCount;
    89 #   endif
    90 
    91     /* \internal \brief List of live objects */
    92     PointerArray objects;     
    93     /*! \internal \brief Number of objects registered for this interface */
    94     M3Gint objCount;
    95 
    96     /*! \internal \brief "Shutdown" flag for when we need to wait for
    97      *  objects to be deleted */
    98     M3Gbool shutdown;
    99     
   100     /* Temporary buffer */
   101     void *tempBuffer;
   102     M3Gsizei tempSize;
   103     M3Gbool tempLocked;
   104 
   105     /* Transformation cache */
   106     TCache *tcache;
   107     
   108 #   if !defined(M3G_NGL_TEXTURE_API)
   109     PointerArray deadGLObjects;
   110 #   endif
   111     
   112 #   if defined(M3G_ENABLE_PROFILING)
   113     /*!
   114      * \internal
   115      * \brief Statistics counters
   116      */
   117     M3Gint statistics[M3G_STAT_MAX];
   118     M3Gint lastPeak;
   119     M3Gint profileInterval;
   120 #   endif /*M3G_ENABLE_PROFILING*/
   121     
   122     /* Memory allocation debug counters */
   123 #   if defined(M3G_DEBUG)
   124     M3Gint mallocCount;
   125     M3Gint objAllocCount;
   126 #   endif
   127 
   128 #   if defined(M3G_DEBUG_OUT_OF_MEMORY)
   129     M3Gsize mallocBytes, mallocLimit;
   130     M3Gsize objAllocBytes, objAllocLimit;
   131     M3Gint mallocFailureCounter, mallocFailRate;
   132     M3Gint objAllocFailureCounter, objAllocFailRate;
   133 #   endif
   134     
   135 #   if defined(M3G_DEBUG_HEAP_TRACKING)
   136     HeapBlock *blockList;
   137 #   endif
   138 
   139     M3Gint maxTextureDimension;
   140     M3Gint maxViewportWidth;
   141     M3Gint maxViewportHeight;
   142     M3Gint maxViewportDim;
   143     M3Gbool supportAntialiasing;
   144     M3Gbool colorMaskWorkaround;
   145     M3Gbool twoSidedLightingWorkaround;
   146 };
   147 
   148 #if defined(M3G_DEBUG)
   149 
   150 typedef struct StartGuardRec
   151 {
   152 #   if defined(M3G_DEBUG_HEAP_TRACKING)
   153     const char *allocFile;
   154     int allocLine : 31;
   155     int isObject  : 1;
   156     HeapBlock *next, *prev;
   157 #   endif
   158     
   159     M3Guint endOffset;
   160     M3Guint magic;
   161 } StartGuard;
   162 
   163 typedef struct
   164 {
   165     M3Guint magic;
   166 } EndGuard;
   167 
   168 /* Magic number used to tag memory blocks */
   169 #define MEMORY_MAGIC 0xAE352001u
   170 
   171 /* Macros for computing the instrumentated and "user" sizes and
   172  * pointers of memory blocks */
   173 
   174 /*@notfunction@*/
   175 #   define INSTRUMENTATED_SIZE(bytes)                           \
   176         ((M3Guint) (sizeof(StartGuard) + sizeof(EndGuard)       \
   177             + M3G_ALIGN_TO(bytes, sizeof(M3Guint))))
   178 /*@notfunction@*/
   179 #   define PAYLOAD_BLOCK(physicalPtr) \
   180         (((M3Gubyte*)physicalPtr) + sizeof(StartGuard))
   181 /*@notfunction@*/
   182 #   define PHYSICAL_BLOCK(payloadPtr) \
   183         (((M3Gubyte*)payloadPtr) - sizeof(StartGuard))
   184 /*@notfunction@*/
   185 #   define PAYLOAD_SIZE(blockPtr)      \
   186         (((const StartGuard *)PHYSICAL_BLOCK(blockPtr))->endOffset      \
   187          - sizeof(StartGuard))
   188             
   189 #else /* !M3G_DEBUG */
   190 
   191 /*@notfunction@*/
   192 #   define INSTRUMENTATED_SIZE(bytes)   ((M3Guint)(bytes))
   193 /*@notfunction@*/
   194 #   define PAYLOAD_BLOCK(ptr)           (ptr)
   195 /*@notfunction@*/
   196 #   define PHYSICAL_BLOCK(ptr)          (ptr)
   197 /*@notfunction@*/
   198 #   define PAYLOAD_SIZE(blockPtr)      0
   199 
   200 #endif /* M3G_DEBUG */
   201 
   202 /*----------------------------------------------------------------------
   203  * Static data for managing NGL contexts
   204  *--------------------------------------------------------------------*/
   205 
   206 #if defined(M3G_NGL_CONTEXT_API)
   207 static M3Gint m3gs_glRefCount = 0;
   208 
   209 #if defined(M3G_BUILD_ISA)
   210 #   define M3G_SYSTEM_ALLOC     os_block_alloc
   211 #   define M3G_SYSTEM_DEALLOC   os_block_dealloc
   212 #else
   213 #   define M3G_SYSTEM_ALLOC     malloc
   214 #   define M3G_SYSTEM_DEALLOC   free
   215 #endif
   216 
   217 static void *m3gs_nglMem;
   218 static void *m3gs_nglTexMgr;
   219 #endif
   220 
   221 #if defined(M3G_NGL_TEXTURE_API)
   222 M3Gubyte *m3gs_nglTexUnit[M3G_NUM_TEXTURE_UNITS];
   223 #endif
   224 
   225 /*----------------------------------------------------------------------
   226  * Private functions
   227  *--------------------------------------------------------------------*/
   228 
   229 /*@access M3GMemObject@*/
   230 static /*@dependent@*/ void *defaultResolver(M3GMemObject handle)
   231 {
   232     return (void *)handle;
   233 }
   234 
   235 /*--------------------------------------------------------------------*/
   236 
   237 #if defined(M3G_DEBUG)
   238 
   239 static void validateBlock(/*@reldef@*//*@temp@*//*@null@*/ const void *ptr) 
   240 {
   241     if (ptr != NULL && M3G_IS_ALIGNED(ptr)) {
   242         const StartGuard *start = (const StartGuard *) PHYSICAL_BLOCK(ptr);
   243         if (start->magic == MEMORY_MAGIC) {
   244             const EndGuard *end = (const EndGuard *)
   245                 (((M3Gubyte *)start) + start->endOffset);
   246             if (end->magic == MEMORY_MAGIC) {
   247                 return; /* all clear */
   248             }
   249         }
   250         M3G_LOG1(M3G_LOG_FATAL_ERRORS,
   251                  "Corrupted memory block 0x%08X!\n", (unsigned) ptr);
   252         M3G_ASSERT(M3G_FALSE);
   253     }
   254     else {
   255         M3G_LOG1(M3G_LOG_FATAL_ERRORS,
   256                  "Invalid pointer to 0x%08X!\n", (unsigned) ptr);
   257         M3G_ASSERT(M3G_FALSE);
   258     }
   259 }
   260 
   261 #if defined(M3G_DEBUG_HEAP_TRACKING)
   262 static void instrumentateBlock(/*@reldef@*//*@temp@*/ void *ptr,
   263                                M3Gsize bytes,
   264                                const char *file,
   265                                int line)
   266 #else
   267 static void instrumentateBlock(/*@reldef@*//*@temp@*/ void *ptr, M3Gsize bytes)
   268 #endif
   269 {
   270     M3Guint offset = M3G_ALIGN_TO(bytes, M3G_ALIGNMENT) + sizeof(StartGuard);
   271     StartGuard *start;
   272     EndGuard *end;
   273     
   274     M3G_ASSERT_PTR(ptr);
   275     M3G_ASSERT_ALIGNMENT(offset);
   276 
   277     /* Insert start and end guard blocks for holding debugging data as
   278      * well as guarding against (short) over- and underruns */
   279     
   280     start = (StartGuard *) PHYSICAL_BLOCK(ptr);
   281     end   = (EndGuard *) (((M3Gubyte *)start) + offset);
   282     
   283     start->endOffset = offset;
   284     start->magic = MEMORY_MAGIC;
   285     end->magic = MEMORY_MAGIC;
   286 
   287 #   if defined(M3G_DEBUG_HEAP_TRACKING)
   288     start->isObject = 0;
   289 #   endif
   290     
   291     /* Fill with garbage that will show up on the debugger if used
   292      * before initialized */
   293     {
   294         M3Guint *p = (M3Guint *) ptr;
   295         M3Guint count = bytes >> 2;
   296         
   297         while (count--) {
   298             *p++ = 0xBAADF00Du;
   299         }
   300     }
   301 
   302 #   if defined(M3G_DEBUG_HEAP_TRACKING)
   303     /* Register allocation location */
   304     start->allocFile = file;
   305     start->allocLine = line;
   306 #   endif
   307     
   308     validateBlock(ptr);
   309 }
   310 
   311 static void destroyBlock(/*@reldef@*//*@temp@*//*@null@*/ void *ptr)
   312 {
   313     if (ptr != NULL) {
   314         validateBlock(ptr);
   315         {
   316             /* Fill with garbage that will show up on the debugger if
   317              * used after deallocation */
   318             
   319             StartGuard *start = (StartGuard *) PHYSICAL_BLOCK(ptr);
   320             M3Guint *p = (M3Guint *) start;
   321             M3Guint count = (start->endOffset + sizeof(EndGuard)) >> 2;
   322 
   323             while (count--) {
   324                 *p++ = 0xDEADBEEFu;
   325             }
   326         }
   327     }
   328 }
   329 
   330 #if defined(M3G_DEBUG_HEAP_TRACKING)
   331 static void insertBlock(void *ptr, HeapBlock **blockListHead)
   332 {
   333     HeapBlock *head  = *blockListHead;
   334     HeapBlock *block = (HeapBlock *) PHYSICAL_BLOCK(ptr);
   335     
   336     M3G_ASSERT_PTR(block);
   337     
   338     *blockListHead = block;
   339     block->prev = NULL;
   340     block->next = head;
   341 
   342     if (head != NULL) {
   343         head->prev = block;
   344     }
   345 }
   346 #endif
   347 
   348 #if defined(M3G_DEBUG_HEAP_TRACKING)
   349 static void removeBlock(void *ptr, HeapBlock **blockListHead)
   350 {
   351     if (ptr != NULL) {
   352         HeapBlock *head  = *blockListHead;
   353         HeapBlock *block = (HeapBlock *) PHYSICAL_BLOCK(ptr);
   354 
   355         validateBlock(ptr);
   356         
   357         M3G_ASSERT_PTR(head);
   358         M3G_ASSERT_PTR(block);
   359         
   360         if (block->prev) {
   361             block->prev->next = block->next;
   362         }
   363         if (block->next) {
   364             block->next->prev = block->prev;
   365         }
   366         if (block == head) {
   367             *blockListHead = block->next;
   368         }
   369         
   370         block->next = NULL;
   371         block->prev = NULL;
   372     }
   373 }
   374 #endif
   375 
   376 #if defined(M3G_DEBUG_HEAP_TRACKING) && defined(M3G_LOGLEVEL)
   377 static void dumpBlocks(const HeapBlock *head) 
   378 {
   379     while (head) {
   380         M3G_LOG4(M3G_LOG_FATAL_ERRORS,
   381                  "0x%08X: %s:%d, %d bytes\n",
   382                  (unsigned) PAYLOAD_BLOCK(head),
   383                  head->allocFile,
   384                  head->allocLine,
   385                  (M3Gsizei) PAYLOAD_SIZE(PAYLOAD_BLOCK(head)));
   386         head = head->next;
   387     }
   388 }
   389 #endif
   390 
   391 #undef MEMORY_MAGIC
   392 
   393 #else /* !M3G_DEBUG */
   394 #   define instrumentateBlock(ptr, bytes)
   395 #   define validateBlock(ptr)
   396 #   define destroyBlock(ptr)
   397 #endif /* M3G_DEBUG */
   398 
   399 /*--------------------------------------------------------------------*/
   400 
   401 /*----------------------------------------------------------------------
   402  * Internal functions
   403  *--------------------------------------------------------------------*/
   404 
   405 #if defined(M3G_DEBUG)
   406 
   407 /*!
   408  * \internal
   409  * \brief Locks all memory allocation for an interface
   410  *
   411  * This is used to ensure that memory compacting does not affect the
   412  * addresses of our "memory objects" while they're in use. A count of
   413  * locks is maintained; the same interface may be locked several
   414  * times, and each lock must be separately released with a call to
   415  * m3gUnlockMemory.
   416  *
   417  * Memory is also automatically locked while a handle is mapped to a
   418  * pointer; see m3gMapObject.
   419  *
   420  */
   421 static void m3gDebugLockMemory(Interface *m3g, const char *file, int line)
   422 {
   423     M3G_ASSERT(m3gInRange(m3g->lockCount, 0, 0x7FFFFFFE));
   424     M3G_ASSERT(m3g->lockCount < MAX_LOCKHEAP_SIZE);
   425     
   426     m3g->lockHeap[m3g->lockCount].file = file;
   427     m3g->lockHeap[m3g->lockCount].line = line;
   428     m3g->lockCount++;
   429     
   430     m3gIncStat(m3g, M3G_STAT_MEMORY_LOCKS, 1);
   431 }
   432 
   433 /*!
   434  * \internal
   435  * \brief Releases a memory allocation lock on an interface
   436  */
   437 static void m3gUnlockMemory(Interface *m3g)
   438 {
   439     M3G_ASSERT(m3g->lockCount > 0);
   440     m3g->lockHeap[m3g->lockCount].file = "";
   441     m3g->lockHeap[m3g->lockCount].line = 0;
   442     m3g->lockCount--;
   443 }
   444 
   445 /*!
   446  * \internal
   447  * \brief Checks whether memory is currently locked (debug only)
   448  */
   449 static M3Gbool m3gMemoryLocked(Interface *m3g)
   450 {
   451     M3G_ASSERT(m3g->lockCount >= 0);
   452     
   453     if (m3g->lockCount > 0) {
   454 #       if defined(M3G_LOGLEVEL)
   455         int i;
   456         M3G_LOG1(M3G_LOG_FATAL_ERRORS,
   457                  "%d memory lock(s) in effect:\n", m3g->lockCount);
   458         for (i = m3g->lockCount - 1; i >= 0; --i) {
   459             M3G_LOG2(M3G_LOG_FATAL_ERRORS, "%s, line %d\n",
   460                      m3g->lockHeap[i].file, m3g->lockHeap[i].line);
   461         }
   462 #       endif
   463         return M3G_TRUE;
   464     }
   465     
   466     return M3G_FALSE;
   467 }
   468 #endif
   469 
   470 /*!
   471  * \internal
   472  * \brief Allocates a block of memory from a regular C-style heap
   473  *
   474  */
   475 #if defined(M3G_DEBUG)
   476 static void *m3gDebugAlloc(
   477     Interface *m3g, M3Gsize bytes, const char *file, int line)
   478 #else
   479 static void *m3gAlloc(Interface *m3g, M3Gsize bytes)
   480 #endif    
   481 {
   482     void *ptr;
   483     M3G_VALIDATE_INTERFACE(m3g);
   484     M3G_ASSERT_NO_LOCK(m3g);
   485 
   486     /* Simulate memory allocation failures if enabled */
   487     
   488 #   if defined(M3G_DEBUG_OUT_OF_MEMORY)
   489     if (m3g->mallocFailRate > 0 &&
   490             m3g->mallocFailureCounter++ >= m3g->mallocFailRate) {
   491         m3g->mallocFailureCounter = 0;
   492         goto AllocFailed;
   493     }
   494     if (m3g->mallocLimit > 0 &&
   495             m3g->mallocBytes + bytes > m3g->mallocLimit) {
   496         goto AllocFailed;
   497     }
   498 #   endif
   499         
   500     /* First just try to allocate more memory; if that fails, garbage
   501      * collect and try again before returning with an error */
   502 
   503     ptr = (*m3g->func.malloc)(INSTRUMENTATED_SIZE(bytes));
   504     if (ptr == NULL) {
   505         M3G_LOG(M3G_LOG_WARNINGS|M3G_LOG_MEMORY_ALL,
   506                 "Warning: heap alloc failed\n");
   507         m3gGarbageCollectAll(m3g);
   508         ptr = (*m3g->func.malloc)(INSTRUMENTATED_SIZE(bytes));
   509     }
   510     if (ptr == NULL) {
   511         goto AllocFailed;
   512     }
   513 
   514     /* Succesfully allocated some, so update statistics */
   515     
   516 #   if defined(M3G_DEBUG)
   517     m3g->mallocCount++;
   518 #   endif
   519 #   if defined(M3G_DEBUG_OUT_OF_MEMORY)
   520     m3g->mallocBytes += bytes;
   521 #   endif
   522 
   523     /* Add instrumentation to the block */
   524     
   525     M3G_ASSERT_ALIGNMENT(ptr);
   526     ptr = PAYLOAD_BLOCK(ptr);
   527 #   if defined(M3G_DEBUG_HEAP_TRACKING)
   528     insertBlock(ptr, &m3g->blockList);
   529     instrumentateBlock(ptr, bytes, file, line);
   530 #   else
   531     instrumentateBlock(ptr, bytes);
   532 #   endif
   533     
   534     m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCS, 1);
   535 #   if defined(M3G_DEBUG) && defined(M3G_ENABLE_PROFILING)
   536     {
   537         M3Gint size = PAYLOAD_SIZE(ptr);
   538         m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, size);
   539         m3gIncStat(m3g, M3G_STAT_MEMORY_MALLOC_BYTES, size);
   540     }
   541 #   else
   542     m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, bytes);
   543     m3gIncStat(m3g, M3G_STAT_MEMORY_MALLOC_BYTES, bytes);
   544 #   endif
   545     
   546 #   if defined(M3G_DEBUG)
   547     M3G_LOG4(M3G_LOG_MEMORY_BLOCKS,
   548              "Alloc 0x%08X, %d bytes (%s, line %d)\n",
   549              (unsigned) ptr, bytes, file, line);
   550 #   else
   551     M3G_LOG2(M3G_LOG_MEMORY_BLOCKS, "Alloc 0x%08X, %d bytes\n",
   552              (unsigned) ptr, bytes);
   553 #   endif
   554     
   555     m3gUpdateMemoryPeakCounter(m3g);
   556     
   557     return ptr;
   558 
   559 AllocFailed:
   560     m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
   561     return NULL;
   562 }
   563     
   564 /*!
   565  * \internal
   566  * \brief Same as m3gAlloc, but also zero-initializes the allocated
   567  * block
   568  */
   569 #if !defined(M3G_DEBUG)
   570 static void *m3gAllocZ(Interface *m3g, M3Gsize bytes)
   571 {
   572     void *ptr = m3gAlloc(m3g, bytes);
   573     if (ptr != NULL) {
   574         m3gZero(ptr, bytes);
   575     }
   576     return ptr;
   577 }
   578 #else
   579 static void *m3gDebugAllocZ(
   580     Interface *m3g, M3Gsize bytes, const char *file, int line)
   581 {
   582     void *ptr = m3gDebugAlloc(m3g, bytes, file, line);
   583     if (ptr != NULL) {
   584         m3gZero(ptr, bytes);
   585     }
   586     return ptr;
   587 }
   588 #endif /* M3G_DEBUG_HEAP_TRACKING */
   589 
   590 /*!
   591  * \internal
   592  * \brief Frees a block of memory allocated using m3gAlloc
   593  */
   594 #if defined(M3G_DEBUG)
   595 static void m3gDebugFree(Interface *m3g, void *ptr, const char *file, int line)
   596 #else
   597 static void m3gFree(Interface *m3g, void *ptr)
   598 #endif
   599 {
   600     M3G_VALIDATE_INTERFACE(m3g);
   601     M3G_ASSERT_ALIGNMENT(ptr);
   602     M3G_ASSERT_NO_LOCK(m3g);
   603     
   604     if (ptr != NULL) {
   605         M3G_VALIDATE_MEMBLOCK(ptr);
   606         M3G_ASSERT(!m3gIsObject(ptr));
   607         
   608 #       if defined(M3G_DEBUG)
   609         m3g->mallocCount--;
   610         M3G_ASSERT(m3g->mallocCount >= 0);
   611 #       endif
   612 
   613 #if defined(M3G_ENABLE_PROFILING) || defined(M3G_DEBUG_OUT_OF_MEMORY)
   614         {
   615             M3Gint size = PAYLOAD_SIZE(ptr);        
   616 #           if defined(M3G_ENABLE_PROFILING)
   617             m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, -size);
   618             m3gIncStat(m3g, M3G_STAT_MEMORY_MALLOC_BYTES, -size);
   619 #           endif
   620 #           if defined(M3G_DEBUG_OUT_OF_MEMORY)
   621             m3g->mallocBytes -= size;
   622 #           endif
   623         }
   624 #endif
   625 
   626 #       if defined(M3G_DEBUG_HEAP_TRACKING)
   627         removeBlock(ptr, &m3g->blockList);
   628 #       endif
   629     
   630     
   631 #       if defined(M3G_DEBUG)
   632 #           if defined(M3G_DEBUG_HEAP_TRACKING)
   633             M3G_LOG4(M3G_LOG_MEMORY_BLOCKS,
   634                      "Free 0x%08X, %d bytes (%s, line %d)\n",
   635                      (unsigned) ptr, PAYLOAD_SIZE(ptr), file, line);
   636 #           else
   637             M3G_LOG3(M3G_LOG_MEMORY_BLOCKS,
   638                      "Free 0x%08X (%s, line %d)\n",
   639                      (unsigned) ptr, file, line);
   640 #           endif
   641 #       else
   642         M3G_LOG1(M3G_LOG_MEMORY_BLOCKS, "Free 0x%08X\n", (unsigned) ptr);
   643 #       endif
   644     
   645         destroyBlock(ptr);
   646         (*m3g->func.free)(PHYSICAL_BLOCK(ptr));
   647     }
   648 }
   649 
   650 #if 0
   651 
   652 /*!
   653  * \brief Recycles a block of memory for later use
   654  *
   655  * Same as free, but instead of always returning the memory to the OS,
   656  * may place it on a list of free blocks for reuse.  Blocks in the
   657  * free list can be quickly reused by m3gAlloc.
   658  * 
   659  * \param m3g    Interface instance
   660  * \param ptr    pointer to block to recycle
   661  * \param bytes  size of the block, in bytes
   662  */
   663 static void m3gRecycle(Interface *m3g, void *ptr, M3Gsize bytes)
   664 {
   665     M3G_UNREF(bytes);
   666     m3gFree(m3g, ptr);
   667 }
   668 
   669 #endif
   670 
   671 #if defined(M3G_DEBUG)
   672 /*!
   673  * \internal
   674  * \brief Checks the integrity of a memory block
   675  */
   676 static void m3gValidateMemory(const void *ptr)
   677 {
   678     validateBlock(ptr);
   679 }
   680 #endif /* M3G_DEBUG */
   681 
   682 #if defined(M3G_DEBUG)
   683 /*!
   684  * \internal
   685  * \brief Checks the integrity of an Interface object
   686  */
   687 static void m3gValidateInterface(const Interface *m3g)
   688 {
   689     M3G_VALIDATE_MEMBLOCK(m3g);
   690 }
   691 #endif /* M3G_DEBUG */
   692 
   693 #if defined (M3G_DEBUG_HEAP_TRACKING)
   694 /*!
   695  * \internal
   696  * \brief Marks a block as a live object block
   697  *
   698  * Live objects can never have their memory freed.
   699  */
   700 static void m3gMarkObject(void *ptr)
   701 {
   702     StartGuard *start;
   703     validateBlock(ptr);
   704     
   705     start = (StartGuard *) PHYSICAL_BLOCK(ptr);
   706     start->isObject = 1;
   707 }
   708 #endif
   709 
   710 #if defined (M3G_DEBUG_HEAP_TRACKING)
   711 /*!
   712  * \internal
   713  * \brief Checks if a block is an object block
   714  */
   715 static M3Gbool m3gIsObject(const void *ptr)
   716 {
   717     const StartGuard *start;
   718     validateBlock(ptr);
   719     
   720     start = (StartGuard *) PHYSICAL_BLOCK(ptr);
   721     return (start->isObject != 0);
   722 }
   723 #endif
   724 
   725 #if defined (M3G_DEBUG_HEAP_TRACKING)
   726 /*!
   727  * \internal
   728  * \brief Unmarks an object block
   729  */
   730 static void m3gUnmarkObject(void *ptr)
   731 {
   732     StartGuard *start;
   733     validateBlock(ptr);
   734     
   735     start = (StartGuard *) PHYSICAL_BLOCK(ptr);
   736     start->isObject = 0;
   737 }
   738 #endif
   739 
   740 
   741 /*!
   742  * \internal
   743  * \brief Allocates a "memory object" from a potentially compacting heap
   744  *
   745  * A block of memory of \c bytes is allocated, but its address is
   746  * available only via the m3gMapObject function.
   747  * 
   748  * \return a handle used to refer to the allocated block during the
   749  * rest of its lifetime
   750  */
   751 /*@access M3GMemObject@*/
   752 #if defined(M3G_DEBUG)
   753 static M3GMemObject m3gDebugAllocObject(
   754     Interface *m3g, M3Gsize bytes, const char *file, int line)
   755 #else
   756 static M3GMemObject m3gAllocObject(Interface *m3g, M3Gsize bytes)
   757 #endif
   758 {
   759     M3GMemObject handle;
   760     M3G_VALIDATE_INTERFACE(m3g);
   761     M3G_ASSERT_NO_LOCK(m3g);
   762 
   763     /* Simulate memory allocation failures if enabled */
   764     
   765 #   if defined(M3G_DEBUG_OUT_OF_MEMORY)
   766     if (m3g->objAllocFailRate > 0 &&
   767             m3g->objAllocFailureCounter++ >= m3g->objAllocFailRate) {
   768         m3g->objAllocFailureCounter = 0;
   769         goto AllocFailed;
   770     }
   771     if (m3g->objAllocLimit > 0 &&
   772             m3g->objAllocBytes + bytes > m3g->objAllocLimit) {
   773         goto AllocFailed;
   774     }
   775 #   endif
   776         
   777     /* Similarly to Alloc, garbage collect and try again if the
   778      * first allocation fails */
   779         
   780     handle = (*m3g->func.objAlloc)(INSTRUMENTATED_SIZE(bytes));
   781     if (!handle) {
   782         M3G_LOG(M3G_LOG_WARNINGS|M3G_LOG_MEMORY_ALL,
   783                 "Warning: object alloc failed\n");
   784         m3gGarbageCollectAll(m3g);
   785         handle = (*m3g->func.objAlloc)(INSTRUMENTATED_SIZE(bytes));
   786     }
   787     if (!handle) {
   788         goto AllocFailed;
   789     }
   790 
   791     /* Succesfully allocated, update statistics */
   792         
   793     m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCS, 1);
   794 #   if defined(M3G_DEBUG)
   795     m3g->objAllocCount++;
   796     if (handle != 0) {
   797         void *ptr = PAYLOAD_BLOCK((*m3g->func.objResolve)(handle));
   798 #       if defined(M3G_DEBUG_HEAP_TRACKING)
   799         instrumentateBlock(ptr, bytes, file, line);
   800 #       else
   801         instrumentateBlock(ptr, bytes);
   802 #       endif
   803 #       if defined(M3G_ENABLE_PROFILING)
   804         {
   805             M3Gint size = PAYLOAD_SIZE(ptr);
   806             m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, size);
   807             m3gIncStat(m3g, M3G_STAT_MEMORY_OBJECT_BYTES, size);
   808         }
   809 #       endif
   810     }
   811 #   else
   812     m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, bytes);
   813     m3gIncStat(m3g, M3G_STAT_MEMORY_OBJECT_BYTES, bytes);
   814 #   endif
   815 
   816 #   if defined(M3G_DEBUG)
   817     M3G_LOG4(M3G_LOG_MEMORY_BLOCKS,
   818              "ObjAlloc 0x%08X, %d bytes (%s, line %d)\n",
   819              (unsigned) handle, bytes, file, line);
   820 #   else
   821     M3G_LOG2(M3G_LOG_MEMORY_BLOCKS, "ObjAlloc 0x%08X, %d bytes\n",
   822              (unsigned) handle, bytes);
   823 #   endif
   824     
   825     m3gUpdateMemoryPeakCounter(m3g);
   826     
   827 #   if defined(M3G_DEBUG_OUT_OF_MEMORY)
   828     m3g->objAllocBytes += bytes;
   829 #   endif
   830         
   831     return handle;
   832 
   833 AllocFailed:
   834     m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
   835     return 0;
   836 }
   837 
   838 /*!
   839  * \internal
   840  * \brief Frees a memory object allocated with m3gAllocObject
   841  */
   842 /*@access M3GMemObject@*/
   843 #if defined(M3G_DEBUG)
   844 static void m3gDebugFreeObject(Interface *m3g, M3GMemObject handle, const char *file, int line)
   845 #else
   846 static void m3gFreeObject(Interface *m3g, M3GMemObject handle)
   847 #endif
   848 {
   849     M3G_VALIDATE_INTERFACE(m3g);
   850     M3G_ASSERT_NO_LOCK(m3g);
   851 
   852     /* Debugging code */
   853     
   854 #   if defined(M3G_DEBUG)
   855     if (handle != 0) {
   856         void *ptr = m3gMapObject(m3g, handle);
   857         M3G_VALIDATE_MEMBLOCK(ptr);
   858         M3G_ASSERT(!m3gIsObject(ptr));
   859         
   860         if (ptr != NULL) {
   861 #           if defined(M3G_ENABLE_PROFILING) || defined(M3G_DEBUG_OUT_OF_MEMORY)
   862             M3Gint size = PAYLOAD_SIZE(ptr);
   863 #           endif
   864 #           if defined(M3G_ENABLE_PROFILING)
   865             m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, -size);
   866             m3gIncStat(m3g, M3G_STAT_MEMORY_OBJECT_BYTES, -size);
   867 #           endif
   868 #           if defined(M3G_DEBUG_OUT_OF_MEMORY)
   869             m3g->objAllocBytes -= size;
   870 #           endif
   871 #           if defined(M3G_DEBUG_HEAP_TRACKING)
   872             M3G_LOG4(M3G_LOG_MEMORY_BLOCKS,
   873                      "ObjFree 0x%08X, %d bytes (%s, line %d)\n",
   874                      (unsigned) handle, PAYLOAD_SIZE(ptr), file, line);
   875 #           else
   876             M3G_LOG3(M3G_LOG_MEMORY_BLOCKS,
   877                      "ObjFree 0x%08X (%s, line %d)\n",
   878                      (unsigned) handle, file, line);
   879 #           endif
   880         }
   881         m3gUnmapObject(m3g, handle);
   882         
   883         destroyBlock(ptr);
   884         
   885         m3g->objAllocCount--;
   886         M3G_ASSERT(m3g->objAllocCount >= 0);
   887     }
   888 #   else /* !M3G_DEBUG*/
   889         M3G_LOG1(M3G_LOG_MEMORY_BLOCKS, "ObjFree 0x%08X\n", (unsigned) handle);
   890 #   endif
   891 
   892     /* Actual operation */
   893     
   894     (*m3g->func.objFree)(handle);
   895 }
   896 
   897 /*!
   898  * \internal
   899  * \brief Allocates a temporary data buffer
   900  *
   901  * The temporary buffer is intended for situations where more
   902  * temporary data is required than can be allocated on the stack.
   903  * Only one temporary buffer exists for each M3G interface, and it
   904  * must be freed via \c m3gFreeTemp before reallocating.
   905  *
   906  * \param m3g   interface object
   907  * \param bytes size of the temp buffer requested, in bytes
   908  */
   909 static void *m3gAllocTemp(Interface *m3g, M3Gsizei bytes)
   910 {
   911     M3G_VALIDATE_INTERFACE(m3g);
   912     M3G_ASSERT_NO_LOCK(m3g);
   913     M3G_ASSERT(!m3g->tempLocked);
   914 
   915     if (m3g->tempSize < bytes) {
   916         m3gFree(m3g, m3g->tempBuffer);
   917         m3g->tempBuffer = NULL;
   918     }
   919 
   920     if (m3g->tempBuffer == NULL) {
   921         m3g->tempBuffer = m3gAlloc(m3g, bytes);
   922         if (m3g->tempBuffer == NULL) {
   923             return NULL; /* automatic out of memory */
   924         }
   925         m3g->tempSize = bytes;
   926     }
   927 
   928     m3g->tempLocked = M3G_TRUE;
   929     return m3g->tempBuffer;
   930 }
   931 
   932 /*!
   933  * \internal
   934  * \brief Release the currently allocated temporary data buffer
   935  */
   936 static void m3gFreeTemp(Interface *m3g)
   937 {
   938     M3G_VALIDATE_INTERFACE(m3g);
   939     M3G_ASSERT_NO_LOCK(m3g);
   940     M3G_ASSERT(m3g->tempLocked);
   941     
   942     m3g->tempLocked = M3G_FALSE;
   943 }
   944 
   945 /*!
   946  * \internal
   947  * \brief
   948  */
   949 static void m3gAddChildObject(Interface *m3g, Object *obj)
   950 {
   951     M3G_ASSERT(!m3g->shutdown);
   952     M3G_ASSERT(m3gInRange(m3g->objCount, 0, 0x7FFFFFFF));
   953     ++m3g->objCount;
   954 
   955     /* Add the object to the list of live objects */
   956     m3gArrayAppend(&m3g->objects, obj, m3g);
   957 }
   958 
   959 /*!
   960  * \internal
   961  * \brief
   962  */
   963 static void m3gDelChildObject(Interface *m3g, Object *obj)
   964 {
   965     M3G_ASSERT(m3g->objCount > 0);
   966 
   967     /* Remove the object from the list of live objects */
   968     m3gArrayDelete(&m3g->objects, m3gArrayFind(&m3g->objects, obj));
   969     if (--m3g->objCount == 0 && m3g->shutdown) {
   970         m3gDeleteInterface(m3g);
   971     }
   972 }
   973 
   974 #if !defined(M3G_NGL_TEXTURE_API)
   975 /*!
   976  * \internal
   977  * \brief Get a list of live objects with matching class ID
   978  */
   979 static void m3gGetObjectsWithClassID(Interface *m3g, M3GClass classID, PointerArray* objects)
   980 {
   981     M3Gsizei i = m3gArraySize(&m3g->objects);
   982     while (i > 0) {
   983         M3GObject obj = (M3GObject)m3gGetArrayElement(&m3g->objects, --i);
   984         if (m3gGetClass(obj) == classID)
   985             m3gArrayAppend(objects, obj, m3g);
   986     }
   987 }
   988 /*!
   989  * \internal
   990  * \brief Queue OpenGL texture objects for deletion
   991  *
   992  * The objects will be deleted when a GL context is next made current.
   993  *
   994  */
   995 static void m3gDeleteGLTextures(Interface *m3g, M3Gsizei n, M3Guint *t)
   996 {
   997     PointerArray *objs = &m3g->deadGLObjects;
   998     while (n--) {
   999         if (m3gArrayAppend(objs, (void*) t[n], m3g) < 0) {
  1000             return; 
  1001         }
  1002     }
  1003 }
  1004 
  1005 /*!
  1006  * \internal
  1007  * \brief Delete queued OpenGL objects
  1008  *
  1009  * This function should be called at suitable points during execution
  1010  * to delete dead GL texture objects.  A GL context must be current.
  1011  */
  1012 static void m3gCollectGLObjects(Interface *m3g)
  1013 {
  1014     PointerArray *objs = &m3g->deadGLObjects;
  1015     M3Gsizei n = m3gArraySize(objs);
  1016     M3Gint i;
  1017     for (i = 0; i < n; ++i) {
  1018         GLuint t = (GLuint) m3gGetArrayElement(objs, i);
  1019         glDeleteTextures(1, &t);
  1020         M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL texture object 0x%08X\n",
  1021                  (unsigned) t);
  1022     }
  1023     m3gClearArray(objs);
  1024 }
  1025 #endif /* !defined(M3G_NGL_TEXTURE_API)*/
  1026 
  1027 
  1028 /*!
  1029  * \internal
  1030  * \brief Locks a memory object in place and returns a pointer to it
  1031  *
  1032  * The block is mapped to the returned address until a matching call
  1033  * to m3gUnmapObject. While any object is mapped, memory allocation is
  1034  * prohibited on the whole interface; see m3gLockMemory. Every
  1035  * m3gMapObject call must be followed by a matching m3gUnmapObject
  1036  * call to release the memory lock.
  1037  */
  1038 /*@access M3GMemObject@*/
  1039 static void *m3gMapObject(Interface *m3g, M3GMemObject handle)
  1040 {
  1041     M3G_VALIDATE_INTERFACE(m3g);
  1042 
  1043     if (handle == 0) {
  1044         return NULL;
  1045     }
  1046     else {
  1047         void *ptr;
  1048 
  1049         m3gLockMemory(m3g);
  1050 
  1051         ptr = (*m3g->func.objResolve)(handle);
  1052         ptr = PAYLOAD_BLOCK(ptr);
  1053 
  1054         M3G_LOG2(M3G_LOG_MEMORY_MAPPING, "MapObj 0x%08X -> 0x%08X\n",
  1055                  (unsigned) handle, (unsigned) ptr);
  1056         
  1057         validateBlock(ptr);
  1058         return ptr;
  1059     }
  1060 }
  1061 
  1062 /*!
  1063  * \internal
  1064  * \brief Releases a memory object locked with m3gMapObject
  1065  *
  1066  * The memory address of the object, as obtained from m3gMapObject,
  1067  * must not be used again until a new m3gMapObject call.
  1068  */
  1069 /*@access M3GMemObject@*/
  1070 static void m3gUnmapObject(Interface *m3g, M3GMemObject handle)
  1071 {
  1072     M3G_VALIDATE_INTERFACE(m3g);
  1073     
  1074 #   if defined(M3G_DEBUG)
  1075     if (handle != 0) {
  1076         void *ptr = (*m3g->func.objResolve)(handle);
  1077         validateBlock(PAYLOAD_BLOCK(ptr));
  1078         M3G_LOG1(M3G_LOG_MEMORY_MAPPING,
  1079                  "UnmapObj 0x%08X\n", (unsigned) handle);
  1080         m3gUnlockMemory(m3g);
  1081     }
  1082 #   else
  1083     M3G_UNREF(m3g);
  1084     M3G_UNREF(handle);
  1085 #   endif
  1086 }
  1087 
  1088 /*!
  1089  * \internal
  1090  * \brief Garbage collect until no more objects can be freed
  1091  *
  1092  * \note This is currently a no-op if reference counting has not been
  1093  * enabled at build time.
  1094  */
  1095 static void m3gGarbageCollectAll(Interface *m3g)
  1096 {
  1097     M3G_VALIDATE_INTERFACE(m3g);
  1098     M3G_ASSERT_NO_LOCK(m3g);
  1099     M3G_ASSERT(!m3g->tempLocked);
  1100 
  1101     /* Free the temporary buffer */
  1102     
  1103     m3gFree(m3g, m3g->tempBuffer);
  1104     m3g->tempBuffer = NULL;
  1105     m3g->tempSize = 0;
  1106 }
  1107 
  1108 #if defined(M3G_NGL_CONTEXT_API)
  1109 /*!
  1110  * \internal
  1111  * \brief Gets the address of an external frame buffer and locks the
  1112  * buffer in place
  1113  *
  1114  * Used for memory rendering targets only; see m3gBindMemoryTarget.
  1115  */
  1116 static void *m3gGetExternalFB(Interface *m3g, M3Guint userTarget)
  1117 {
  1118     void *ptr = NULL;
  1119     M3G_VALIDATE_INTERFACE(m3g);
  1120     M3G_ASSERT_NO_LOCK(m3g);
  1121 
  1122     if (m3g->func.getFrameBuffer != NULL) {
  1123         ptr = (*m3g->func.getFrameBuffer)(userTarget);
  1124     }
  1125     
  1126     m3gLockMemory(m3g); /* note that this must be done *after* calling
  1127                          * the callback, or we'll get asserts if GC is
  1128                          * triggered */
  1129     return ptr;
  1130 }
  1131 #endif /* defined(M3G_NGL_CONTEXT_API) */
  1132 
  1133 #if defined(M3G_NGL_CONTEXT_API)
  1134 /*!
  1135  * \internal
  1136  * \brief Releases the external frame buffer locked with
  1137  * m3gGetExternalFB
  1138  */
  1139 static void m3gReleaseExternalFB(Interface *m3g, M3Guint userTarget)
  1140 {
  1141     M3G_VALIDATE_INTERFACE(m3g);
  1142     m3gUnlockMemory(m3g);
  1143     if (m3g->func.releaseFrameBuffer != NULL) {
  1144         (*m3g->func.releaseFrameBuffer)(userTarget);
  1145     }
  1146 }
  1147 #endif /* defined(M3G_NGL_CONTEXT_API) */
  1148 
  1149 #if defined(M3G_NGL_CONTEXT_API)
  1150 /*!
  1151  * \internal
  1152  * \brief Signals to a user callback that the bound rendering target
  1153  * has been released
  1154  *
  1155  * \note This is a callback because it is possible in Java for a
  1156  * rendering context to be destroyed without first releasing the
  1157  * target; in that case, this callback is called to ensure that all
  1158  * external resources are freed.
  1159  */
  1160 static void m3gSignalTargetRelease(Interface *m3g, M3Guint userTarget)
  1161 {
  1162     M3G_VALIDATE_INTERFACE(m3g);
  1163     if (m3g->func.releaseTarget != NULL) {
  1164         (*m3g->func.releaseTarget)(userTarget);
  1165     }
  1166 }
  1167 #endif /* defined(M3G_NGL_CONTEXT_API) */
  1168 
  1169 /*!
  1170  * \internal
  1171  * \brief Raise an error status on this interface
  1172  *
  1173  * Any previous error code will be overwritten.
  1174  */
  1175 #if defined(M3G_DEBUG)
  1176 static void m3gDebugRaiseError(Interface *m3g,
  1177                                M3Genum errorCode,
  1178                                const char *filename,
  1179                                int line)
  1180 #else
  1181 static void m3gRaiseError(Interface *m3g, M3Genum errorCode)
  1182 #endif
  1183 {
  1184     M3G_VALIDATE_INTERFACE(m3g);
  1185 
  1186     if (errorCode == M3G_OUT_OF_MEMORY) {
  1187         M3G_LOG(M3G_LOG_MEMORY_ALL|M3G_LOG_WARNINGS,
  1188                 "Error: Out of memory!\n");
  1189     }
  1190     
  1191 #   if defined(M3G_DEBUG)
  1192     M3G_LOG3(M3G_LOG_USER_ERRORS,
  1193              "Error %d at %s, line %d\n", (int) errorCode, filename, line);
  1194 #   else
  1195     M3G_LOG1(M3G_LOG_USER_ERRORS, "Error %d\n", (int) errorCode);
  1196 #   endif /* M3G_DEBUG */
  1197     
  1198     m3g->error = errorCode;
  1199     if (m3g->func.error != NULL) {
  1200         (*m3g->func.error)(errorCode, (M3GInterface) m3g);
  1201         m3g->error = M3G_NO_ERROR;
  1202     }
  1203 }
  1204 
  1205 #ifdef M3G_NATIVE_LOADER
  1206 /*!
  1207  * \internal
  1208  * \brief Checks whether an error flag has been raised
  1209  */
  1210 static M3GError m3gErrorRaised(const Interface *m3g)
  1211 {
  1212     M3G_VALIDATE_INTERFACE(m3g);
  1213     return (M3GError)m3g->error;
  1214 }
  1215 
  1216 /*!
  1217  * \internal
  1218  * \brief Sets a new error handler and returns the old one
  1219  */
  1220 static m3gErrorHandler *m3gSetErrorHandler(Interface *m3g, m3gErrorHandler *errorHandler)
  1221 {
  1222     m3gErrorHandler *current = m3g->func.error;
  1223     m3g->func.error = errorHandler;
  1224     return current;
  1225 }
  1226 #endif
  1227 
  1228 /*!
  1229  * \internal
  1230  * \brief Gets various constants from the GL driver
  1231  */
  1232 
  1233 #include <stdio.h>
  1234 
  1235 static void m3gConfigureGL(Interface *m3g)
  1236 {
  1237 #   if defined(M3G_NGL_CONTEXT_API)
  1238     m3g->maxTextureDimension = M3G_MAX_TEXTURE_DIMENSION;
  1239     m3g->maxViewportWidth = M3G_MAX_VIEWPORT_WIDTH;
  1240     m3g->maxViewportHeight = M3G_MAX_VIEWPORT_HEIGHT;
  1241     m3g->maxViewportDim = M3G_MAX_VIEWPORT_DIMENSION;
  1242 #   else /* !M3G_NGL_CONTEXT_API */
  1243 	 const GLubyte *info;
  1244     int params[2];
  1245     int numConfigs;
  1246     EGLContext ctx;
  1247     EGLConfig config;
  1248     EGLSurface surf;
  1249     EGLint attrib[5];
  1250 
  1251     m3gInitializeGL(m3g);
  1252 
  1253     attrib[0] = EGL_SURFACE_TYPE;
  1254     attrib[1] = EGL_PBUFFER_BIT;
  1255     attrib[2] = EGL_NONE;
  1256     
  1257     eglChooseConfig(eglGetDisplay(0),
  1258                     attrib,
  1259                     &config, 1,
  1260                     &numConfigs);
  1261 
  1262     M3G_ASSERT(numConfigs > 0);
  1263     
  1264     eglBindAPI(EGL_OPENGL_ES_API);
  1265     ctx = eglCreateContext(eglGetDisplay(0),
  1266                            config,
  1267                            NULL,
  1268                            NULL);
  1269 
  1270     attrib[0] = EGL_WIDTH;
  1271     attrib[1] = 2;
  1272     attrib[2] = EGL_HEIGHT;
  1273     attrib[3] = 2;
  1274     attrib[4] = EGL_NONE;
  1275 
  1276     surf = eglCreatePbufferSurface(eglGetDisplay(0),
  1277                                    config,
  1278                                    attrib);
  1279 
  1280     eglMakeCurrent(eglGetDisplay(0), 
  1281                    surf, surf, ctx);
  1282 
  1283 
  1284     /* Check antialiasing support and workarounds
  1285        from the renderer string.
  1286        HW platforms like MBX has AA, Gerbera and NGL does not.
  1287        MBX needs workarounds for color mask and two sided lighting.
  1288     */
  1289 
  1290 	info = glGetString(GL_RENDERER);
  1291 
  1292     if (strstr((const char *)info, "HW")) {
  1293         m3g->supportAntialiasing = M3G_TRUE;
  1294     }
  1295     else {
  1296         m3g->supportAntialiasing = M3G_FALSE;
  1297     }
  1298 
  1299     if (strstr((const char *)info, "MBX")) {
  1300         m3g->colorMaskWorkaround = M3G_TRUE;
  1301         m3g->twoSidedLightingWorkaround = M3G_TRUE;
  1302     }
  1303     else {
  1304         m3g->colorMaskWorkaround = M3G_FALSE;
  1305         m3g->twoSidedLightingWorkaround = M3G_FALSE;
  1306     }
  1307 
  1308     /* For testing purposes only */
  1309 #   if defined(M3G_FORCE_MBX_WORKAROUNDS)
  1310     m3g->colorMaskWorkaround = M3G_TRUE;
  1311     m3g->twoSidedLightingWorkaround = M3G_TRUE;
  1312 #   endif
  1313 
  1314     glGetIntegerv(GL_MAX_TEXTURE_SIZE, params);
  1315 
  1316     m3g->maxTextureDimension = params[0];
  1317 
  1318     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, params);
  1319 
  1320     m3g->maxViewportWidth = params[0];
  1321     m3g->maxViewportHeight = params[1];
  1322     m3g->maxViewportDim = M3G_MIN(params[0], params[1]);
  1323 
  1324     eglMakeCurrent(eglGetDisplay(0), NULL, NULL, NULL);
  1325     eglDestroySurface(eglGetDisplay(0), surf);
  1326     eglDestroyContext(eglGetDisplay(0), ctx);
  1327 
  1328     m3gShutdownGL(m3g);
  1329     
  1330 #endif /* M3G_NGL_CONTEXT_API */
  1331 }
  1332 
  1333 
  1334 /*!
  1335  * \internal
  1336  * \brief Initializes the GL subsystem
  1337  */
  1338 static void m3gInitializeGL(Interface *m3g)
  1339 {
  1340 #   if defined(M3G_NGL_CONTEXT_API)
  1341 #       define glRefCount m3gs_glRefCount
  1342 #   else
  1343 #       define glRefCount m3g->glRefCount
  1344 #   endif
  1345     
  1346     M3G_VALIDATE_INTERFACE(m3g);
  1347     M3G_UNREF(m3g);
  1348 
  1349     if (++glRefCount == 1) {
  1350         M3G_LOG(M3G_LOG_INTERFACE, "Initializing GL\n");
  1351         
  1352 #       if defined(M3G_NGL_CONTEXT_API)
  1353         
  1354         m3gs_nglMem = M3G_SYSTEM_ALLOC(NGL_MIN_WORKING_MEMORY_BYTES);
  1355         m3gs_nglTexMgr = nglCreateTextureManager();
  1356         if (!nglInit(m3gs_nglMem, NGL_MIN_WORKING_MEMORY_BYTES,
  1357                      m3gs_nglTexMgr,
  1358                      0)) {
  1359             M3G_ASSERT(M3G_FALSE);
  1360         }
  1361         M3G_ASSERT_GL;
  1362     
  1363 #       else /* !M3G_NGL_CONTEXT_API */
  1364     
  1365         m3gInitializeEGL();
  1366 
  1367 #       endif
  1368         
  1369 #       if defined(M3G_NGL_TEXTURE_API)
  1370         {
  1371             int i;
  1372             GLuint texture;
  1373             for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
  1374                 m3gs_nglTexUnit[i] = M3G_SYSTEM_ALLOC(NGL_TEXTURE_STRUCT_SIZE);
  1375                 M3G_ASSERT(m3gs_nglTexUnit[i]);
  1376                 texture = (GLuint) m3gs_nglTexUnit[i];
  1377                 nglInitTextures(1, &texture);
  1378                 glActiveTexture(GL_TEXTURE0 + i);
  1379                 nglBindTextureInternal(GL_TEXTURE_2D, texture);
  1380             }
  1381             glActiveTexture(GL_TEXTURE0);
  1382         }
  1383 #       endif
  1384     }
  1385     
  1386     M3G_ASSERT(glRefCount > 0);
  1387 
  1388 #   undef glRefCount
  1389 }
  1390 
  1391 /*!
  1392  * \internal
  1393  * \brief Shuts down the GL subsystem
  1394  */
  1395 static void m3gShutdownGL(Interface *m3g)
  1396 {
  1397 #   if defined(M3G_NGL_CONTEXT_API)
  1398 #       define glRefCount m3gs_glRefCount
  1399 #   else
  1400 #       define glRefCount m3g->glRefCount
  1401 #   endif
  1402 
  1403     M3G_VALIDATE_INTERFACE(m3g);
  1404     M3G_UNREF(m3g);
  1405     M3G_ASSERT(glRefCount > 0);    
  1406     M3G_LOG(M3G_LOG_INTERFACE, "Shutting down GL...\n");
  1407         
  1408     if (--glRefCount == 0) {
  1409 #       if defined(M3G_NGL_CONTEXT_API)
  1410         
  1411         nglExit();
  1412         nglDeleteTextureManager(m3gs_nglTexMgr);
  1413         M3G_SYSTEM_DEALLOC(m3gs_nglMem);
  1414         m3gs_nglTexMgr = NULL;
  1415         m3gs_nglMem = NULL;
  1416     
  1417 #       else /* !M3G_NGL_CONTEXT_API */
  1418     
  1419         m3gTerminateEGL();
  1420         
  1421 #       endif
  1422 
  1423 #       if defined(M3G_NGL_TEXTURE_API)
  1424         {
  1425             int i;
  1426             for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
  1427                 M3G_SYSTEM_DEALLOC(m3gs_nglTexUnit[i]);
  1428                 m3gs_nglTexUnit[i] = NULL;
  1429             }
  1430         }
  1431 #       endif
  1432     }
  1433     else {
  1434         M3G_LOG1(M3G_LOG_INTERFACE, "Waiting for %d GL objects\n",
  1435                  glRefCount);
  1436     }
  1437 #   undef glRefCount
  1438 }
  1439 
  1440 /*!
  1441  * \internal
  1442  * \brief Make any run-time portability checks
  1443  */
  1444 static M3Gbool m3gSystemCheck(void)
  1445 {
  1446     /* Check that right shifts on signed values work as expected
  1447      * (extending the top bit to keep the sign) */
  1448     {
  1449         int magic = -0x7F7E80B1;
  1450         if ((magic >> 4) != -0x07F7E80C) { 
  1451             return M3G_FALSE;
  1452         }
  1453     }
  1454 
  1455     /* Check endianess if dependent code introduced */
  1456     
  1457     return M3G_TRUE;
  1458 }
  1459 
  1460 static M3Gbool m3gGetColorMaskWorkaround(Interface *m3g)
  1461 {
  1462     return m3g->colorMaskWorkaround;
  1463 }
  1464 
  1465 static M3Gbool m3gGetTwoSidedLightingWorkaround(Interface *m3g)
  1466 {
  1467     return m3g->twoSidedLightingWorkaround;
  1468 }
  1469 
  1470 /*!
  1471  * \internal
  1472  * \brief Increment a statistics counter
  1473  */
  1474 #if defined(M3G_ENABLE_PROFILING)
  1475 static void m3gIncStat(Interface *m3g, M3Gstatistic stat, M3Gint increment)
  1476 {
  1477     m3g->statistics[stat] += increment;
  1478 }
  1479 #endif
  1480 
  1481 /*!
  1482  * \internal
  1483  * \brief Increment a statistics counter
  1484  */
  1485 #if defined(M3G_ENABLE_PROFILING)
  1486 static void m3gResetStat(Interface *m3g, M3Gstatistic stat)
  1487 {
  1488     m3g->statistics[stat] = 0;
  1489 }
  1490 #endif
  1491 
  1492 /*!
  1493  * \internal
  1494  * \brief Output memory peak counters to the log
  1495  */
  1496 #if defined(M3G_ENABLE_PROFILING)
  1497 static void m3gLogMemoryPeakCounter(const Interface *m3g) 
  1498 {
  1499     M3G_LOG3(M3G_LOG_MEMORY_USAGE,
  1500              "Memory peaks: %d KB heap, %d KB obj, %d KB total\n",
  1501              m3g->statistics[M3G_STAT_MEMORY_MALLOC_PEAK] >> 10,
  1502              m3g->statistics[M3G_STAT_MEMORY_OBJECT_PEAK] >> 10,
  1503              m3g->statistics[M3G_STAT_MEMORY_PEAK] >> 10);
  1504 }
  1505 #endif
  1506 
  1507 /*!
  1508  * \internal
  1509  * \brief Update the peak memory counter
  1510  *
  1511  * This function should be called after each memory allocation
  1512  */
  1513 #if defined(M3G_ENABLE_PROFILING)
  1514 static void m3gUpdateMemoryPeakCounter(Interface *m3g)
  1515 {
  1516     if (m3g->statistics[M3G_STAT_MEMORY_ALLOCATED] > m3g->statistics[M3G_STAT_MEMORY_PEAK]) {
  1517         m3g->statistics[M3G_STAT_MEMORY_PEAK] = m3g->statistics[M3G_STAT_MEMORY_ALLOCATED];
  1518     }
  1519     if (m3g->statistics[M3G_STAT_MEMORY_MALLOC_BYTES] > m3g->statistics[M3G_STAT_MEMORY_MALLOC_PEAK]) {
  1520         m3g->statistics[M3G_STAT_MEMORY_MALLOC_PEAK] = m3g->statistics[M3G_STAT_MEMORY_MALLOC_BYTES];
  1521     }
  1522     if (m3g->statistics[M3G_STAT_MEMORY_OBJECT_BYTES] > m3g->statistics[M3G_STAT_MEMORY_OBJECT_PEAK]) {
  1523         m3g->statistics[M3G_STAT_MEMORY_OBJECT_PEAK] = m3g->statistics[M3G_STAT_MEMORY_OBJECT_BYTES];
  1524     }
  1525 
  1526     /* Output peaks in 100 KB increments to reduce amont of log clutter */
  1527     
  1528     if (m3g->statistics[M3G_STAT_MEMORY_PEAK] - m3g->lastPeak > (100 << 10)) {
  1529         m3gLogMemoryPeakCounter(m3g);
  1530         m3g->lastPeak = m3g->statistics[M3G_STAT_MEMORY_PEAK];
  1531     }
  1532 }
  1533 #endif /* M3G_ENABLE_PROFILING */
  1534 
  1535 #if defined(M3G_ENABLE_PROFILING) && defined(M3G_TARGET_SYMBIAN)
  1536 extern M3Gbool m3gProfileTriggered(void);
  1537 #endif
  1538 
  1539 /*!
  1540  * \internal
  1541  * \brief Output profiling counters to the log
  1542  */
  1543 #if defined(M3G_ENABLE_PROFILING) && (M3G_PROFILE_LOG_INTERVAL > 0)
  1544 static void m3gLogProfileCounters(Interface *m3g)
  1545 {
  1546     M3Gint profTime;
  1547 #   if defined(M3G_TARGET_SYMBIAN)
  1548     profTime = m3gProfileTriggered();
  1549     if (profTime > 0) {
  1550 #   else
  1551     profTime = M3G_PROFILE_LOG_INTERVAL;
  1552     if (++m3g->profileInterval >= M3G_PROFILE_LOG_INTERVAL) {
  1553 #   endif
  1554         M3Gint v, i;
  1555             
  1556         M3G_LOG1(M3G_LOG_PROFILE, "Profile %d:", profTime);
  1557 
  1558         for (i = 0; i < M3G_STAT_MAX; ++i) {
  1559             v = m3gGetStatistic(m3g, i);
  1560             M3G_LOG1(M3G_LOG_PROFILE, " %d", v);
  1561         }
  1562         M3G_LOG(M3G_LOG_PROFILE, "\n");
  1563         m3g->profileInterval = 0;
  1564     }
  1565 }
  1566 #endif /* M3G_ENABLE_PROFILING && M3G_PROFILE_LOG_INTERVAL > 0 */
  1567 
  1568 /*----------------------------------------------------------------------
  1569  * Public API implementation
  1570  *--------------------------------------------------------------------*/
  1571 
  1572 /*!
  1573  * \brief Creates a new M3G interface
  1574  *
  1575  */
  1576 M3G_API M3GInterface m3gCreateInterface(
  1577     /*@in@*//*@temp@*/ const M3Gparams *params)
  1578 {
  1579     /* This is likely to get executed exactly once during the
  1580      * execution of an application, so before doing anything else,
  1581      * execute the run-time sanity checks */
  1582 
  1583     if (!m3gSystemCheck()) {
  1584         M3G_ASSERT(M3G_FALSE);
  1585         return NULL;
  1586     }
  1587 
  1588     /* Allow for deletion of existing log files */
  1589     
  1590 #   if defined(M3G_LOGLEVEL)
  1591     m3gBeginLog();
  1592 #   endif
  1593     
  1594     /* To actually create the interface, first check the supplied
  1595      * function pointers */
  1596     
  1597     if (params == NULL
  1598         || params->mallocFunc == NULL
  1599         || params->freeFunc == NULL) {
  1600         return NULL;
  1601     }
  1602     if (params->objAllocFunc != NULL
  1603         && (params->objResolveFunc == NULL
  1604             || params->objFreeFunc == NULL)) {
  1605         return NULL;
  1606     }
  1607     
  1608     /* Allocate the interface using the provided malloc function and
  1609      * initialize. Note that this is slightly different from all other
  1610      * constructors due to the memory instrumentation being done
  1611      * directly rather than via m3gAlloc which isn't usable yet. */
  1612     {
  1613         Interface *m3g = (*params->mallocFunc)(INSTRUMENTATED_SIZE(sizeof(Interface)));
  1614         if (m3g == NULL) {
  1615             M3G_LOG(M3G_LOG_FATAL_ERRORS, "Interface creation failed\n");
  1616             return NULL;
  1617         }
  1618         M3G_LOG1(M3G_LOG_INTERFACE, "New interface 0x%08X\n", (unsigned) m3g);
  1619         
  1620         m3g = (Interface *) PAYLOAD_BLOCK(m3g);
  1621 #       if defined(M3G_DEBUG_HEAP_TRACKING)
  1622         instrumentateBlock(m3g, sizeof(*m3g), __FILE__, __LINE__);
  1623 #       else
  1624         instrumentateBlock(m3g, sizeof(*m3g));
  1625 #       endif
  1626         m3gZero(m3g, sizeof(*m3g));
  1627             
  1628         m3g->func.malloc = params->mallocFunc;
  1629         m3g->func.free   = params->freeFunc;
  1630 
  1631         if (params->objAllocFunc) {
  1632             m3g->func.objAlloc   = params->objAllocFunc;
  1633             m3g->func.objFree    = params->objFreeFunc;
  1634             m3g->func.objResolve = params->objResolveFunc;
  1635         }
  1636         else {
  1637             m3g->func.objAlloc   = (m3gObjectAllocator*)(m3g->func.malloc);
  1638             m3g->func.objFree    = (m3gObjectDeallocator*)(m3g->func.free);
  1639             m3g->func.objResolve = defaultResolver;
  1640         }
  1641 
  1642         m3g->func.error = params->errorFunc;
  1643         m3g->func.getFrameBuffer = params->beginRenderFunc;
  1644         m3g->func.releaseFrameBuffer = params->endRenderFunc;
  1645 
  1646         m3g->userContext = params->userContext;
  1647         
  1648         /* Initialize memory allocation failure debugging */
  1649 #       if defined(M3G_DEBUG_OUT_OF_MEMORY)
  1650         {
  1651             const char *str;
  1652             
  1653 #           define M3G_GETENV(name, field)      \
  1654                 str = getenv(name);             \
  1655                 if (str) {                      \
  1656                     m3g-> ## field = atoi(str); \
  1657                 }
  1658 
  1659             M3G_GETENV("M3G_DEBUG_MALLOC_LIMIT", mallocLimit);
  1660             M3G_GETENV("M3G_DEBUG_OBJALLOC_LIMIT", objAllocLimit);
  1661             M3G_GETENV("M3G_DEBUG_MALLOC_FAILRATE", mallocFailRate);
  1662             M3G_GETENV("M3G_DEBUG_OBJALLOC_FAILRATE", objAllocFailRate);
  1663 
  1664 #           undef M3G_GETENV
  1665         }
  1666 #       endif
  1667 
  1668 #       if !defined(M3G_NGL_CONTEXT_API)
  1669         /* Before messing with EGL state, check if EGL is already
  1670          * initialized by the calling application. */
  1671     
  1672         if (eglQueryString(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_VERSION)) {
  1673             ++m3g->glRefCount;
  1674         }
  1675 #       endif /*!M3G_NGL_CONTEXT_API*/
  1676         
  1677         /* Dig some constants from the GL implementation */
  1678         
  1679         m3gConfigureGL(m3g);
  1680 
  1681         /* All done! Now we can allocate the more trival stuff */
  1682 
  1683         m3g->tcache = m3gCreateTransformCache(m3g);
  1684         m3gInitArray(&m3g->objects);
  1685         
  1686         M3G_LOG1(M3G_LOG_INTERFACE,
  1687                  "Interface 0x%08X initialized\n", (unsigned) m3g);
  1688         return (M3GInterface) m3g;
  1689     }
  1690 }
  1691 
  1692 /*!
  1693  * \brief Deletes an M3G interface and all associated objects
  1694  */
  1695 M3G_API void m3gDeleteInterface(M3GInterface interface)
  1696 {
  1697     Interface *m3g = (Interface *)interface;
  1698     M3G_VALIDATE_INTERFACE(m3g);
  1699     M3G_LOG1(M3G_LOG_INTERFACE,
  1700              "Shutting down interface 0x%08X...\n", (unsigned) m3g);
  1701 
  1702     /* Check if we still have objects lingering (this may happen when
  1703      * Java GC deletes the interface first, for instance), and just
  1704      * mark the interface for deletion in that case */
  1705     
  1706     if (m3g->objCount > 0) {
  1707         M3G_ASSERT(!interface->shutdown);
  1708         M3G_LOG1(M3G_LOG_INTERFACE, "Waiting for %d objects\n",
  1709                  interface->objCount);
  1710         interface->shutdown = M3G_TRUE;
  1711         return;
  1712     }
  1713 
  1714     m3gDestroyArray(&m3g->objects, m3g);
  1715 #   if !defined(M3G_NGL_TEXTURE_API)
  1716     /* Free the list of dead GL objects (those will have been deleted
  1717      * along with the owning contexts by now) */
  1718 
  1719     m3gDestroyArray(&m3g->deadGLObjects, m3g);
  1720 #   endif
  1721 
  1722     /* Delete temp buffers and caches */
  1723     
  1724     M3G_ASSERT(!m3g->tempLocked);
  1725     m3gFree(m3g, m3g->tempBuffer);
  1726 
  1727     m3gDeleteTransformCache(m3g->tcache);
  1728     
  1729     /* Check for any leaked memory */
  1730 #   if defined(M3G_DEBUG)
  1731     if (m3g->mallocCount != 0 || m3g->objAllocCount != 0) {
  1732         M3G_LOG(M3G_LOG_FATAL_ERRORS, "Memory leak detected!\n");
  1733         if (m3g->mallocCount != 0) {
  1734             M3G_LOG1(M3G_LOG_FATAL_ERRORS,
  1735                      "\t%d memory blocks\n", m3g->mallocCount);
  1736 #           if defined(M3G_DEBUG_HEAP_TRACKING) && defined(M3G_LOGLEVEL)
  1737             M3G_LOG(M3G_LOG_FATAL_ERRORS, "Dumping blocks...\n");
  1738             dumpBlocks(m3g->blockList);
  1739 #           endif
  1740         }
  1741         if (m3g->objAllocCount != 0) {
  1742             M3G_LOG1(M3G_LOG_FATAL_ERRORS,
  1743                      "\t%d memory objects\n", m3g->objAllocCount);
  1744         }
  1745     }
  1746 #   endif /* M3G_DEBUG */
  1747 
  1748     /* Cleanup profiling resources */
  1749     m3gCleanupProfile();
  1750     m3gLogMemoryPeakCounter(m3g);
  1751     
  1752     /* Delete self */
  1753     {
  1754         m3gFreeFunc *freeFunc = m3g->func.free;
  1755         destroyBlock(m3g);
  1756         (*freeFunc)(PHYSICAL_BLOCK(m3g));
  1757     }
  1758 
  1759     M3G_LOG1(M3G_LOG_INTERFACE,
  1760              "Interface 0x%08X destroyed\n", (unsigned) m3g);
  1761 
  1762     /* Allow for log cleanup */
  1763     
  1764 #   if defined(M3G_LOGLEVEL)
  1765     m3gEndLog();
  1766 #   endif    
  1767 }
  1768 
  1769 /*!
  1770  * \brief Returns the latest error that occurred on an M3G interface
  1771  *
  1772  * Returns the latest error for \c interface, and resets the error
  1773  * status to M3G_NO_ERROR.
  1774  *
  1775  * @param interface handle of the interface to query for errors
  1776  */
  1777 M3G_API M3Genum m3gGetError(M3GInterface interface)
  1778 {
  1779     Interface *m3g = (Interface *)interface;
  1780     M3G_VALIDATE_INTERFACE(m3g);
  1781     {
  1782         M3Genum error = m3g->error;
  1783         m3g->error = M3G_NO_ERROR;
  1784         return error;
  1785     }
  1786 }
  1787 
  1788 /*!
  1789  * \brief Returns the user context data pointer associated with an M3G interface
  1790  *
  1791  * User context data can be associated with an interface via the
  1792  * M3GParams struct in m3gCreateInterface.
  1793  * 
  1794  * @param interface handle of the interface
  1795  * @return pointer to the user context
  1796  */
  1797 M3G_API void *m3gGetUserContext(M3GInterface interface)
  1798 {
  1799     Interface *m3g = (Interface *)interface;
  1800     M3G_VALIDATE_INTERFACE(m3g);
  1801     return m3g->userContext;
  1802 }
  1803 
  1804 /*!
  1805  * \brief Returns if antialiasing is supported
  1806  *
  1807  * User context data can be associated with an interface via the
  1808  * M3GParams struct in m3gCreateInterface.
  1809  * 
  1810  * @param interface handle of the interface
  1811  * @return pointer to the user context
  1812  */
  1813 M3G_API M3Gbool m3gIsAntialiasingSupported(M3GInterface interface)
  1814 {
  1815     Interface *m3g = (Interface *)interface;
  1816     M3G_VALIDATE_INTERFACE(m3g);
  1817     return m3g->supportAntialiasing;
  1818 }
  1819 
  1820 /*!
  1821  * \brief Free memory by removing all detached objects
  1822  *
  1823  * Garbage collection will run automatically during normal operation,
  1824  * so there is no need to call this function. However, it allows the
  1825  * application to signal a suitable time to invest more time in
  1826  * garbage collection, potentially improving performance later on.
  1827  */
  1828 M3G_API void m3gGarbageCollect(M3GInterface interface)
  1829 {
  1830     Interface *m3g = (Interface *) interface;
  1831     M3G_VALIDATE_INTERFACE(m3g);
  1832     m3gGarbageCollectAll(m3g);
  1833 }
  1834 
  1835 /*!
  1836  * \brief Returns the transformation cache for this interface
  1837  */
  1838 static TCache *m3gGetTransformCache(Interface *m3g)
  1839 {
  1840     return m3g->tcache;
  1841 }
  1842 
  1843 /*!
  1844  * \brief Returns the value of a given statistic
  1845  *
  1846  * If the statistic is a counter (such as number of rendering calls),
  1847  * its value is also cleared.
  1848  *
  1849  * \note Dependent on the M3G_ENABLE_PROFILING compile-time flag; if
  1850  * undefined, all statistic queries return zero
  1851  */
  1852 #if defined(M3G_ENABLE_PROFILING)
  1853 /*@access M3GInterface@*/
  1854 M3G_API M3Gint m3gGetStatistic(M3GInterface hInterface, M3Gstatistic stat)
  1855 {
  1856     Interface *m3g = (Interface*) hInterface;
  1857     M3G_VALIDATE_INTERFACE(m3g);
  1858     
  1859     if (m3gInRange(stat, 0, M3G_STAT_MAX-1)) {
  1860         M3Gint value = m3g->statistics[stat];
  1861 
  1862         if (stat < M3G_STAT_CUMULATIVE) {
  1863             m3g->statistics[stat] = 0;
  1864         }
  1865     
  1866         return value;
  1867     }
  1868     else {
  1869         m3gRaiseError(m3g, M3G_INVALID_ENUM);
  1870         return -1;
  1871     }
  1872 }
  1873 #else
  1874 M3G_API M3Gint m3gGetStatistic(M3GInterface hInterface, M3Gstatistic stat)
  1875 {
  1876     M3G_UNREF(hInterface);
  1877     M3G_UNREF(stat);
  1878     return 0;
  1879 }
  1880 #endif /*M3G_ENABLE_PROFILING*/
  1881 
  1882 
  1883 #undef INSTRUMENTATED_SIZE
  1884 #undef PAYLOAD_BLOCK
  1885 #undef PHYSICAL_BLOCK
  1886