Windows-Server-2003/multimedia/opengl/dlist/dl_list.c

1011 lines
26 KiB
C
Raw Permalink Normal View History

2024-08-04 01:28:15 +02:00
/******************************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
}