m3g/m3gcore11/src/m3g_interface.c
changeset 0 5d03bc08d59c
child 26 15986eb6c500
child 45 36b2e23a8629
equal deleted inserted replaced
-1:000000000000 0:5d03bc08d59c
       
     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     /*Object *objects;*/
       
    72 
       
    73     /*! \internal \brief Latest error code for this interface */
       
    74     M3Genum error;
       
    75 
       
    76     /*! \internal \brief Associated user context data */
       
    77     void *userContext;
       
    78     
       
    79 #   if defined(M3G_DEBUG)
       
    80     /*! \internal \brief Number of memory lock requests in effect */
       
    81     M3Gint lockCount;
       
    82     struct {
       
    83         const char *file;
       
    84         int line;
       
    85     } lockHeap[MAX_LOCKHEAP_SIZE];
       
    86 #   endif
       
    87 
       
    88 #   if !defined(M3G_NGL_CONTEXT_API)
       
    89     /*! \internal \brief Number of GL activation requests */
       
    90     M3Gint glRefCount;
       
    91 #   endif
       
    92 
       
    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)
       
   950 {
       
   951     M3G_ASSERT(!m3g->shutdown);
       
   952     M3G_ASSERT(m3gInRange(m3g->objCount, 0, 0x7FFFFFFF));
       
   953     ++m3g->objCount;
       
   954 }
       
   955 
       
   956 /*!
       
   957  * \internal
       
   958  * \brief
       
   959  */
       
   960 static void m3gDelChildObject(Interface *m3g)
       
   961 {
       
   962     M3G_ASSERT(m3g->objCount > 0);
       
   963     if (--m3g->objCount == 0 && m3g->shutdown) {
       
   964         m3gDeleteInterface(m3g);
       
   965     }
       
   966 }
       
   967 
       
   968 #if !defined(M3G_NGL_TEXTURE_API)
       
   969 /*!
       
   970  * \internal
       
   971  * \brief Queue OpenGL texture objects for deletion
       
   972  *
       
   973  * The objects will be deleted when a GL context is next made current.
       
   974  *
       
   975  */
       
   976 static void m3gDeleteGLTextures(Interface *m3g, M3Gsizei n, M3Guint *t)
       
   977 {
       
   978     PointerArray *objs = &m3g->deadGLObjects;
       
   979     while (n--) {
       
   980         if (m3gArrayAppend(objs, (void*) t[n], m3g) < 0) {
       
   981             return; 
       
   982         }
       
   983     }
       
   984 }
       
   985 
       
   986 /*!
       
   987  * \internal
       
   988  * \brief Delete queued OpenGL objects
       
   989  *
       
   990  * This function should be called at suitable points during execution
       
   991  * to delete dead GL texture objects.  A GL context must be current.
       
   992  */
       
   993 static void m3gCollectGLObjects(Interface *m3g)
       
   994 {
       
   995     PointerArray *objs = &m3g->deadGLObjects;
       
   996     M3Gsizei n = m3gArraySize(objs);
       
   997     M3Gint i;
       
   998     for (i = 0; i < n; ++i) {
       
   999         GLuint t = (GLuint) m3gGetArrayElement(objs, i);
       
  1000         glDeleteTextures(1, &t);
       
  1001         M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL texture object 0x%08X\n",
       
  1002                  (unsigned) t);
       
  1003     }
       
  1004     m3gClearArray(objs);
       
  1005 }
       
  1006 #endif /* !defined(M3G_NGL_TEXTURE_API)*/
       
  1007 
       
  1008 
       
  1009 /*!
       
  1010  * \internal
       
  1011  * \brief Locks a memory object in place and returns a pointer to it
       
  1012  *
       
  1013  * The block is mapped to the returned address until a matching call
       
  1014  * to m3gUnmapObject. While any object is mapped, memory allocation is
       
  1015  * prohibited on the whole interface; see m3gLockMemory. Every
       
  1016  * m3gMapObject call must be followed by a matching m3gUnmapObject
       
  1017  * call to release the memory lock.
       
  1018  */
       
  1019 /*@access M3GMemObject@*/
       
  1020 static void *m3gMapObject(Interface *m3g, M3GMemObject handle)
       
  1021 {
       
  1022     M3G_VALIDATE_INTERFACE(m3g);
       
  1023 
       
  1024     if (handle == 0) {
       
  1025         return NULL;
       
  1026     }
       
  1027     else {
       
  1028         void *ptr;
       
  1029 
       
  1030         m3gLockMemory(m3g);
       
  1031 
       
  1032         ptr = (*m3g->func.objResolve)(handle);
       
  1033         ptr = PAYLOAD_BLOCK(ptr);
       
  1034 
       
  1035         M3G_LOG2(M3G_LOG_MEMORY_MAPPING, "MapObj 0x%08X -> 0x%08X\n",
       
  1036                  (unsigned) handle, (unsigned) ptr);
       
  1037         
       
  1038         validateBlock(ptr);
       
  1039         return ptr;
       
  1040     }
       
  1041 }
       
  1042 
       
  1043 /*!
       
  1044  * \internal
       
  1045  * \brief Releases a memory object locked with m3gMapObject
       
  1046  *
       
  1047  * The memory address of the object, as obtained from m3gMapObject,
       
  1048  * must not be used again until a new m3gMapObject call.
       
  1049  */
       
  1050 /*@access M3GMemObject@*/
       
  1051 static void m3gUnmapObject(Interface *m3g, M3GMemObject handle)
       
  1052 {
       
  1053     M3G_VALIDATE_INTERFACE(m3g);
       
  1054     
       
  1055 #   if defined(M3G_DEBUG)
       
  1056     if (handle != 0) {
       
  1057         void *ptr = (*m3g->func.objResolve)(handle);
       
  1058         validateBlock(PAYLOAD_BLOCK(ptr));
       
  1059         M3G_LOG1(M3G_LOG_MEMORY_MAPPING,
       
  1060                  "UnmapObj 0x%08X\n", (unsigned) handle);
       
  1061         m3gUnlockMemory(m3g);
       
  1062     }
       
  1063 #   else
       
  1064     M3G_UNREF(m3g);
       
  1065     M3G_UNREF(handle);
       
  1066 #   endif
       
  1067 }
       
  1068 
       
  1069 /*!
       
  1070  * \internal
       
  1071  * \brief Garbage collect until no more objects can be freed
       
  1072  *
       
  1073  * \note This is currently a no-op if reference counting has not been
       
  1074  * enabled at build time.
       
  1075  */
       
  1076 static void m3gGarbageCollectAll(Interface *m3g)
       
  1077 {
       
  1078     M3G_VALIDATE_INTERFACE(m3g);
       
  1079     M3G_ASSERT_NO_LOCK(m3g);
       
  1080     M3G_ASSERT(!m3g->tempLocked);
       
  1081 
       
  1082     /* Free the temporary buffer */
       
  1083     
       
  1084     m3gFree(m3g, m3g->tempBuffer);
       
  1085     m3g->tempBuffer = NULL;
       
  1086     m3g->tempSize = 0;
       
  1087 }
       
  1088 
       
  1089 #if defined(M3G_NGL_CONTEXT_API)
       
  1090 /*!
       
  1091  * \internal
       
  1092  * \brief Gets the address of an external frame buffer and locks the
       
  1093  * buffer in place
       
  1094  *
       
  1095  * Used for memory rendering targets only; see m3gBindMemoryTarget.
       
  1096  */
       
  1097 static void *m3gGetExternalFB(Interface *m3g, M3Guint userTarget)
       
  1098 {
       
  1099     void *ptr = NULL;
       
  1100     M3G_VALIDATE_INTERFACE(m3g);
       
  1101     M3G_ASSERT_NO_LOCK(m3g);
       
  1102 
       
  1103     if (m3g->func.getFrameBuffer != NULL) {
       
  1104         ptr = (*m3g->func.getFrameBuffer)(userTarget);
       
  1105     }
       
  1106     
       
  1107     m3gLockMemory(m3g); /* note that this must be done *after* calling
       
  1108                          * the callback, or we'll get asserts if GC is
       
  1109                          * triggered */
       
  1110     return ptr;
       
  1111 }
       
  1112 #endif /* defined(M3G_NGL_CONTEXT_API) */
       
  1113 
       
  1114 #if defined(M3G_NGL_CONTEXT_API)
       
  1115 /*!
       
  1116  * \internal
       
  1117  * \brief Releases the external frame buffer locked with
       
  1118  * m3gGetExternalFB
       
  1119  */
       
  1120 static void m3gReleaseExternalFB(Interface *m3g, M3Guint userTarget)
       
  1121 {
       
  1122     M3G_VALIDATE_INTERFACE(m3g);
       
  1123     m3gUnlockMemory(m3g);
       
  1124     if (m3g->func.releaseFrameBuffer != NULL) {
       
  1125         (*m3g->func.releaseFrameBuffer)(userTarget);
       
  1126     }
       
  1127 }
       
  1128 #endif /* defined(M3G_NGL_CONTEXT_API) */
       
  1129 
       
  1130 #if defined(M3G_NGL_CONTEXT_API)
       
  1131 /*!
       
  1132  * \internal
       
  1133  * \brief Signals to a user callback that the bound rendering target
       
  1134  * has been released
       
  1135  *
       
  1136  * \note This is a callback because it is possible in Java for a
       
  1137  * rendering context to be destroyed without first releasing the
       
  1138  * target; in that case, this callback is called to ensure that all
       
  1139  * external resources are freed.
       
  1140  */
       
  1141 static void m3gSignalTargetRelease(Interface *m3g, M3Guint userTarget)
       
  1142 {
       
  1143     M3G_VALIDATE_INTERFACE(m3g);
       
  1144     if (m3g->func.releaseTarget != NULL) {
       
  1145         (*m3g->func.releaseTarget)(userTarget);
       
  1146     }
       
  1147 }
       
  1148 #endif /* defined(M3G_NGL_CONTEXT_API) */
       
  1149 
       
  1150 /*!
       
  1151  * \internal
       
  1152  * \brief Raise an error status on this interface
       
  1153  *
       
  1154  * Any previous error code will be overwritten.
       
  1155  */
       
  1156 #if defined(M3G_DEBUG)
       
  1157 static void m3gDebugRaiseError(Interface *m3g,
       
  1158                                M3Genum errorCode,
       
  1159                                const char *filename,
       
  1160                                int line)
       
  1161 #else
       
  1162 static void m3gRaiseError(Interface *m3g, M3Genum errorCode)
       
  1163 #endif
       
  1164 {
       
  1165     M3G_VALIDATE_INTERFACE(m3g);
       
  1166 
       
  1167     if (errorCode == M3G_OUT_OF_MEMORY) {
       
  1168         M3G_LOG(M3G_LOG_MEMORY_ALL|M3G_LOG_WARNINGS,
       
  1169                 "Error: Out of memory!\n");
       
  1170     }
       
  1171     
       
  1172 #   if defined(M3G_DEBUG)
       
  1173     M3G_LOG3(M3G_LOG_USER_ERRORS,
       
  1174              "Error %d at %s, line %d\n", (int) errorCode, filename, line);
       
  1175 #   else
       
  1176     M3G_LOG1(M3G_LOG_USER_ERRORS, "Error %d\n", (int) errorCode);
       
  1177 #   endif /* M3G_DEBUG */
       
  1178     
       
  1179     m3g->error = errorCode;
       
  1180     if (m3g->func.error != NULL) {
       
  1181         (*m3g->func.error)(errorCode, (M3GInterface) m3g);
       
  1182         m3g->error = M3G_NO_ERROR;
       
  1183     }
       
  1184 }
       
  1185 
       
  1186 #ifdef M3G_NATIVE_LOADER
       
  1187 /*!
       
  1188  * \internal
       
  1189  * \brief Checks whether an error flag has been raised
       
  1190  */
       
  1191 static M3GError m3gErrorRaised(const Interface *m3g)
       
  1192 {
       
  1193     M3G_VALIDATE_INTERFACE(m3g);
       
  1194     return (M3GError)m3g->error;
       
  1195 }
       
  1196 
       
  1197 /*!
       
  1198  * \internal
       
  1199  * \brief Sets a new error handler and returns the old one
       
  1200  */
       
  1201 static m3gErrorHandler *m3gSetErrorHandler(Interface *m3g, m3gErrorHandler *errorHandler)
       
  1202 {
       
  1203     m3gErrorHandler *current = m3g->func.error;
       
  1204     m3g->func.error = errorHandler;
       
  1205     return current;
       
  1206 }
       
  1207 #endif
       
  1208 
       
  1209 /*!
       
  1210  * \internal
       
  1211  * \brief Gets various constants from the GL driver
       
  1212  */
       
  1213 
       
  1214 #include <stdio.h>
       
  1215 
       
  1216 static void m3gConfigureGL(Interface *m3g)
       
  1217 {
       
  1218 #   if defined(M3G_NGL_CONTEXT_API)
       
  1219     m3g->maxTextureDimension = M3G_MAX_TEXTURE_DIMENSION;
       
  1220     m3g->maxViewportWidth = M3G_MAX_VIEWPORT_WIDTH;
       
  1221     m3g->maxViewportHeight = M3G_MAX_VIEWPORT_HEIGHT;
       
  1222     m3g->maxViewportDim = M3G_MAX_VIEWPORT_DIMENSION;
       
  1223 #   else /* !M3G_NGL_CONTEXT_API */
       
  1224 	 const GLubyte *info;
       
  1225     int params[2];
       
  1226     int numConfigs;
       
  1227     EGLContext ctx;
       
  1228     EGLConfig config;
       
  1229     EGLSurface surf;
       
  1230     EGLint attrib[5];
       
  1231 
       
  1232     m3gInitializeGL(m3g);
       
  1233 
       
  1234     attrib[0] = EGL_SURFACE_TYPE;
       
  1235     attrib[1] = EGL_PBUFFER_BIT;
       
  1236     attrib[2] = EGL_NONE;
       
  1237     
       
  1238     eglChooseConfig(eglGetDisplay(0),
       
  1239                     attrib,
       
  1240                     &config, 1,
       
  1241                     &numConfigs);
       
  1242 
       
  1243     M3G_ASSERT(numConfigs > 0);
       
  1244     
       
  1245     ctx = eglCreateContext(eglGetDisplay(0),
       
  1246                            config,
       
  1247                            NULL,
       
  1248                            NULL);
       
  1249 
       
  1250     attrib[0] = EGL_WIDTH;
       
  1251     attrib[1] = 2;
       
  1252     attrib[2] = EGL_HEIGHT;
       
  1253     attrib[3] = 2;
       
  1254     attrib[4] = EGL_NONE;
       
  1255 
       
  1256     surf = eglCreatePbufferSurface(eglGetDisplay(0),
       
  1257                                    config,
       
  1258                                    attrib);
       
  1259 
       
  1260     eglMakeCurrent(eglGetDisplay(0), 
       
  1261                    surf, surf, ctx);
       
  1262 
       
  1263 
       
  1264     /* Check antialiasing support and workarounds
       
  1265        from the renderer string.
       
  1266        HW platforms like MBX has AA, Gerbera and NGL does not.
       
  1267        MBX needs workarounds for color mask and two sided lighting.
       
  1268     */
       
  1269 
       
  1270 	info = glGetString(GL_RENDERER);
       
  1271 
       
  1272     if (strstr((const char *)info, "HW")) {
       
  1273         m3g->supportAntialiasing = M3G_TRUE;
       
  1274     }
       
  1275     else {
       
  1276         m3g->supportAntialiasing = M3G_FALSE;
       
  1277     }
       
  1278 
       
  1279     if (strstr((const char *)info, "MBX")) {
       
  1280         m3g->colorMaskWorkaround = M3G_TRUE;
       
  1281         m3g->twoSidedLightingWorkaround = M3G_TRUE;
       
  1282     }
       
  1283     else {
       
  1284         m3g->colorMaskWorkaround = M3G_FALSE;
       
  1285         m3g->twoSidedLightingWorkaround = M3G_FALSE;
       
  1286     }
       
  1287 
       
  1288     /* For testing purposes only */
       
  1289 #   if defined(M3G_FORCE_MBX_WORKAROUNDS)
       
  1290     m3g->colorMaskWorkaround = M3G_TRUE;
       
  1291     m3g->twoSidedLightingWorkaround = M3G_TRUE;
       
  1292 #   endif
       
  1293 
       
  1294     glGetIntegerv(GL_MAX_TEXTURE_SIZE, params);
       
  1295 
       
  1296     m3g->maxTextureDimension = params[0];
       
  1297 
       
  1298     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, params);
       
  1299 
       
  1300     m3g->maxViewportWidth = params[0];
       
  1301     m3g->maxViewportHeight = params[1];
       
  1302     m3g->maxViewportDim = M3G_MIN(params[0], params[1]);
       
  1303 
       
  1304     eglMakeCurrent(eglGetDisplay(0), NULL, NULL, NULL);
       
  1305     eglDestroySurface(eglGetDisplay(0), surf);
       
  1306     eglDestroyContext(eglGetDisplay(0), ctx);
       
  1307 
       
  1308     m3gShutdownGL(m3g);
       
  1309     
       
  1310 #endif /* M3G_NGL_CONTEXT_API */
       
  1311 }
       
  1312 
       
  1313 
       
  1314 /*!
       
  1315  * \internal
       
  1316  * \brief Initializes the GL subsystem
       
  1317  */
       
  1318 static void m3gInitializeGL(Interface *m3g)
       
  1319 {
       
  1320 #   if defined(M3G_NGL_CONTEXT_API)
       
  1321 #       define glRefCount m3gs_glRefCount
       
  1322 #   else
       
  1323 #       define glRefCount m3g->glRefCount
       
  1324 #   endif
       
  1325     
       
  1326     M3G_VALIDATE_INTERFACE(m3g);
       
  1327     M3G_UNREF(m3g);
       
  1328 
       
  1329     if (++glRefCount == 1) {
       
  1330         M3G_LOG(M3G_LOG_INTERFACE, "Initializing GL\n");
       
  1331         
       
  1332 #       if defined(M3G_NGL_CONTEXT_API)
       
  1333         
       
  1334         m3gs_nglMem = M3G_SYSTEM_ALLOC(NGL_MIN_WORKING_MEMORY_BYTES);
       
  1335         m3gs_nglTexMgr = nglCreateTextureManager();
       
  1336         if (!nglInit(m3gs_nglMem, NGL_MIN_WORKING_MEMORY_BYTES,
       
  1337                      m3gs_nglTexMgr,
       
  1338                      0)) {
       
  1339             M3G_ASSERT(M3G_FALSE);
       
  1340         }
       
  1341         M3G_ASSERT_GL;
       
  1342     
       
  1343 #       else /* !M3G_NGL_CONTEXT_API */
       
  1344     
       
  1345         m3gInitializeEGL();
       
  1346 
       
  1347 #       endif
       
  1348         
       
  1349 #       if defined(M3G_NGL_TEXTURE_API)
       
  1350         {
       
  1351             int i;
       
  1352             GLuint texture;
       
  1353             for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
       
  1354                 m3gs_nglTexUnit[i] = M3G_SYSTEM_ALLOC(NGL_TEXTURE_STRUCT_SIZE);
       
  1355                 M3G_ASSERT(m3gs_nglTexUnit[i]);
       
  1356                 texture = (GLuint) m3gs_nglTexUnit[i];
       
  1357                 nglInitTextures(1, &texture);
       
  1358                 glActiveTexture(GL_TEXTURE0 + i);
       
  1359                 nglBindTextureInternal(GL_TEXTURE_2D, texture);
       
  1360             }
       
  1361             glActiveTexture(GL_TEXTURE0);
       
  1362         }
       
  1363 #       endif
       
  1364     }
       
  1365     
       
  1366     M3G_ASSERT(glRefCount > 0);
       
  1367 
       
  1368 #   undef glRefCount
       
  1369 }
       
  1370 
       
  1371 /*!
       
  1372  * \internal
       
  1373  * \brief Shuts down the GL subsystem
       
  1374  */
       
  1375 static void m3gShutdownGL(Interface *m3g)
       
  1376 {
       
  1377 #   if defined(M3G_NGL_CONTEXT_API)
       
  1378 #       define glRefCount m3gs_glRefCount
       
  1379 #   else
       
  1380 #       define glRefCount m3g->glRefCount
       
  1381 #   endif
       
  1382 
       
  1383     M3G_VALIDATE_INTERFACE(m3g);
       
  1384     M3G_UNREF(m3g);
       
  1385     M3G_ASSERT(glRefCount > 0);    
       
  1386     M3G_LOG(M3G_LOG_INTERFACE, "Shutting down GL...\n");
       
  1387         
       
  1388     if (--glRefCount == 0) {
       
  1389 #       if defined(M3G_NGL_CONTEXT_API)
       
  1390         
       
  1391         nglExit();
       
  1392         nglDeleteTextureManager(m3gs_nglTexMgr);
       
  1393         M3G_SYSTEM_DEALLOC(m3gs_nglMem);
       
  1394         m3gs_nglTexMgr = NULL;
       
  1395         m3gs_nglMem = NULL;
       
  1396     
       
  1397 #       else /* !M3G_NGL_CONTEXT_API */
       
  1398     
       
  1399         m3gTerminateEGL();
       
  1400         
       
  1401 #       endif
       
  1402 
       
  1403 #       if defined(M3G_NGL_TEXTURE_API)
       
  1404         {
       
  1405             int i;
       
  1406             for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
       
  1407                 M3G_SYSTEM_DEALLOC(m3gs_nglTexUnit[i]);
       
  1408                 m3gs_nglTexUnit[i] = NULL;
       
  1409             }
       
  1410         }
       
  1411 #       endif
       
  1412     }
       
  1413     else {
       
  1414         M3G_LOG1(M3G_LOG_INTERFACE, "Waiting for %d GL objects\n",
       
  1415                  glRefCount);
       
  1416     }
       
  1417 #   undef glRefCount
       
  1418 }
       
  1419 
       
  1420 /*!
       
  1421  * \internal
       
  1422  * \brief Make any run-time portability checks
       
  1423  */
       
  1424 static M3Gbool m3gSystemCheck(void)
       
  1425 {
       
  1426     /* Check that right shifts on signed values work as expected
       
  1427      * (extending the top bit to keep the sign) */
       
  1428     {
       
  1429         int magic = -0x7F7E80B1;
       
  1430         if ((magic >> 4) != -0x07F7E80C) { 
       
  1431             return M3G_FALSE;
       
  1432         }
       
  1433     }
       
  1434 
       
  1435     /* Check endianess if dependent code introduced */
       
  1436     
       
  1437     return M3G_TRUE;
       
  1438 }
       
  1439 
       
  1440 static M3Gbool m3gGetColorMaskWorkaround(Interface *m3g)
       
  1441 {
       
  1442     return m3g->colorMaskWorkaround;
       
  1443 }
       
  1444 
       
  1445 static M3Gbool m3gGetTwoSidedLightingWorkaround(Interface *m3g)
       
  1446 {
       
  1447     return m3g->twoSidedLightingWorkaround;
       
  1448 }
       
  1449 
       
  1450 /*!
       
  1451  * \internal
       
  1452  * \brief Increment a statistics counter
       
  1453  */
       
  1454 #if defined(M3G_ENABLE_PROFILING)
       
  1455 static void m3gIncStat(Interface *m3g, M3Gstatistic stat, M3Gint increment)
       
  1456 {
       
  1457     m3g->statistics[stat] += increment;
       
  1458 }
       
  1459 #endif
       
  1460 
       
  1461 /*!
       
  1462  * \internal
       
  1463  * \brief Increment a statistics counter
       
  1464  */
       
  1465 #if defined(M3G_ENABLE_PROFILING)
       
  1466 static void m3gResetStat(Interface *m3g, M3Gstatistic stat)
       
  1467 {
       
  1468     m3g->statistics[stat] = 0;
       
  1469 }
       
  1470 #endif
       
  1471 
       
  1472 /*!
       
  1473  * \internal
       
  1474  * \brief Output memory peak counters to the log
       
  1475  */
       
  1476 #if defined(M3G_ENABLE_PROFILING)
       
  1477 static void m3gLogMemoryPeakCounter(const Interface *m3g) 
       
  1478 {
       
  1479     M3G_LOG3(M3G_LOG_MEMORY_USAGE,
       
  1480              "Memory peaks: %d KB heap, %d KB obj, %d KB total\n",
       
  1481              m3g->statistics[M3G_STAT_MEMORY_MALLOC_PEAK] >> 10,
       
  1482              m3g->statistics[M3G_STAT_MEMORY_OBJECT_PEAK] >> 10,
       
  1483              m3g->statistics[M3G_STAT_MEMORY_PEAK] >> 10);
       
  1484 }
       
  1485 #endif
       
  1486 
       
  1487 /*!
       
  1488  * \internal
       
  1489  * \brief Update the peak memory counter
       
  1490  *
       
  1491  * This function should be called after each memory allocation
       
  1492  */
       
  1493 #if defined(M3G_ENABLE_PROFILING)
       
  1494 static void m3gUpdateMemoryPeakCounter(Interface *m3g)
       
  1495 {
       
  1496     if (m3g->statistics[M3G_STAT_MEMORY_ALLOCATED] > m3g->statistics[M3G_STAT_MEMORY_PEAK]) {
       
  1497         m3g->statistics[M3G_STAT_MEMORY_PEAK] = m3g->statistics[M3G_STAT_MEMORY_ALLOCATED];
       
  1498     }
       
  1499     if (m3g->statistics[M3G_STAT_MEMORY_MALLOC_BYTES] > m3g->statistics[M3G_STAT_MEMORY_MALLOC_PEAK]) {
       
  1500         m3g->statistics[M3G_STAT_MEMORY_MALLOC_PEAK] = m3g->statistics[M3G_STAT_MEMORY_MALLOC_BYTES];
       
  1501     }
       
  1502     if (m3g->statistics[M3G_STAT_MEMORY_OBJECT_BYTES] > m3g->statistics[M3G_STAT_MEMORY_OBJECT_PEAK]) {
       
  1503         m3g->statistics[M3G_STAT_MEMORY_OBJECT_PEAK] = m3g->statistics[M3G_STAT_MEMORY_OBJECT_BYTES];
       
  1504     }
       
  1505 
       
  1506     /* Output peaks in 100 KB increments to reduce amont of log clutter */
       
  1507     
       
  1508     if (m3g->statistics[M3G_STAT_MEMORY_PEAK] - m3g->lastPeak > (100 << 10)) {
       
  1509         m3gLogMemoryPeakCounter(m3g);
       
  1510         m3g->lastPeak = m3g->statistics[M3G_STAT_MEMORY_PEAK];
       
  1511     }
       
  1512 }
       
  1513 #endif /* M3G_ENABLE_PROFILING */
       
  1514 
       
  1515 #if defined(M3G_ENABLE_PROFILING) && defined(M3G_TARGET_SYMBIAN)
       
  1516 extern M3Gbool m3gProfileTriggered(void);
       
  1517 #endif
       
  1518 
       
  1519 /*!
       
  1520  * \internal
       
  1521  * \brief Output profiling counters to the log
       
  1522  */
       
  1523 #if defined(M3G_ENABLE_PROFILING) && (M3G_PROFILE_LOG_INTERVAL > 0)
       
  1524 static void m3gLogProfileCounters(Interface *m3g)
       
  1525 {
       
  1526     M3Gint profTime;
       
  1527 #   if defined(M3G_TARGET_SYMBIAN)
       
  1528     profTime = m3gProfileTriggered();
       
  1529     if (profTime > 0) {
       
  1530 #   else
       
  1531     profTime = M3G_PROFILE_LOG_INTERVAL;
       
  1532     if (++m3g->profileInterval >= M3G_PROFILE_LOG_INTERVAL) {
       
  1533 #   endif
       
  1534         M3Gint v, i;
       
  1535             
       
  1536         M3G_LOG1(M3G_LOG_PROFILE, "Profile %d:", profTime);
       
  1537 
       
  1538         for (i = 0; i < M3G_STAT_MAX; ++i) {
       
  1539             v = m3gGetStatistic(m3g, i);
       
  1540             M3G_LOG1(M3G_LOG_PROFILE, " %d", v);
       
  1541         }
       
  1542         M3G_LOG(M3G_LOG_PROFILE, "\n");
       
  1543         m3g->profileInterval = 0;
       
  1544     }
       
  1545 }
       
  1546 #endif /* M3G_ENABLE_PROFILING && M3G_PROFILE_LOG_INTERVAL > 0 */
       
  1547 
       
  1548 /*----------------------------------------------------------------------
       
  1549  * Public API implementation
       
  1550  *--------------------------------------------------------------------*/
       
  1551 
       
  1552 /*!
       
  1553  * \brief Creates a new M3G interface
       
  1554  *
       
  1555  */
       
  1556 M3G_API M3GInterface m3gCreateInterface(
       
  1557     /*@in@*//*@temp@*/ const M3Gparams *params)
       
  1558 {
       
  1559     /* This is likely to get executed exactly once during the
       
  1560      * execution of an application, so before doing anything else,
       
  1561      * execute the run-time sanity checks */
       
  1562 
       
  1563     if (!m3gSystemCheck()) {
       
  1564         M3G_ASSERT(M3G_FALSE);
       
  1565         return NULL;
       
  1566     }
       
  1567 
       
  1568     /* Allow for deletion of existing log files */
       
  1569     
       
  1570 #   if defined(M3G_LOGLEVEL)
       
  1571     m3gBeginLog();
       
  1572 #   endif
       
  1573     
       
  1574     /* To actually create the interface, first check the supplied
       
  1575      * function pointers */
       
  1576     
       
  1577     if (params == NULL
       
  1578         || params->mallocFunc == NULL
       
  1579         || params->freeFunc == NULL) {
       
  1580         return NULL;
       
  1581     }
       
  1582     if (params->objAllocFunc != NULL
       
  1583         && (params->objResolveFunc == NULL
       
  1584             || params->objFreeFunc == NULL)) {
       
  1585         return NULL;
       
  1586     }
       
  1587     
       
  1588     /* Allocate the interface using the provided malloc function and
       
  1589      * initialize. Note that this is slightly different from all other
       
  1590      * constructors due to the memory instrumentation being done
       
  1591      * directly rather than via m3gAlloc which isn't usable yet. */
       
  1592     {
       
  1593         Interface *m3g = (*params->mallocFunc)(INSTRUMENTATED_SIZE(sizeof(Interface)));
       
  1594         if (m3g == NULL) {
       
  1595             M3G_LOG(M3G_LOG_FATAL_ERRORS, "Interface creation failed\n");
       
  1596             return NULL;
       
  1597         }
       
  1598         M3G_LOG1(M3G_LOG_INTERFACE, "New interface 0x%08X\n", (unsigned) m3g);
       
  1599         
       
  1600         m3g = (Interface *) PAYLOAD_BLOCK(m3g);
       
  1601 #       if defined(M3G_DEBUG_HEAP_TRACKING)
       
  1602         instrumentateBlock(m3g, sizeof(*m3g), __FILE__, __LINE__);
       
  1603 #       else
       
  1604         instrumentateBlock(m3g, sizeof(*m3g));
       
  1605 #       endif
       
  1606         m3gZero(m3g, sizeof(*m3g));
       
  1607             
       
  1608         m3g->func.malloc = params->mallocFunc;
       
  1609         m3g->func.free   = params->freeFunc;
       
  1610 
       
  1611         if (params->objAllocFunc) {
       
  1612             m3g->func.objAlloc   = params->objAllocFunc;
       
  1613             m3g->func.objFree    = params->objFreeFunc;
       
  1614             m3g->func.objResolve = params->objResolveFunc;
       
  1615         }
       
  1616         else {
       
  1617             m3g->func.objAlloc   = (m3gObjectAllocator*)(m3g->func.malloc);
       
  1618             m3g->func.objFree    = (m3gObjectDeallocator*)(m3g->func.free);
       
  1619             m3g->func.objResolve = defaultResolver;
       
  1620         }
       
  1621 
       
  1622         m3g->func.error = params->errorFunc;
       
  1623         m3g->func.getFrameBuffer = params->beginRenderFunc;
       
  1624         m3g->func.releaseFrameBuffer = params->endRenderFunc;
       
  1625 
       
  1626         m3g->userContext = params->userContext;
       
  1627         
       
  1628         /* Initialize memory allocation failure debugging */
       
  1629 #       if defined(M3G_DEBUG_OUT_OF_MEMORY)
       
  1630         {
       
  1631             const char *str;
       
  1632             
       
  1633 #           define M3G_GETENV(name, field)      \
       
  1634                 str = getenv(name);             \
       
  1635                 if (str) {                      \
       
  1636                     m3g-> ## field = atoi(str); \
       
  1637                 }
       
  1638 
       
  1639             M3G_GETENV("M3G_DEBUG_MALLOC_LIMIT", mallocLimit);
       
  1640             M3G_GETENV("M3G_DEBUG_OBJALLOC_LIMIT", objAllocLimit);
       
  1641             M3G_GETENV("M3G_DEBUG_MALLOC_FAILRATE", mallocFailRate);
       
  1642             M3G_GETENV("M3G_DEBUG_OBJALLOC_FAILRATE", objAllocFailRate);
       
  1643 
       
  1644 #           undef M3G_GETENV
       
  1645         }
       
  1646 #       endif
       
  1647 
       
  1648 #       if !defined(M3G_NGL_CONTEXT_API)
       
  1649         /* Before messing with EGL state, check if EGL is already
       
  1650          * initialized by the calling application. */
       
  1651     
       
  1652         if (eglQueryString(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_VERSION)) {
       
  1653             ++m3g->glRefCount;
       
  1654         }
       
  1655 #       endif /*!M3G_NGL_CONTEXT_API*/
       
  1656         
       
  1657         /* Dig some constants from the GL implementation */
       
  1658         
       
  1659         m3gConfigureGL(m3g);
       
  1660 
       
  1661         /* All done! Now we can allocate the more trival stuff */
       
  1662 
       
  1663         m3g->tcache = m3gCreateTransformCache(m3g);
       
  1664         
       
  1665         M3G_LOG1(M3G_LOG_INTERFACE,
       
  1666                  "Interface 0x%08X initialized\n", (unsigned) m3g);
       
  1667         return (M3GInterface) m3g;
       
  1668     }
       
  1669 }
       
  1670 
       
  1671 /*!
       
  1672  * \brief Deletes an M3G interface and all associated objects
       
  1673  */
       
  1674 M3G_API void m3gDeleteInterface(M3GInterface interface)
       
  1675 {
       
  1676     Interface *m3g = (Interface *)interface;
       
  1677     M3G_VALIDATE_INTERFACE(m3g);
       
  1678     M3G_LOG1(M3G_LOG_INTERFACE,
       
  1679              "Shutting down interface 0x%08X...\n", (unsigned) m3g);
       
  1680 
       
  1681     /* Check if we still have objects lingering (this may happen when
       
  1682      * Java GC deletes the interface first, for instance), and just
       
  1683      * mark the interface for deletion in that case */
       
  1684     
       
  1685     if (m3g->objCount > 0) {
       
  1686         M3G_ASSERT(!interface->shutdown);
       
  1687         M3G_LOG1(M3G_LOG_INTERFACE, "Waiting for %d objects\n",
       
  1688                  interface->objCount);
       
  1689         interface->shutdown = M3G_TRUE;
       
  1690         return;
       
  1691     }
       
  1692 
       
  1693 #   if !defined(M3G_NGL_TEXTURE_API)
       
  1694     /* Free the list of dead GL objects (those will have been deleted
       
  1695      * along with the owning contexts by now) */
       
  1696 
       
  1697     m3gDestroyArray(&m3g->deadGLObjects, m3g);
       
  1698 #   endif
       
  1699 
       
  1700     /* Delete temp buffers and caches */
       
  1701     
       
  1702     M3G_ASSERT(!m3g->tempLocked);
       
  1703     m3gFree(m3g, m3g->tempBuffer);
       
  1704 
       
  1705     m3gDeleteTransformCache(m3g->tcache);
       
  1706     
       
  1707     /* Check for any leaked memory */
       
  1708 #   if defined(M3G_DEBUG)
       
  1709     if (m3g->mallocCount != 0 || m3g->objAllocCount != 0) {
       
  1710         M3G_LOG(M3G_LOG_FATAL_ERRORS, "Memory leak detected!\n");
       
  1711         if (m3g->mallocCount != 0) {
       
  1712             M3G_LOG1(M3G_LOG_FATAL_ERRORS,
       
  1713                      "\t%d memory blocks\n", m3g->mallocCount);
       
  1714 #           if defined(M3G_DEBUG_HEAP_TRACKING) && defined(M3G_LOGLEVEL)
       
  1715             M3G_LOG(M3G_LOG_FATAL_ERRORS, "Dumping blocks...\n");
       
  1716             dumpBlocks(m3g->blockList);
       
  1717 #           endif
       
  1718         }
       
  1719         if (m3g->objAllocCount != 0) {
       
  1720             M3G_LOG1(M3G_LOG_FATAL_ERRORS,
       
  1721                      "\t%d memory objects\n", m3g->objAllocCount);
       
  1722         }
       
  1723     }
       
  1724 #   endif /* M3G_DEBUG */
       
  1725 
       
  1726     /* Cleanup profiling resources */
       
  1727     m3gCleanupProfile();
       
  1728     m3gLogMemoryPeakCounter(m3g);
       
  1729     
       
  1730     /* Delete self */
       
  1731     {
       
  1732         m3gFreeFunc *freeFunc = m3g->func.free;
       
  1733         destroyBlock(m3g);
       
  1734         (*freeFunc)(PHYSICAL_BLOCK(m3g));
       
  1735     }
       
  1736 
       
  1737     M3G_LOG1(M3G_LOG_INTERFACE,
       
  1738              "Interface 0x%08X destroyed\n", (unsigned) m3g);
       
  1739 
       
  1740     /* Allow for log cleanup */
       
  1741     
       
  1742 #   if defined(M3G_LOGLEVEL)
       
  1743     m3gEndLog();
       
  1744 #   endif    
       
  1745 }
       
  1746 
       
  1747 /*!
       
  1748  * \brief Returns the latest error that occurred on an M3G interface
       
  1749  *
       
  1750  * Returns the latest error for \c interface, and resets the error
       
  1751  * status to M3G_NO_ERROR.
       
  1752  *
       
  1753  * @param interface handle of the interface to query for errors
       
  1754  */
       
  1755 M3G_API M3Genum m3gGetError(M3GInterface interface)
       
  1756 {
       
  1757     Interface *m3g = (Interface *)interface;
       
  1758     M3G_VALIDATE_INTERFACE(m3g);
       
  1759     {
       
  1760         M3Genum error = m3g->error;
       
  1761         m3g->error = M3G_NO_ERROR;
       
  1762         return error;
       
  1763     }
       
  1764 }
       
  1765 
       
  1766 /*!
       
  1767  * \brief Returns the user context data pointer associated with an M3G interface
       
  1768  *
       
  1769  * User context data can be associated with an interface via the
       
  1770  * M3GParams struct in m3gCreateInterface.
       
  1771  * 
       
  1772  * @param interface handle of the interface
       
  1773  * @return pointer to the user context
       
  1774  */
       
  1775 M3G_API void *m3gGetUserContext(M3GInterface interface)
       
  1776 {
       
  1777     Interface *m3g = (Interface *)interface;
       
  1778     M3G_VALIDATE_INTERFACE(m3g);
       
  1779     return m3g->userContext;
       
  1780 }
       
  1781 
       
  1782 /*!
       
  1783  * \brief Returns if antialiasing is supported
       
  1784  *
       
  1785  * User context data can be associated with an interface via the
       
  1786  * M3GParams struct in m3gCreateInterface.
       
  1787  * 
       
  1788  * @param interface handle of the interface
       
  1789  * @return pointer to the user context
       
  1790  */
       
  1791 M3G_API M3Gbool m3gIsAntialiasingSupported(M3GInterface interface)
       
  1792 {
       
  1793     Interface *m3g = (Interface *)interface;
       
  1794     M3G_VALIDATE_INTERFACE(m3g);
       
  1795     return m3g->supportAntialiasing;
       
  1796 }
       
  1797 
       
  1798 /*!
       
  1799  * \brief Free memory by removing all detached objects
       
  1800  *
       
  1801  * Garbage collection will run automatically during normal operation,
       
  1802  * so there is no need to call this function. However, it allows the
       
  1803  * application to signal a suitable time to invest more time in
       
  1804  * garbage collection, potentially improving performance later on.
       
  1805  */
       
  1806 M3G_API void m3gGarbageCollect(M3GInterface interface)
       
  1807 {
       
  1808     Interface *m3g = (Interface *) interface;
       
  1809     M3G_VALIDATE_INTERFACE(m3g);
       
  1810     m3gGarbageCollectAll(m3g);
       
  1811 }
       
  1812 
       
  1813 /*!
       
  1814  * \brief Returns the transformation cache for this interface
       
  1815  */
       
  1816 static TCache *m3gGetTransformCache(Interface *m3g)
       
  1817 {
       
  1818     return m3g->tcache;
       
  1819 }
       
  1820 
       
  1821 /*!
       
  1822  * \brief Returns the value of a given statistic
       
  1823  *
       
  1824  * If the statistic is a counter (such as number of rendering calls),
       
  1825  * its value is also cleared.
       
  1826  *
       
  1827  * \note Dependent on the M3G_ENABLE_PROFILING compile-time flag; if
       
  1828  * undefined, all statistic queries return zero
       
  1829  */
       
  1830 #if defined(M3G_ENABLE_PROFILING)
       
  1831 /*@access M3GInterface@*/
       
  1832 M3G_API M3Gint m3gGetStatistic(M3GInterface hInterface, M3Gstatistic stat)
       
  1833 {
       
  1834     Interface *m3g = (Interface*) hInterface;
       
  1835     M3G_VALIDATE_INTERFACE(m3g);
       
  1836     
       
  1837     if (m3gInRange(stat, 0, M3G_STAT_MAX-1)) {
       
  1838         M3Gint value = m3g->statistics[stat];
       
  1839 
       
  1840         if (stat < M3G_STAT_CUMULATIVE) {
       
  1841             m3g->statistics[stat] = 0;
       
  1842         }
       
  1843     
       
  1844         return value;
       
  1845     }
       
  1846     else {
       
  1847         m3gRaiseError(m3g, M3G_INVALID_ENUM);
       
  1848         return -1;
       
  1849     }
       
  1850 }
       
  1851 #else
       
  1852 M3G_API M3Gint m3gGetStatistic(M3GInterface hInterface, M3Gstatistic stat)
       
  1853 {
       
  1854     M3G_UNREF(hInterface);
       
  1855     M3G_UNREF(stat);
       
  1856     return 0;
       
  1857 }
       
  1858 #endif /*M3G_ENABLE_PROFILING*/
       
  1859 
       
  1860 
       
  1861 #undef INSTRUMENTATED_SIZE
       
  1862 #undef PAYLOAD_BLOCK
       
  1863 #undef PHYSICAL_BLOCK
       
  1864