Update contrib.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
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".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: Interface function implementation
22 * \brief Interface function implementation
25 #ifndef M3G_CORE_INCLUDE
26 # error included by m3g_core.c; do not compile separately.
29 #include "m3g_array.h"
32 #if defined(M3G_DEBUG_OUT_OF_MEMORY)
33 # include <stdlib.h> /* for getenv() and atoi() */
36 /*----------------------------------------------------------------------
37 * Private data structure(s)
38 *--------------------------------------------------------------------*/
40 #define MAX_LOCKHEAP_SIZE 100
42 typedef struct StartGuardRec HeapBlock;
46 * \brief "Interface" structure
48 * Holds global state for an M3G instance
50 struct M3GInterfaceImpl
54 * \brief Interface function pointers
56 * Each interface can have a separate set of functions for memory
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;
71 /*! \internal \brief Latest error code for this interface */
74 /*! \internal \brief Associated user context data */
77 # if defined(M3G_DEBUG)
78 /*! \internal \brief Number of memory lock requests in effect */
83 } lockHeap[MAX_LOCKHEAP_SIZE];
86 # if !defined(M3G_NGL_CONTEXT_API)
87 /*! \internal \brief Number of GL activation requests */
91 /* \internal \brief List of live objects */
93 /*! \internal \brief Number of objects registered for this interface */
96 /*! \internal \brief "Shutdown" flag for when we need to wait for
97 * objects to be deleted */
100 /* Temporary buffer */
105 /* Transformation cache */
108 # if !defined(M3G_NGL_TEXTURE_API)
109 PointerArray deadGLObjects;
112 # if defined(M3G_ENABLE_PROFILING)
115 * \brief Statistics counters
117 M3Gint statistics[M3G_STAT_MAX];
119 M3Gint profileInterval;
120 # endif /*M3G_ENABLE_PROFILING*/
122 /* Memory allocation debug counters */
123 # if defined(M3G_DEBUG)
125 M3Gint objAllocCount;
128 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
129 M3Gsize mallocBytes, mallocLimit;
130 M3Gsize objAllocBytes, objAllocLimit;
131 M3Gint mallocFailureCounter, mallocFailRate;
132 M3Gint objAllocFailureCounter, objAllocFailRate;
135 # if defined(M3G_DEBUG_HEAP_TRACKING)
136 HeapBlock *blockList;
139 M3Gint maxTextureDimension;
140 M3Gint maxViewportWidth;
141 M3Gint maxViewportHeight;
142 M3Gint maxViewportDim;
143 M3Gbool supportAntialiasing;
144 M3Gbool colorMaskWorkaround;
145 M3Gbool twoSidedLightingWorkaround;
148 #if defined(M3G_DEBUG)
150 typedef struct StartGuardRec
152 # if defined(M3G_DEBUG_HEAP_TRACKING)
153 const char *allocFile;
156 HeapBlock *next, *prev;
168 /* Magic number used to tag memory blocks */
169 #define MEMORY_MAGIC 0xAE352001u
171 /* Macros for computing the instrumentated and "user" sizes and
172 * pointers of memory blocks */
175 # define INSTRUMENTATED_SIZE(bytes) \
176 ((M3Guint) (sizeof(StartGuard) + sizeof(EndGuard) \
177 + M3G_ALIGN_TO(bytes, sizeof(M3Guint))))
179 # define PAYLOAD_BLOCK(physicalPtr) \
180 (((M3Gubyte*)physicalPtr) + sizeof(StartGuard))
182 # define PHYSICAL_BLOCK(payloadPtr) \
183 (((M3Gubyte*)payloadPtr) - sizeof(StartGuard))
185 # define PAYLOAD_SIZE(blockPtr) \
186 (((const StartGuard *)PHYSICAL_BLOCK(blockPtr))->endOffset \
187 - sizeof(StartGuard))
189 #else /* !M3G_DEBUG */
192 # define INSTRUMENTATED_SIZE(bytes) ((M3Guint)(bytes))
194 # define PAYLOAD_BLOCK(ptr) (ptr)
196 # define PHYSICAL_BLOCK(ptr) (ptr)
198 # define PAYLOAD_SIZE(blockPtr) 0
200 #endif /* M3G_DEBUG */
202 /*----------------------------------------------------------------------
203 * Static data for managing NGL contexts
204 *--------------------------------------------------------------------*/
206 #if defined(M3G_NGL_CONTEXT_API)
207 static M3Gint m3gs_glRefCount = 0;
209 #if defined(M3G_BUILD_ISA)
210 # define M3G_SYSTEM_ALLOC os_block_alloc
211 # define M3G_SYSTEM_DEALLOC os_block_dealloc
213 # define M3G_SYSTEM_ALLOC malloc
214 # define M3G_SYSTEM_DEALLOC free
217 static void *m3gs_nglMem;
218 static void *m3gs_nglTexMgr;
221 #if defined(M3G_NGL_TEXTURE_API)
222 M3Gubyte *m3gs_nglTexUnit[M3G_NUM_TEXTURE_UNITS];
225 /*----------------------------------------------------------------------
227 *--------------------------------------------------------------------*/
229 /*@access M3GMemObject@*/
230 static /*@dependent@*/ void *defaultResolver(M3GMemObject handle)
232 return (void *)handle;
235 /*--------------------------------------------------------------------*/
237 #if defined(M3G_DEBUG)
239 static void validateBlock(/*@reldef@*//*@temp@*//*@null@*/ const void *ptr)
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 */
250 M3G_LOG1(M3G_LOG_FATAL_ERRORS,
251 "Corrupted memory block 0x%08X!\n", (unsigned) ptr);
252 M3G_ASSERT(M3G_FALSE);
255 M3G_LOG1(M3G_LOG_FATAL_ERRORS,
256 "Invalid pointer to 0x%08X!\n", (unsigned) ptr);
257 M3G_ASSERT(M3G_FALSE);
261 #if defined(M3G_DEBUG_HEAP_TRACKING)
262 static void instrumentateBlock(/*@reldef@*//*@temp@*/ void *ptr,
267 static void instrumentateBlock(/*@reldef@*//*@temp@*/ void *ptr, M3Gsize bytes)
270 M3Guint offset = M3G_ALIGN_TO(bytes, M3G_ALIGNMENT) + sizeof(StartGuard);
275 M3G_ASSERT_ALIGNMENT(offset);
277 /* Insert start and end guard blocks for holding debugging data as
278 * well as guarding against (short) over- and underruns */
280 start = (StartGuard *) PHYSICAL_BLOCK(ptr);
281 end = (EndGuard *) (((M3Gubyte *)start) + offset);
283 start->endOffset = offset;
284 start->magic = MEMORY_MAGIC;
285 end->magic = MEMORY_MAGIC;
287 # if defined(M3G_DEBUG_HEAP_TRACKING)
291 /* Fill with garbage that will show up on the debugger if used
292 * before initialized */
294 M3Guint *p = (M3Guint *) ptr;
295 M3Guint count = bytes >> 2;
302 # if defined(M3G_DEBUG_HEAP_TRACKING)
303 /* Register allocation location */
304 start->allocFile = file;
305 start->allocLine = line;
311 static void destroyBlock(/*@reldef@*//*@temp@*//*@null@*/ void *ptr)
316 /* Fill with garbage that will show up on the debugger if
317 * used after deallocation */
319 StartGuard *start = (StartGuard *) PHYSICAL_BLOCK(ptr);
320 M3Guint *p = (M3Guint *) start;
321 M3Guint count = (start->endOffset + sizeof(EndGuard)) >> 2;
330 #if defined(M3G_DEBUG_HEAP_TRACKING)
331 static void insertBlock(void *ptr, HeapBlock **blockListHead)
333 HeapBlock *head = *blockListHead;
334 HeapBlock *block = (HeapBlock *) PHYSICAL_BLOCK(ptr);
336 M3G_ASSERT_PTR(block);
338 *blockListHead = block;
348 #if defined(M3G_DEBUG_HEAP_TRACKING)
349 static void removeBlock(void *ptr, HeapBlock **blockListHead)
352 HeapBlock *head = *blockListHead;
353 HeapBlock *block = (HeapBlock *) PHYSICAL_BLOCK(ptr);
357 M3G_ASSERT_PTR(head);
358 M3G_ASSERT_PTR(block);
361 block->prev->next = block->next;
364 block->next->prev = block->prev;
367 *blockListHead = block->next;
376 #if defined(M3G_DEBUG_HEAP_TRACKING) && defined(M3G_LOGLEVEL)
377 static void dumpBlocks(const HeapBlock *head)
380 M3G_LOG4(M3G_LOG_FATAL_ERRORS,
381 "0x%08X: %s:%d, %d bytes\n",
382 (unsigned) PAYLOAD_BLOCK(head),
385 (M3Gsizei) PAYLOAD_SIZE(PAYLOAD_BLOCK(head)));
393 #else /* !M3G_DEBUG */
394 # define instrumentateBlock(ptr, bytes)
395 # define validateBlock(ptr)
396 # define destroyBlock(ptr)
397 #endif /* M3G_DEBUG */
399 /*--------------------------------------------------------------------*/
401 /*----------------------------------------------------------------------
403 *--------------------------------------------------------------------*/
405 #if defined(M3G_DEBUG)
409 * \brief Locks all memory allocation for an interface
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
417 * Memory is also automatically locked while a handle is mapped to a
418 * pointer; see m3gMapObject.
421 static void m3gDebugLockMemory(Interface *m3g, const char *file, int line)
423 M3G_ASSERT(m3gInRange(m3g->lockCount, 0, 0x7FFFFFFE));
424 M3G_ASSERT(m3g->lockCount < MAX_LOCKHEAP_SIZE);
426 m3g->lockHeap[m3g->lockCount].file = file;
427 m3g->lockHeap[m3g->lockCount].line = line;
430 m3gIncStat(m3g, M3G_STAT_MEMORY_LOCKS, 1);
435 * \brief Releases a memory allocation lock on an interface
437 static void m3gUnlockMemory(Interface *m3g)
439 M3G_ASSERT(m3g->lockCount > 0);
440 m3g->lockHeap[m3g->lockCount].file = "";
441 m3g->lockHeap[m3g->lockCount].line = 0;
447 * \brief Checks whether memory is currently locked (debug only)
449 static M3Gbool m3gMemoryLocked(Interface *m3g)
451 M3G_ASSERT(m3g->lockCount >= 0);
453 if (m3g->lockCount > 0) {
454 # if defined(M3G_LOGLEVEL)
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);
472 * \brief Allocates a block of memory from a regular C-style heap
475 #if defined(M3G_DEBUG)
476 static void *m3gDebugAlloc(
477 Interface *m3g, M3Gsize bytes, const char *file, int line)
479 static void *m3gAlloc(Interface *m3g, M3Gsize bytes)
483 M3G_VALIDATE_INTERFACE(m3g);
484 M3G_ASSERT_NO_LOCK(m3g);
486 /* Simulate memory allocation failures if enabled */
488 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
489 if (m3g->mallocFailRate > 0 &&
490 m3g->mallocFailureCounter++ >= m3g->mallocFailRate) {
491 m3g->mallocFailureCounter = 0;
494 if (m3g->mallocLimit > 0 &&
495 m3g->mallocBytes + bytes > m3g->mallocLimit) {
500 /* First just try to allocate more memory; if that fails, garbage
501 * collect and try again before returning with an error */
503 ptr = (*m3g->func.malloc)(INSTRUMENTATED_SIZE(bytes));
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));
514 /* Succesfully allocated some, so update statistics */
516 # if defined(M3G_DEBUG)
519 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
520 m3g->mallocBytes += bytes;
523 /* Add instrumentation to the block */
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);
531 instrumentateBlock(ptr, bytes);
534 m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCS, 1);
535 # if defined(M3G_DEBUG) && defined(M3G_ENABLE_PROFILING)
537 M3Gint size = PAYLOAD_SIZE(ptr);
538 m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, size);
539 m3gIncStat(m3g, M3G_STAT_MEMORY_MALLOC_BYTES, size);
542 m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, bytes);
543 m3gIncStat(m3g, M3G_STAT_MEMORY_MALLOC_BYTES, bytes);
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);
551 M3G_LOG2(M3G_LOG_MEMORY_BLOCKS, "Alloc 0x%08X, %d bytes\n",
552 (unsigned) ptr, bytes);
555 m3gUpdateMemoryPeakCounter(m3g);
560 m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
566 * \brief Same as m3gAlloc, but also zero-initializes the allocated
569 #if !defined(M3G_DEBUG)
570 static void *m3gAllocZ(Interface *m3g, M3Gsize bytes)
572 void *ptr = m3gAlloc(m3g, bytes);
579 static void *m3gDebugAllocZ(
580 Interface *m3g, M3Gsize bytes, const char *file, int line)
582 void *ptr = m3gDebugAlloc(m3g, bytes, file, line);
588 #endif /* M3G_DEBUG_HEAP_TRACKING */
592 * \brief Frees a block of memory allocated using m3gAlloc
594 #if defined(M3G_DEBUG)
595 static void m3gDebugFree(Interface *m3g, void *ptr, const char *file, int line)
597 static void m3gFree(Interface *m3g, void *ptr)
600 M3G_VALIDATE_INTERFACE(m3g);
601 M3G_ASSERT_ALIGNMENT(ptr);
602 M3G_ASSERT_NO_LOCK(m3g);
605 M3G_VALIDATE_MEMBLOCK(ptr);
606 M3G_ASSERT(!m3gIsObject(ptr));
608 # if defined(M3G_DEBUG)
610 M3G_ASSERT(m3g->mallocCount >= 0);
613 #if defined(M3G_ENABLE_PROFILING) || defined(M3G_DEBUG_OUT_OF_MEMORY)
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);
620 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
621 m3g->mallocBytes -= size;
626 # if defined(M3G_DEBUG_HEAP_TRACKING)
627 removeBlock(ptr, &m3g->blockList);
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);
637 M3G_LOG3(M3G_LOG_MEMORY_BLOCKS,
638 "Free 0x%08X (%s, line %d)\n",
639 (unsigned) ptr, file, line);
642 M3G_LOG1(M3G_LOG_MEMORY_BLOCKS, "Free 0x%08X\n", (unsigned) ptr);
646 (*m3g->func.free)(PHYSICAL_BLOCK(ptr));
653 * \brief Recycles a block of memory for later use
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.
659 * \param m3g Interface instance
660 * \param ptr pointer to block to recycle
661 * \param bytes size of the block, in bytes
663 static void m3gRecycle(Interface *m3g, void *ptr, M3Gsize bytes)
671 #if defined(M3G_DEBUG)
674 * \brief Checks the integrity of a memory block
676 static void m3gValidateMemory(const void *ptr)
680 #endif /* M3G_DEBUG */
682 #if defined(M3G_DEBUG)
685 * \brief Checks the integrity of an Interface object
687 static void m3gValidateInterface(const Interface *m3g)
689 M3G_VALIDATE_MEMBLOCK(m3g);
691 #endif /* M3G_DEBUG */
693 #if defined (M3G_DEBUG_HEAP_TRACKING)
696 * \brief Marks a block as a live object block
698 * Live objects can never have their memory freed.
700 static void m3gMarkObject(void *ptr)
705 start = (StartGuard *) PHYSICAL_BLOCK(ptr);
710 #if defined (M3G_DEBUG_HEAP_TRACKING)
713 * \brief Checks if a block is an object block
715 static M3Gbool m3gIsObject(const void *ptr)
717 const StartGuard *start;
720 start = (StartGuard *) PHYSICAL_BLOCK(ptr);
721 return (start->isObject != 0);
725 #if defined (M3G_DEBUG_HEAP_TRACKING)
728 * \brief Unmarks an object block
730 static void m3gUnmarkObject(void *ptr)
735 start = (StartGuard *) PHYSICAL_BLOCK(ptr);
743 * \brief Allocates a "memory object" from a potentially compacting heap
745 * A block of memory of \c bytes is allocated, but its address is
746 * available only via the m3gMapObject function.
748 * \return a handle used to refer to the allocated block during the
749 * rest of its lifetime
751 /*@access M3GMemObject@*/
752 #if defined(M3G_DEBUG)
753 static M3GMemObject m3gDebugAllocObject(
754 Interface *m3g, M3Gsize bytes, const char *file, int line)
756 static M3GMemObject m3gAllocObject(Interface *m3g, M3Gsize bytes)
760 M3G_VALIDATE_INTERFACE(m3g);
761 M3G_ASSERT_NO_LOCK(m3g);
763 /* Simulate memory allocation failures if enabled */
765 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
766 if (m3g->objAllocFailRate > 0 &&
767 m3g->objAllocFailureCounter++ >= m3g->objAllocFailRate) {
768 m3g->objAllocFailureCounter = 0;
771 if (m3g->objAllocLimit > 0 &&
772 m3g->objAllocBytes + bytes > m3g->objAllocLimit) {
777 /* Similarly to Alloc, garbage collect and try again if the
778 * first allocation fails */
780 handle = (*m3g->func.objAlloc)(INSTRUMENTATED_SIZE(bytes));
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));
791 /* Succesfully allocated, update statistics */
793 m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCS, 1);
794 # if defined(M3G_DEBUG)
795 m3g->objAllocCount++;
797 void *ptr = PAYLOAD_BLOCK((*m3g->func.objResolve)(handle));
798 # if defined(M3G_DEBUG_HEAP_TRACKING)
799 instrumentateBlock(ptr, bytes, file, line);
801 instrumentateBlock(ptr, bytes);
803 # if defined(M3G_ENABLE_PROFILING)
805 M3Gint size = PAYLOAD_SIZE(ptr);
806 m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, size);
807 m3gIncStat(m3g, M3G_STAT_MEMORY_OBJECT_BYTES, size);
812 m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, bytes);
813 m3gIncStat(m3g, M3G_STAT_MEMORY_OBJECT_BYTES, bytes);
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);
821 M3G_LOG2(M3G_LOG_MEMORY_BLOCKS, "ObjAlloc 0x%08X, %d bytes\n",
822 (unsigned) handle, bytes);
825 m3gUpdateMemoryPeakCounter(m3g);
827 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
828 m3g->objAllocBytes += bytes;
834 m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
840 * \brief Frees a memory object allocated with m3gAllocObject
842 /*@access M3GMemObject@*/
843 #if defined(M3G_DEBUG)
844 static void m3gDebugFreeObject(Interface *m3g, M3GMemObject handle, const char *file, int line)
846 static void m3gFreeObject(Interface *m3g, M3GMemObject handle)
849 M3G_VALIDATE_INTERFACE(m3g);
850 M3G_ASSERT_NO_LOCK(m3g);
854 # if defined(M3G_DEBUG)
856 void *ptr = m3gMapObject(m3g, handle);
857 M3G_VALIDATE_MEMBLOCK(ptr);
858 M3G_ASSERT(!m3gIsObject(ptr));
861 # if defined(M3G_ENABLE_PROFILING) || defined(M3G_DEBUG_OUT_OF_MEMORY)
862 M3Gint size = PAYLOAD_SIZE(ptr);
864 # if defined(M3G_ENABLE_PROFILING)
865 m3gIncStat(m3g, M3G_STAT_MEMORY_ALLOCATED, -size);
866 m3gIncStat(m3g, M3G_STAT_MEMORY_OBJECT_BYTES, -size);
868 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
869 m3g->objAllocBytes -= size;
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);
876 M3G_LOG3(M3G_LOG_MEMORY_BLOCKS,
877 "ObjFree 0x%08X (%s, line %d)\n",
878 (unsigned) handle, file, line);
881 m3gUnmapObject(m3g, handle);
885 m3g->objAllocCount--;
886 M3G_ASSERT(m3g->objAllocCount >= 0);
888 # else /* !M3G_DEBUG*/
889 M3G_LOG1(M3G_LOG_MEMORY_BLOCKS, "ObjFree 0x%08X\n", (unsigned) handle);
892 /* Actual operation */
894 (*m3g->func.objFree)(handle);
899 * \brief Allocates a temporary data buffer
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.
906 * \param m3g interface object
907 * \param bytes size of the temp buffer requested, in bytes
909 static void *m3gAllocTemp(Interface *m3g, M3Gsizei bytes)
911 M3G_VALIDATE_INTERFACE(m3g);
912 M3G_ASSERT_NO_LOCK(m3g);
913 M3G_ASSERT(!m3g->tempLocked);
915 if (m3g->tempSize < bytes) {
916 m3gFree(m3g, m3g->tempBuffer);
917 m3g->tempBuffer = NULL;
920 if (m3g->tempBuffer == NULL) {
921 m3g->tempBuffer = m3gAlloc(m3g, bytes);
922 if (m3g->tempBuffer == NULL) {
923 return NULL; /* automatic out of memory */
925 m3g->tempSize = bytes;
928 m3g->tempLocked = M3G_TRUE;
929 return m3g->tempBuffer;
934 * \brief Release the currently allocated temporary data buffer
936 static void m3gFreeTemp(Interface *m3g)
938 M3G_VALIDATE_INTERFACE(m3g);
939 M3G_ASSERT_NO_LOCK(m3g);
940 M3G_ASSERT(m3g->tempLocked);
942 m3g->tempLocked = M3G_FALSE;
949 static void m3gAddChildObject(Interface *m3g, Object *obj)
951 M3G_ASSERT(!m3g->shutdown);
952 M3G_ASSERT(m3gInRange(m3g->objCount, 0, 0x7FFFFFFF));
955 /* Add the object to the list of live objects */
956 m3gArrayAppend(&m3g->objects, obj, m3g);
963 static void m3gDelChildObject(Interface *m3g, Object *obj)
965 M3G_ASSERT(m3g->objCount > 0);
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);
974 #if !defined(M3G_NGL_TEXTURE_API)
977 * \brief Get a list of live objects with matching class ID
979 static void m3gGetObjectsWithClassID(Interface *m3g, M3GClass classID, PointerArray* objects)
981 M3Gsizei i = m3gArraySize(&m3g->objects);
983 M3GObject obj = (M3GObject)m3gGetArrayElement(&m3g->objects, --i);
984 if (m3gGetClass(obj) == classID)
985 m3gArrayAppend(objects, obj, m3g);
990 * \brief Queue OpenGL texture objects for deletion
992 * The objects will be deleted when a GL context is next made current.
995 static void m3gDeleteGLTextures(Interface *m3g, M3Gsizei n, M3Guint *t)
997 PointerArray *objs = &m3g->deadGLObjects;
999 if (m3gArrayAppend(objs, (void*) t[n], m3g) < 0) {
1007 * \brief Delete queued OpenGL objects
1009 * This function should be called at suitable points during execution
1010 * to delete dead GL texture objects. A GL context must be current.
1012 static void m3gCollectGLObjects(Interface *m3g)
1014 PointerArray *objs = &m3g->deadGLObjects;
1015 M3Gsizei n = m3gArraySize(objs);
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",
1023 m3gClearArray(objs);
1025 #endif /* !defined(M3G_NGL_TEXTURE_API)*/
1030 * \brief Locks a memory object in place and returns a pointer to it
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.
1038 /*@access M3GMemObject@*/
1039 static void *m3gMapObject(Interface *m3g, M3GMemObject handle)
1041 M3G_VALIDATE_INTERFACE(m3g);
1051 ptr = (*m3g->func.objResolve)(handle);
1052 ptr = PAYLOAD_BLOCK(ptr);
1054 M3G_LOG2(M3G_LOG_MEMORY_MAPPING, "MapObj 0x%08X -> 0x%08X\n",
1055 (unsigned) handle, (unsigned) ptr);
1064 * \brief Releases a memory object locked with m3gMapObject
1066 * The memory address of the object, as obtained from m3gMapObject,
1067 * must not be used again until a new m3gMapObject call.
1069 /*@access M3GMemObject@*/
1070 static void m3gUnmapObject(Interface *m3g, M3GMemObject handle)
1072 M3G_VALIDATE_INTERFACE(m3g);
1074 # if defined(M3G_DEBUG)
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);
1090 * \brief Garbage collect until no more objects can be freed
1092 * \note This is currently a no-op if reference counting has not been
1093 * enabled at build time.
1095 static void m3gGarbageCollectAll(Interface *m3g)
1097 M3G_VALIDATE_INTERFACE(m3g);
1098 M3G_ASSERT_NO_LOCK(m3g);
1099 M3G_ASSERT(!m3g->tempLocked);
1101 /* Free the temporary buffer */
1103 m3gFree(m3g, m3g->tempBuffer);
1104 m3g->tempBuffer = NULL;
1108 #if defined(M3G_NGL_CONTEXT_API)
1111 * \brief Gets the address of an external frame buffer and locks the
1114 * Used for memory rendering targets only; see m3gBindMemoryTarget.
1116 static void *m3gGetExternalFB(Interface *m3g, M3Guint userTarget)
1119 M3G_VALIDATE_INTERFACE(m3g);
1120 M3G_ASSERT_NO_LOCK(m3g);
1122 if (m3g->func.getFrameBuffer != NULL) {
1123 ptr = (*m3g->func.getFrameBuffer)(userTarget);
1126 m3gLockMemory(m3g); /* note that this must be done *after* calling
1127 * the callback, or we'll get asserts if GC is
1131 #endif /* defined(M3G_NGL_CONTEXT_API) */
1133 #if defined(M3G_NGL_CONTEXT_API)
1136 * \brief Releases the external frame buffer locked with
1139 static void m3gReleaseExternalFB(Interface *m3g, M3Guint userTarget)
1141 M3G_VALIDATE_INTERFACE(m3g);
1142 m3gUnlockMemory(m3g);
1143 if (m3g->func.releaseFrameBuffer != NULL) {
1144 (*m3g->func.releaseFrameBuffer)(userTarget);
1147 #endif /* defined(M3G_NGL_CONTEXT_API) */
1149 #if defined(M3G_NGL_CONTEXT_API)
1152 * \brief Signals to a user callback that the bound rendering target
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.
1160 static void m3gSignalTargetRelease(Interface *m3g, M3Guint userTarget)
1162 M3G_VALIDATE_INTERFACE(m3g);
1163 if (m3g->func.releaseTarget != NULL) {
1164 (*m3g->func.releaseTarget)(userTarget);
1167 #endif /* defined(M3G_NGL_CONTEXT_API) */
1171 * \brief Raise an error status on this interface
1173 * Any previous error code will be overwritten.
1175 #if defined(M3G_DEBUG)
1176 static void m3gDebugRaiseError(Interface *m3g,
1178 const char *filename,
1181 static void m3gRaiseError(Interface *m3g, M3Genum errorCode)
1184 M3G_VALIDATE_INTERFACE(m3g);
1186 if (errorCode == M3G_OUT_OF_MEMORY) {
1187 M3G_LOG(M3G_LOG_MEMORY_ALL|M3G_LOG_WARNINGS,
1188 "Error: Out of memory!\n");
1191 # if defined(M3G_DEBUG)
1192 M3G_LOG3(M3G_LOG_USER_ERRORS,
1193 "Error %d at %s, line %d\n", (int) errorCode, filename, line);
1195 M3G_LOG1(M3G_LOG_USER_ERRORS, "Error %d\n", (int) errorCode);
1196 # endif /* M3G_DEBUG */
1198 m3g->error = errorCode;
1199 if (m3g->func.error != NULL) {
1200 (*m3g->func.error)(errorCode, (M3GInterface) m3g);
1201 m3g->error = M3G_NO_ERROR;
1205 #ifdef M3G_NATIVE_LOADER
1208 * \brief Checks whether an error flag has been raised
1210 static M3GError m3gErrorRaised(const Interface *m3g)
1212 M3G_VALIDATE_INTERFACE(m3g);
1213 return (M3GError)m3g->error;
1218 * \brief Sets a new error handler and returns the old one
1220 static m3gErrorHandler *m3gSetErrorHandler(Interface *m3g, m3gErrorHandler *errorHandler)
1222 m3gErrorHandler *current = m3g->func.error;
1223 m3g->func.error = errorHandler;
1230 * \brief Gets various constants from the GL driver
1235 static void m3gConfigureGL(Interface *m3g)
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;
1251 m3gInitializeGL(m3g);
1253 attrib[0] = EGL_SURFACE_TYPE;
1254 attrib[1] = EGL_PBUFFER_BIT;
1255 attrib[2] = EGL_NONE;
1257 eglChooseConfig(eglGetDisplay(0),
1262 M3G_ASSERT(numConfigs > 0);
1264 eglBindAPI(EGL_OPENGL_ES_API);
1265 ctx = eglCreateContext(eglGetDisplay(0),
1270 attrib[0] = EGL_WIDTH;
1272 attrib[2] = EGL_HEIGHT;
1274 attrib[4] = EGL_NONE;
1276 surf = eglCreatePbufferSurface(eglGetDisplay(0),
1280 eglMakeCurrent(eglGetDisplay(0),
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.
1290 info = glGetString(GL_RENDERER);
1292 if (strstr((const char *)info, "HW")) {
1293 m3g->supportAntialiasing = M3G_TRUE;
1296 m3g->supportAntialiasing = M3G_FALSE;
1299 if (strstr((const char *)info, "MBX")) {
1300 m3g->colorMaskWorkaround = M3G_TRUE;
1301 m3g->twoSidedLightingWorkaround = M3G_TRUE;
1304 m3g->colorMaskWorkaround = M3G_FALSE;
1305 m3g->twoSidedLightingWorkaround = M3G_FALSE;
1308 /* For testing purposes only */
1309 # if defined(M3G_FORCE_MBX_WORKAROUNDS)
1310 m3g->colorMaskWorkaround = M3G_TRUE;
1311 m3g->twoSidedLightingWorkaround = M3G_TRUE;
1314 glGetIntegerv(GL_MAX_TEXTURE_SIZE, params);
1316 m3g->maxTextureDimension = params[0];
1318 glGetIntegerv(GL_MAX_VIEWPORT_DIMS, params);
1320 m3g->maxViewportWidth = params[0];
1321 m3g->maxViewportHeight = params[1];
1322 m3g->maxViewportDim = M3G_MIN(params[0], params[1]);
1324 eglMakeCurrent(eglGetDisplay(0), NULL, NULL, NULL);
1325 eglDestroySurface(eglGetDisplay(0), surf);
1326 eglDestroyContext(eglGetDisplay(0), ctx);
1330 #endif /* M3G_NGL_CONTEXT_API */
1336 * \brief Initializes the GL subsystem
1338 static void m3gInitializeGL(Interface *m3g)
1340 # if defined(M3G_NGL_CONTEXT_API)
1341 # define glRefCount m3gs_glRefCount
1343 # define glRefCount m3g->glRefCount
1346 M3G_VALIDATE_INTERFACE(m3g);
1349 if (++glRefCount == 1) {
1350 M3G_LOG(M3G_LOG_INTERFACE, "Initializing GL\n");
1352 # if defined(M3G_NGL_CONTEXT_API)
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,
1359 M3G_ASSERT(M3G_FALSE);
1363 # else /* !M3G_NGL_CONTEXT_API */
1369 # if defined(M3G_NGL_TEXTURE_API)
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);
1381 glActiveTexture(GL_TEXTURE0);
1386 M3G_ASSERT(glRefCount > 0);
1393 * \brief Shuts down the GL subsystem
1395 static void m3gShutdownGL(Interface *m3g)
1397 # if defined(M3G_NGL_CONTEXT_API)
1398 # define glRefCount m3gs_glRefCount
1400 # define glRefCount m3g->glRefCount
1403 M3G_VALIDATE_INTERFACE(m3g);
1405 M3G_ASSERT(glRefCount > 0);
1406 M3G_LOG(M3G_LOG_INTERFACE, "Shutting down GL...\n");
1408 if (--glRefCount == 0) {
1409 # if defined(M3G_NGL_CONTEXT_API)
1412 nglDeleteTextureManager(m3gs_nglTexMgr);
1413 M3G_SYSTEM_DEALLOC(m3gs_nglMem);
1414 m3gs_nglTexMgr = NULL;
1417 # else /* !M3G_NGL_CONTEXT_API */
1423 # if defined(M3G_NGL_TEXTURE_API)
1426 for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
1427 M3G_SYSTEM_DEALLOC(m3gs_nglTexUnit[i]);
1428 m3gs_nglTexUnit[i] = NULL;
1434 M3G_LOG1(M3G_LOG_INTERFACE, "Waiting for %d GL objects\n",
1442 * \brief Make any run-time portability checks
1444 static M3Gbool m3gSystemCheck(void)
1446 /* Check that right shifts on signed values work as expected
1447 * (extending the top bit to keep the sign) */
1449 int magic = -0x7F7E80B1;
1450 if ((magic >> 4) != -0x07F7E80C) {
1455 /* Check endianess if dependent code introduced */
1460 static M3Gbool m3gGetColorMaskWorkaround(Interface *m3g)
1462 return m3g->colorMaskWorkaround;
1465 static M3Gbool m3gGetTwoSidedLightingWorkaround(Interface *m3g)
1467 return m3g->twoSidedLightingWorkaround;
1472 * \brief Increment a statistics counter
1474 #if defined(M3G_ENABLE_PROFILING)
1475 static void m3gIncStat(Interface *m3g, M3Gstatistic stat, M3Gint increment)
1477 m3g->statistics[stat] += increment;
1483 * \brief Increment a statistics counter
1485 #if defined(M3G_ENABLE_PROFILING)
1486 static void m3gResetStat(Interface *m3g, M3Gstatistic stat)
1488 m3g->statistics[stat] = 0;
1494 * \brief Output memory peak counters to the log
1496 #if defined(M3G_ENABLE_PROFILING)
1497 static void m3gLogMemoryPeakCounter(const Interface *m3g)
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);
1509 * \brief Update the peak memory counter
1511 * This function should be called after each memory allocation
1513 #if defined(M3G_ENABLE_PROFILING)
1514 static void m3gUpdateMemoryPeakCounter(Interface *m3g)
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];
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];
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];
1526 /* Output peaks in 100 KB increments to reduce amont of log clutter */
1528 if (m3g->statistics[M3G_STAT_MEMORY_PEAK] - m3g->lastPeak > (100 << 10)) {
1529 m3gLogMemoryPeakCounter(m3g);
1530 m3g->lastPeak = m3g->statistics[M3G_STAT_MEMORY_PEAK];
1533 #endif /* M3G_ENABLE_PROFILING */
1535 #if defined(M3G_ENABLE_PROFILING) && defined(M3G_TARGET_SYMBIAN)
1536 extern M3Gbool m3gProfileTriggered(void);
1541 * \brief Output profiling counters to the log
1543 #if defined(M3G_ENABLE_PROFILING) && (M3G_PROFILE_LOG_INTERVAL > 0)
1544 static void m3gLogProfileCounters(Interface *m3g)
1547 # if defined(M3G_TARGET_SYMBIAN)
1548 profTime = m3gProfileTriggered();
1551 profTime = M3G_PROFILE_LOG_INTERVAL;
1552 if (++m3g->profileInterval >= M3G_PROFILE_LOG_INTERVAL) {
1556 M3G_LOG1(M3G_LOG_PROFILE, "Profile %d:", profTime);
1558 for (i = 0; i < M3G_STAT_MAX; ++i) {
1559 v = m3gGetStatistic(m3g, i);
1560 M3G_LOG1(M3G_LOG_PROFILE, " %d", v);
1562 M3G_LOG(M3G_LOG_PROFILE, "\n");
1563 m3g->profileInterval = 0;
1566 #endif /* M3G_ENABLE_PROFILING && M3G_PROFILE_LOG_INTERVAL > 0 */
1568 /*----------------------------------------------------------------------
1569 * Public API implementation
1570 *--------------------------------------------------------------------*/
1573 * \brief Creates a new M3G interface
1576 M3G_API M3GInterface m3gCreateInterface(
1577 /*@in@*//*@temp@*/ const M3Gparams *params)
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 */
1583 if (!m3gSystemCheck()) {
1584 M3G_ASSERT(M3G_FALSE);
1588 /* Allow for deletion of existing log files */
1590 # if defined(M3G_LOGLEVEL)
1594 /* To actually create the interface, first check the supplied
1595 * function pointers */
1598 || params->mallocFunc == NULL
1599 || params->freeFunc == NULL) {
1602 if (params->objAllocFunc != NULL
1603 && (params->objResolveFunc == NULL
1604 || params->objFreeFunc == NULL)) {
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. */
1613 Interface *m3g = (*params->mallocFunc)(INSTRUMENTATED_SIZE(sizeof(Interface)));
1615 M3G_LOG(M3G_LOG_FATAL_ERRORS, "Interface creation failed\n");
1618 M3G_LOG1(M3G_LOG_INTERFACE, "New interface 0x%08X\n", (unsigned) m3g);
1620 m3g = (Interface *) PAYLOAD_BLOCK(m3g);
1621 # if defined(M3G_DEBUG_HEAP_TRACKING)
1622 instrumentateBlock(m3g, sizeof(*m3g), __FILE__, __LINE__);
1624 instrumentateBlock(m3g, sizeof(*m3g));
1626 m3gZero(m3g, sizeof(*m3g));
1628 m3g->func.malloc = params->mallocFunc;
1629 m3g->func.free = params->freeFunc;
1631 if (params->objAllocFunc) {
1632 m3g->func.objAlloc = params->objAllocFunc;
1633 m3g->func.objFree = params->objFreeFunc;
1634 m3g->func.objResolve = params->objResolveFunc;
1637 m3g->func.objAlloc = (m3gObjectAllocator*)(m3g->func.malloc);
1638 m3g->func.objFree = (m3gObjectDeallocator*)(m3g->func.free);
1639 m3g->func.objResolve = defaultResolver;
1642 m3g->func.error = params->errorFunc;
1643 m3g->func.getFrameBuffer = params->beginRenderFunc;
1644 m3g->func.releaseFrameBuffer = params->endRenderFunc;
1646 m3g->userContext = params->userContext;
1648 /* Initialize memory allocation failure debugging */
1649 # if defined(M3G_DEBUG_OUT_OF_MEMORY)
1653 # define M3G_GETENV(name, field) \
1654 str = getenv(name); \
1656 m3g-> ## field = atoi(str); \
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);
1668 # if !defined(M3G_NGL_CONTEXT_API)
1669 /* Before messing with EGL state, check if EGL is already
1670 * initialized by the calling application. */
1672 if (eglQueryString(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_VERSION)) {
1675 # endif /*!M3G_NGL_CONTEXT_API*/
1677 /* Dig some constants from the GL implementation */
1679 m3gConfigureGL(m3g);
1681 /* All done! Now we can allocate the more trival stuff */
1683 m3g->tcache = m3gCreateTransformCache(m3g);
1684 m3gInitArray(&m3g->objects);
1686 M3G_LOG1(M3G_LOG_INTERFACE,
1687 "Interface 0x%08X initialized\n", (unsigned) m3g);
1688 return (M3GInterface) m3g;
1693 * \brief Deletes an M3G interface and all associated objects
1695 M3G_API void m3gDeleteInterface(M3GInterface interface)
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);
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 */
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;
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) */
1719 m3gDestroyArray(&m3g->deadGLObjects, m3g);
1722 /* Delete temp buffers and caches */
1724 M3G_ASSERT(!m3g->tempLocked);
1725 m3gFree(m3g, m3g->tempBuffer);
1727 m3gDeleteTransformCache(m3g->tcache);
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);
1741 if (m3g->objAllocCount != 0) {
1742 M3G_LOG1(M3G_LOG_FATAL_ERRORS,
1743 "\t%d memory objects\n", m3g->objAllocCount);
1746 # endif /* M3G_DEBUG */
1748 /* Cleanup profiling resources */
1749 m3gCleanupProfile();
1750 m3gLogMemoryPeakCounter(m3g);
1754 m3gFreeFunc *freeFunc = m3g->func.free;
1756 (*freeFunc)(PHYSICAL_BLOCK(m3g));
1759 M3G_LOG1(M3G_LOG_INTERFACE,
1760 "Interface 0x%08X destroyed\n", (unsigned) m3g);
1762 /* Allow for log cleanup */
1764 # if defined(M3G_LOGLEVEL)
1770 * \brief Returns the latest error that occurred on an M3G interface
1772 * Returns the latest error for \c interface, and resets the error
1773 * status to M3G_NO_ERROR.
1775 * @param interface handle of the interface to query for errors
1777 M3G_API M3Genum m3gGetError(M3GInterface interface)
1779 Interface *m3g = (Interface *)interface;
1780 M3G_VALIDATE_INTERFACE(m3g);
1782 M3Genum error = m3g->error;
1783 m3g->error = M3G_NO_ERROR;
1789 * \brief Returns the user context data pointer associated with an M3G interface
1791 * User context data can be associated with an interface via the
1792 * M3GParams struct in m3gCreateInterface.
1794 * @param interface handle of the interface
1795 * @return pointer to the user context
1797 M3G_API void *m3gGetUserContext(M3GInterface interface)
1799 Interface *m3g = (Interface *)interface;
1800 M3G_VALIDATE_INTERFACE(m3g);
1801 return m3g->userContext;
1805 * \brief Returns if antialiasing is supported
1807 * User context data can be associated with an interface via the
1808 * M3GParams struct in m3gCreateInterface.
1810 * @param interface handle of the interface
1811 * @return pointer to the user context
1813 M3G_API M3Gbool m3gIsAntialiasingSupported(M3GInterface interface)
1815 Interface *m3g = (Interface *)interface;
1816 M3G_VALIDATE_INTERFACE(m3g);
1817 return m3g->supportAntialiasing;
1821 * \brief Free memory by removing all detached objects
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.
1828 M3G_API void m3gGarbageCollect(M3GInterface interface)
1830 Interface *m3g = (Interface *) interface;
1831 M3G_VALIDATE_INTERFACE(m3g);
1832 m3gGarbageCollectAll(m3g);
1836 * \brief Returns the transformation cache for this interface
1838 static TCache *m3gGetTransformCache(Interface *m3g)
1844 * \brief Returns the value of a given statistic
1846 * If the statistic is a counter (such as number of rendering calls),
1847 * its value is also cleared.
1849 * \note Dependent on the M3G_ENABLE_PROFILING compile-time flag; if
1850 * undefined, all statistic queries return zero
1852 #if defined(M3G_ENABLE_PROFILING)
1853 /*@access M3GInterface@*/
1854 M3G_API M3Gint m3gGetStatistic(M3GInterface hInterface, M3Gstatistic stat)
1856 Interface *m3g = (Interface*) hInterface;
1857 M3G_VALIDATE_INTERFACE(m3g);
1859 if (m3gInRange(stat, 0, M3G_STAT_MAX-1)) {
1860 M3Gint value = m3g->statistics[stat];
1862 if (stat < M3G_STAT_CUMULATIVE) {
1863 m3g->statistics[stat] = 0;
1869 m3gRaiseError(m3g, M3G_INVALID_ENUM);
1874 M3G_API M3Gint m3gGetStatistic(M3GInterface hInterface, M3Gstatistic stat)
1876 M3G_UNREF(hInterface);
1880 #endif /*M3G_ENABLE_PROFILING*/
1883 #undef INSTRUMENTATED_SIZE
1884 #undef PAYLOAD_BLOCK
1885 #undef PHYSICAL_BLOCK