/******************************Module*Header*******************************\ * Module Name: dl_list.c * * Display list management rountines. * * Copyright (c) 1995-96 Microsoft Corporation \**************************************************************************/ /* ** Copyright 1991-1993, Silicon Graphics, Inc. ** All Rights Reserved. ** ** This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.; ** the contents of this file may not be disclosed to third parties, copied or ** duplicated in any form, in whole or in part, without the prior written ** permission of Silicon Graphics, Inc. ** ** RESTRICTED RIGHTS LEGEND: ** Use, duplication or disclosure by the Government is subject to restrictions ** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data ** and Computer Software clause at DFARS 252.227-7013, and/or in similar or ** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - ** rights reserved under the Copyright Laws of the United States. ** ** Basic display list routines. ** */ #include "precomp.h" #pragma hdrstop extern GLCLTPROCTABLE ListCompCltProcTable; extern GLEXTPROCTABLE ListCompExtProcTable; __GLdlist *__glShrinkDlist(__GLcontext *gc, __GLdlist *dlist); // #define DL_HEAP_VERBOSE #ifdef DL_HEAP_VERBOSE int cbDlistTotal = 0; extern ULONG glSize; #ifdef DBG #define GL_MSIZE(pv) _msize((BYTE *)(pv)-16) #else #define GL_MSIZE(pv) _msize(pv) #endif #endif #if defined(DL_BLOCK_VERBOSE) || defined(DL_HEAP_VERBOSE) #include "malloc.h" #endif /* ** Arbitrary limit for looking up multiple display lists at once ** (with glCallLists()). Any number from 128 to 1024 should work well. ** This value doesn't change the functionality of OpenGL at all, but ** will make minor variations to the performance characteristics. */ #define MAX_LISTS_CACHE 256 const GLubyte __GLdlsize_tab[] = { /* GL_BYTE */ 1, /* GL_UNSIGNED_BYTE */ 1, /* GL_SHORT */ 2, /* GL_UNSIGNED_SHORT */ 2, /* GL_INT */ 4, /* GL_UNSIGNED_INT */ 4, /* GL_FLOAT */ 4, /* GL_2_BYTES */ 2, /* GL_3_BYTES */ 3, /* GL_4_BYTES */ 4, }; #define __glCallListsSize(type) \ ((type) >= GL_BYTE && (type) <= GL_4_BYTES ? \ __GLdlsize_tab[(type)-GL_BYTE] : -1) #define DL_LINK_SIZE (sizeof(__GLlistExecFunc *)+sizeof(GLubyte *)) #define DL_TERMINATOR_SIZE sizeof(GLubyte *) #define DL_OVERHEAD (offsetof(__GLdlist, head)+DL_LINK_SIZE+\ DL_TERMINATOR_SIZE) // This value should be a power of two #define DL_BLOCK_SIZE (256 * 1024) // This value is chosen specifically to give the initial total size // of the dlist an even block size #define DL_INITIAL_SIZE (DL_BLOCK_SIZE-DL_OVERHEAD) // Skip to the next block in the display list block chain const GLubyte * FASTCALL __glle_NextBlock(__GLcontext *gc, const GLubyte *PC) { #ifdef DL_BLOCK_VERBOSE DbgPrint("NextBlock: %08lX\n", *(const GLubyte * UNALIGNED64 *)PC); #endif return *(const GLubyte * UNALIGNED64 *)PC; } /* ** Used to pad display list entries to double word boundaries where needed ** (for those few OpenGL commands which take double precision values). */ const GLubyte * FASTCALL __glle_Nop(__GLcontext *gc, const GLubyte *PC) { return PC; } void APIENTRY glcltNewList ( IN GLuint list, IN GLenum mode ) { __GLdlistMachine *dlstate; __GL_SETUP(); // Must use the client side begin state if (gc->paTeb->flags & POLYARRAY_IN_BEGIN) { GLSETERROR(GL_INVALID_OPERATION); return; } dlstate = &gc->dlist; /* Valid mode? */ switch(mode) { case GL_COMPILE: case GL_COMPILE_AND_EXECUTE: break; default: GLSETERROR(GL_INVALID_ENUM); return; } if (dlstate->currentList) { /* Must call EndList before calling NewList again! */ GLSETERROR(GL_INVALID_OPERATION); return; } if (list == 0) { GLSETERROR(GL_INVALID_VALUE); return; } // If we are in COMPILE mode, we need to clear the command buffer, // the poly array buffer, and the poly material buffer so that we // can use them to compile poly array. Otherwise, previously batched // commands may be lost. if (mode == GL_COMPILE) glsbAttention(); ASSERTOPENGL((DL_BLOCK_SIZE & (DL_BLOCK_SIZE-1)) == 0, "DL_BLOCK_SIZE is not a power of two\n"); ASSERTOPENGL(dlstate->listData == NULL, "listData non-NULL in NewList\n"); dlstate->listData = __glAllocDlist(gc, DL_INITIAL_SIZE); if (dlstate->listData == NULL) { GLSETERROR(GL_OUT_OF_MEMORY); return; } /* ** Save current client dispatch pointers into saved state in context. Then ** switch to the list tables. */ gc->savedCltProcTable.cEntries = ListCompCltProcTable.cEntries; gc->savedExtProcTable.cEntries = ListCompExtProcTable.cEntries; GetCltProcTable(&gc->savedCltProcTable, &gc->savedExtProcTable, FALSE); SetCltProcTable(&ListCompCltProcTable, &ListCompExtProcTable, FALSE); dlstate->currentList = list; dlstate->mode = mode; dlstate->nesting = 0; #if 0 dlstate->drawBuffer = GL_FALSE; #endif dlstate->beginRec = NULL; (*dlstate->initState)(gc); } void APIENTRY glcltEndList ( void ) { __GLdlistMachine *dlstate; __GLdlist *dlist; __GLdlist *newDlist; __GLdlist *prevDlist; GLubyte *allEnd; GLubyte *data; GLuint totalSize; GLuint currentList; POLYARRAY *pa; __GL_SETUP(); pa = gc->paTeb; dlstate = &gc->dlist; /* Must call NewList() first! */ if (dlstate->currentList == 0) { GLSETERROR(GL_INVALID_OPERATION); return; } // In COMPILE_AND_EXECUTE mode, EndList must not be called in Begin. // In COMPILE mode, however, this flag should be clear (enforced in NewList) // unless it was set in the poly array compilation code. if (dlstate->mode == GL_COMPILE_AND_EXECUTE && pa->flags & POLYARRAY_IN_BEGIN) { GLSETERROR(GL_INVALID_OPERATION); return; } // If we are in the middle of compiling poly array, end the poly array // compilation. if (gc->dlist.beginRec) { ASSERTOPENGL(pa->flags & POLYARRAY_IN_BEGIN, "not in begin!\n"); gc->dlist.beginRec->flags |= DLIST_BEGIN_NO_MATCHING_END; // Record the last POLYDATA since it may contain attribute changes. __glDlistCompilePolyData(gc, GL_TRUE); // Terminate poly array compilation gc->dlist.beginRec = NULL; } // If we are in COMPILE mode, we need to reset the command buffer, // the poly array buffer, and the poly material buffer. if (gc->dlist.mode == GL_COMPILE) { glsbResetBuffers(gc->dlist.beginRec ? TRUE : FALSE); // Clear begin flag too pa->flags &= ~POLYARRAY_IN_BEGIN; } dlist = dlstate->listData; #if 0 // Copy over the DrawBuffer flag dlist->drawBuffer = dlstate->drawBuffer; #endif // Shrink this block to remove wasted space dlist = __glShrinkDlist(gc, dlist); // Remember the true end of the list allEnd = dlist->head+dlist->used; // Reverse the order of the list prevDlist = NULL; while (dlist->nextBlock != NULL) { newDlist = dlist->nextBlock; dlist->nextBlock = prevDlist; prevDlist = dlist; dlist = newDlist; } dlist->nextBlock = prevDlist; // Set the end pointer correctly dlist->end = allEnd; // Mark the end of the display list data with 0: *((DWORD *)dlist->end) = 0; dlstate->listData = NULL; currentList = dlstate->currentList; dlstate->currentList = 0; #ifdef DL_HEAP_VERBOSE DbgPrint("Dlists using %8d, total %8d\n", cbDlistTotal, glSize); #endif #ifdef DL_BLOCK_VERBOSE DbgPrint("List %d: start %08lX, end %08lX\n", currentList, dlist->head, dlist->end); DbgPrint("Blocks at:"); newDlist = dlist; while (newDlist != NULL) { DbgPrint(" %08lX:%d", newDlist, GL_MSIZE(newDlist)); newDlist = newDlist->nextBlock; } DbgPrint("\n"); #endif // __glNamesNewData sets dlist refcount to 1. if (!__glNamesNewData(gc, gc->dlist.namesArray, currentList, dlist)) { /* ** No memory! ** Nuke the list! */ __glFreeDlist(gc, dlist); } /* Switch back to saved dispatch state */ SetCltProcTable(&gc->savedCltProcTable, &gc->savedExtProcTable, FALSE); } #ifdef NT_SERVER_SHARE_LISTS /******************************Public*Routine******************************\ * * DlLockLists * * Remember the locked lists for possible later cleanup * * History: * Mon Dec 12 18:58:32 1994 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ // Number of locks to allocate when the lock list needs to grow // Must be a power of two #define DL_LOCK_LIST_BLOCK 32 GLboolean DlLockLists(__GLcontext *gc, GLsizei n, __GLdlist **dlists) { DlLockArray *pdla; DlLockEntry *pdle; GLsizei nNewSize; pdla = &gc->dla; // Extend current lock array if needed if (pdla->nAllocated-pdla->nFilled < n) { // Round the needed size up to the block size nNewSize = (pdla->nAllocated+n+DL_LOCK_LIST_BLOCK-1) & ~(DL_LOCK_LIST_BLOCK-1); pdle = GCREALLOC(gc, pdla->pdleEntries, sizeof(DlLockEntry)*nNewSize); if (pdle == NULL) { return 0; } pdla->nAllocated = nNewSize; pdla->pdleEntries = pdle; } // We must have enough space now ASSERTOPENGL(pdla->nAllocated-pdla->nFilled >= n, "no enough space!\n"); // Lock down dlists and remember them pdle = pdla->pdleEntries+pdla->nFilled; pdla->nFilled += n; while (n-- > 0) { pdle->dlist = *dlists; DBGLEVEL3(LEVEL_INFO, "Locked %p for %p, ref %d\n", *dlists, gc, (*dlists)->refcount); dlists++; pdle++; } return (GLboolean) (pdla->nFilled != 0); // return high water mark } /******************************Public*Routine******************************\ * * DlUnlockLists * * Remove list lock entries. * * History: * Mon Dec 12 18:58:54 1994 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void DlUnlockLists(__GLcontext *gc, GLsizei n) { DlLockArray *pdla; DlLockEntry *pdle; GLsizei i; __GLdlist *dlist; // Since DlLockLists and DlUnlockLists are called in a recursive manner, // we can simply decrement the filled count. pdla = &gc->dla; pdla->nFilled -= n; // Lock list doesn't shrink. This would be fairly easy since realloc // is guaranteed not to fail when the memory block shrinks // Is this important? } /******************************Public*Routine******************************\ * * DlReleaseLocks * * Releases any locks in the lock list and frees the lock list * * Must be executed under the dlist semaphore * * History: * Tue Dec 13 11:45:26 1994 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void DlReleaseLocks(__GLcontext *gc) { DlLockArray *pdla; DlLockEntry *pdle; __GL_NAMES_ASSERT_LOCKED(gc->dlist.namesArray); pdla = &gc->dla; DBGLEVEL3(LEVEL_INFO, "Cleaning up %p, locks %d (%d)\n", gc, pdla->nFilled, pdla->nAllocated); // Sanity check the counts ASSERTOPENGL(pdla->nFilled <= pdla->nAllocated, "bad nFilled!\n"); pdle = pdla->pdleEntries; while (pdla->nFilled) { pdla->nFilled--; // This function is called to clean up display list locks held by // glCallList or glCallLists when it dies. We need to release the // locks here and free the dlists if their refcounts reach 0. // The refcounts will reach 0 here only when the dlists were deleted // by another thread while this thread was also holding the locks. __glDisposeDlist(gc, pdle->dlist); pdle++; } pdla->nAllocated = 0; if (pdla->pdleEntries) { GCFREE(gc, pdla->pdleEntries); } } #endif // NT_SERVER_SIDE // If the a dlist was deleted by another thread while we have it locked, // we need to free the dlist here. void FASTCALL DlCleanup(__GLcontext *gc, void *pData) { __glFreeDlist(gc, (__GLdlist *)pData); } void FASTCALL DoCallList(GLuint list) { __GLdlist *dlist; __GLdlistMachine *dlstate; const GLubyte *end, *PC; __GLlistExecFunc *fp; __GL_SETUP(); dlstate = &gc->dlist; if (dlstate->nesting >= __GL_MAX_LIST_NESTING) { /* Force unwinding of the display list */ dlstate->nesting = __GL_MAX_LIST_NESTING*2; return; } /* Increment dlist refcount */ dlist = __glNamesLockData(gc, gc->dlist.namesArray, list); /* No list, no action! */ if (!dlist) { return; } #ifdef NT_SERVER_SHARE_LISTS if (!DlLockLists(gc, 1, &dlist)) { /* Decrement dlist refcount */ __glNamesUnlockData(gc, (void *)dlist, DlCleanup); GLSETERROR(GL_OUT_OF_MEMORY); return; } #endif dlstate->nesting++; end = dlist->end; PC = dlist->head; while (PC != end) { // Get the current function pointer. fp = *((__GLlistExecFunc * const UNALIGNED64 *) PC); // Execute the current function. Return value is pointer to // next function/parameter block in the display list. PC = (*fp)(gc, PC+sizeof(__GLlistExecFunc * const *)); } dlstate->nesting--; /* Decrement dlist refcount */ // Will perform cleanup if necessary __glNamesUnlockData(gc, (void *)dlist, DlCleanup); #ifdef NT_SERVER_SHARE_LISTS DlUnlockLists(gc, 1); #endif } /* ** Display list compilation and execution versions of CallList and CallLists ** are maintained here for the sake of sanity. Note that __glle_CallList ** may not call glcltCallList or it will break the infinite recursive ** display list prevention code. */ void APIENTRY __gllc_CallList ( IN GLuint list ) { struct __gllc_CallList_Rec *data; __GL_SETUP(); if (list == 0) { __gllc_InvalidValue(); return; } // It is extremely difficult to make CallList(s) work with poly array // compilation. For example, in the call sequence in COMPILE_AND_EXECUTE // mode [Begin, TexCoord, CallList, Vertex, ...], it is difficult to record // the partial POLYDATA in both COMPILE and COMPILE_AND_EXECUTE modes. // That is, we may end up recording and playing back TexCoord twice in the // above example. As a result, we may have to stop building poly array in // some cases. Fortunately, this situation is rare. if (gc->dlist.beginRec) { gc->dlist.beginRec->flags |= DLIST_BEGIN_HAS_CALLLIST; // Record the last POLYDATA since it may contain attribute changes. __glDlistCompilePolyData(gc, GL_TRUE); } data = (struct __gllc_CallList_Rec *) __glDlistAddOpUnaligned(gc, DLIST_SIZE(sizeof(struct __gllc_CallList_Rec)), DLIST_GENERIC_OP(CallList)); if (data == NULL) return; data->list = list; __glDlistAppendOp(gc, data, __glle_CallList); if (gc->dlist.beginRec) { POLYARRAY *pa; pa = gc->paTeb; // In COMPILE_AND_EXECUTE mode, we can actually get out of the Begin mode. // Although it is an application error, we need to terminate poly array // compilation! if (!(pa->flags & POLYARRAY_IN_BEGIN)) gc->dlist.beginRec = NULL; else { // If there is a partial vertex record after CallList(s), we will terminate // the poly array compilation. Otherwise, it is safe to continue the // processing. if (pa->pdNextVertex->flags) { // Terminate poly array compilation gc->dlist.beginRec = NULL; if (gc->dlist.mode == GL_COMPILE) { glsbResetBuffers(TRUE); // Clear begin flag too pa->flags &= ~POLYARRAY_IN_BEGIN; } } } } } const GLubyte * FASTCALL __glle_CallList(__GLcontext *gc, const GLubyte *PC) { struct __gllc_CallList_Rec *data; data = (struct __gllc_CallList_Rec *) PC; DoCallList(data->list); return PC + sizeof(struct __gllc_CallList_Rec); } void APIENTRY glcltCallList ( IN GLuint list ) { __GL_SETUP(); if (list == 0) { GLSETERROR(GL_INVALID_VALUE); return; } gc->dlist.nesting = 0; DoCallList(list); } void FASTCALL DoCallLists(GLsizei n, GLenum type, const GLvoid *lists) { __GLdlist *dlists[MAX_LISTS_CACHE]; __GLdlist *dlist; __GLdlistMachine *dlstate; GLint i, dlcount, datasize; const GLubyte *listiter; const GLubyte *end, *PC; __GLlistExecFunc *fp; __GL_SETUP(); dlstate = &gc->dlist; datasize = __glCallListsSize(type); if (dlstate->nesting >= __GL_MAX_LIST_NESTING) { /* Force unwinding of the display list */ dlstate->nesting = __GL_MAX_LIST_NESTING*2; return; } dlstate->nesting++; listiter = (const GLubyte *) lists; while (n) { dlcount = n; if (dlcount > MAX_LISTS_CACHE) dlcount = MAX_LISTS_CACHE; #ifdef NT_SERVER_SHARE_LISTS // Is there anything we can do here in the failure case besides // just skip the lists? This is more or less consistent // with the behavior for not-found lists /* Increment dlist refcount */ __glNamesLockDataList(gc, gc->dlist.namesArray, dlcount, type, gc->state.list.listBase, (const GLvoid *) listiter, (void **)dlists); if (!DlLockLists(gc, dlcount, dlists)) { /* Decrement dlist refcount */ __glNamesUnlockDataList(gc, dlcount, (void **)dlists, DlCleanup); GLSETERROR(GL_OUT_OF_MEMORY); } else { #else __glNamesLockDataList(gc, gc->dlist.namesArray, dlcount, type, gc->state.list.listBase, (const GLvoid *) listiter, (void **)dlists); #endif i = 0; while (i < dlcount) { dlist = dlists[i]; end = dlist->end; PC = dlist->head; while (PC != end) { // Get the current function pointer. fp = *((__GLlistExecFunc * const UNALIGNED64 *) PC); // Execute the current function. Return value is pointer to // next function/parameter block in the display list. PC = (*fp)(gc, PC+sizeof(__GLlistExecFunc * const *)); } i++; } /* Decrement dlist refcount */ // Will perform cleanup if necessary __glNamesUnlockDataList(gc, dlcount, (void **)dlists, DlCleanup); #ifdef NT_SERVER_SHARE_LISTS DlUnlockLists(gc, dlcount); } #endif listiter += dlcount * datasize; n -= dlcount; } dlstate->nesting--; } /* ** Display list compilation and execution versions of CallList and CallLists ** are maintained here for the sake of sanity. Note that __glle_CallLists ** may not call glcltCallLists or it will break the infinite recursive ** display list prevention code. */ void APIENTRY __gllc_CallLists ( IN GLsizei n, IN GLenum type, IN const GLvoid *lists ) { GLuint size; GLint arraySize; struct __gllc_CallLists_Rec *data; __GL_SETUP(); if (n < 0) { __gllc_InvalidValue(); return; } else if (n == 0) { return; } // It is extremely difficult to make CallList(s) work with poly array // compilation. For example, in the call sequence in COMPILE_AND_EXECUTE // mode [Begin, TexCoord, CallList, Vertex, ...], it is difficult to record // the partial POLYDATA in both COMPILE and COMPILE_AND_EXECUTE modes. // That is, we may end up recording and playing back TexCoord twice in the // above example. As a result, we may have to stop building poly array in // some cases. Fortunately, this situation is rare. if (gc->dlist.beginRec) { gc->dlist.beginRec->flags |= DLIST_BEGIN_HAS_CALLLIST; // Record the last POLYDATA since it may contain attribute changes. __glDlistCompilePolyData(gc, GL_TRUE); } arraySize = __glCallListsSize(type)*n; if (arraySize < 0) { __gllc_InvalidEnum(); return; } #ifdef NT size = sizeof(struct __gllc_CallLists_Rec) + __GL_PAD(arraySize); #else arraySize = __GL_PAD(arraySize); size = sizeof(struct __gllc_CallLists_Rec) + arraySize; #endif data = (struct __gllc_CallLists_Rec *) __glDlistAddOpUnaligned(gc, DLIST_SIZE(size), DLIST_GENERIC_OP(CallLists)); if (data == NULL) return; data->n = n; data->type = type; __GL_MEMCOPY((GLubyte *)data + sizeof(struct __gllc_CallLists_Rec), lists, arraySize); __glDlistAppendOp(gc, data, __glle_CallLists); if (gc->dlist.beginRec) { POLYARRAY *pa; pa = gc->paTeb; // In COMPILE_AND_EXECUTE mode, we can actually get out of the Begin mode. // Although it is an application error, we need to terminate poly array // compilation! if (!(pa->flags & POLYARRAY_IN_BEGIN)) gc->dlist.beginRec = NULL; else { // If there is a partial vertex record after CallList(s), we will terminate // the poly array compilation. Otherwise, it is safe to continue the // processing. if (pa->pdNextVertex->flags) { // Terminate poly array compilation gc->dlist.beginRec = NULL; if (gc->dlist.mode == GL_COMPILE) { glsbResetBuffers(TRUE); // Clear begin flag too pa->flags &= ~POLYARRAY_IN_BEGIN; } } } } } const GLubyte * FASTCALL __glle_CallLists(__GLcontext *gc, const GLubyte *PC) { GLuint size; GLuint arraySize; struct __gllc_CallLists_Rec *data; data = (struct __gllc_CallLists_Rec *) PC; DoCallLists(data->n, data->type, (GLvoid *) (data+1)); arraySize = __GL_PAD(__glCallListsSize(data->type)*data->n); size = sizeof(struct __gllc_CallLists_Rec) + arraySize; return PC + size; } void APIENTRY glcltCallLists ( IN GLsizei n, IN GLenum type, IN const GLvoid *lists ) { __GL_SETUP(); if (n < 0) { GLSETERROR(GL_INVALID_VALUE); return; } else if (n == 0) { return; } if ((GLint) __glCallListsSize(type) < 0) { GLSETERROR(GL_INVALID_ENUM); return; } gc->dlist.nesting = 0; DoCallLists(n, type, lists); } /************************************************************************/ // Expand a dlist __GLdlist *__glDlistGrow(GLuint size) { __GLdlist *dlist, *newDlist; GLubyte * UNALIGNED64 *op; __GL_SETUP(); newDlist = __glAllocDlist(gc, size); if (newDlist == NULL) { GLSETERROR(GL_OUT_OF_MEMORY); return NULL; } // Add on record to link old block to new block dlist = gc->dlist.listData; op = (GLubyte **)(dlist->head+dlist->used); *(__GLlistExecFunc * UNALIGNED64 *)op = __glle_NextBlock; *(op+1) = newDlist->head; // Shrink old block down to remove any wasted space at the end of it dlist = __glShrinkDlist(gc, dlist); // Link new block into chain newDlist->nextBlock = dlist; gc->dlist.listData = newDlist; return newDlist; } // Shrink a dlist block down to the minimum size // Guaranteed not to fail since we can always just use the overly // large block if the realloc fails // NOTE: This function should only be used during build time // where the nextBlock links are in the opposite direction of // the __glle_NextBlock link record links __GLdlist *__glShrinkDlist(__GLcontext *gc, __GLdlist *dlist) { __GLdlist *newDlist, *prevDlist; // If the amount of unused space is small, don't bother shrinking the block. if (dlist->size - dlist->used < 4096) return dlist; // If it is in COMPILE_AND_EXECUTE mode, flush the command buffer before // reallocating listData. Shrinking listData may invalidate the memory // pointers placed in the command buffer by the the display list execution // code. When we are in the middle of building POLYARRAY, glsbAttention // will not flush commands batched before the Begin call. As a result, // we also need to flush the command buffer before compiling the Begin call. if (gc->dlist.mode == GL_COMPILE_AND_EXECUTE) glsbAttention(); #ifdef DL_HEAP_VERBOSE cbDlistTotal -= GL_MSIZE(dlist); #endif newDlist = (__GLdlist *)GCREALLOC(gc, dlist, dlist->used+DL_OVERHEAD); // If the realloc fails, just use the original list if (newDlist != NULL) { // If the realloc moved the block, fix up the link from the // previous block. This should be relatively rare if (newDlist != dlist && newDlist->nextBlock != NULL) { prevDlist = newDlist->nextBlock; ASSERTOPENGL(*(__GLlistExecFunc * UNALIGNED64 *) (prevDlist->head+prevDlist->used) == __glle_NextBlock, "Link not found where expected\n"); *(GLubyte * UNALIGNED64 *)(prevDlist->head+prevDlist->used+ sizeof(__GLlistExecFunc *)) = newDlist->head; } // If we are compiling the poly array record, we need to fix up // the Begin pointer! Note that if beginRec is not in the moved // block, the pointer does not change! if (newDlist != dlist && gc->dlist.beginRec && (GLubyte *) gc->dlist.beginRec >= dlist->head && (GLubyte *) gc->dlist.beginRec <= dlist->head + dlist->used) { gc->dlist.beginRec += newDlist->head - dlist->head; } dlist = newDlist; dlist->size = dlist->used; } #ifdef DL_HEAP_VERBOSE cbDlistTotal += GL_MSIZE(dlist); #endif return dlist; } __GLdlist *__glAllocDlist(__GLcontext *gc, GLuint size) { __GLdlist *dlist; __GLdlist temp; GLuint memsize; // Add on overhead and round size to an even block memsize = (size+DL_OVERHEAD+DL_BLOCK_SIZE-1) & ~(DL_BLOCK_SIZE-1); // Check overflow if (memsize < size) return NULL; size = memsize-DL_OVERHEAD; dlist = (__GLdlist *)GCALLOC(gc, memsize); if (dlist == NULL) return NULL; #if 0 // NT_SERVER_SHARE_LISTS dlist->refcount = 1; #else // refcount is set to 1 in __glNamesNewData. dlist->refcount = 0; #endif dlist->size = size; dlist->used = 0; dlist->nextBlock = NULL; #ifdef DL_HEAP_VERBOSE cbDlistTotal += GL_MSIZE(dlist); #endif return dlist; } void FASTCALL __glFreeDlist(__GLcontext *gc, __GLdlist *dlist) { __GLdlist *dlistNext; #ifdef NT_SERVER_SHARE_LISTS if (dlist->refcount != 0) { WARNING2("dlist %p refcount on free is %d\n", dlist, dlist->refcount); } #endif while (dlist != NULL) { dlistNext = dlist->nextBlock; #ifdef DL_HEAP_VERBOSE cbDlistTotal -= GL_MSIZE(dlist); #endif GCFREE(gc, dlist); dlist = dlistNext; } #ifdef DL_HEAP_VERBOSE DbgPrint("Dlists using %8d, total %8d\n", cbDlistTotal, glSize); #endif }