/* $Id$ */
#include <float.h>
#include "cr_dlm.h"
#include "cr_mem.h"
#include "dlm.h"
/* This file defines the display list functions such as NewList, EndList,
* IsList, DeleteLists, etc.
* Generally, SPUs will call these as needed to implement display lists.
* See the expando, replicate, and tilesort SPUs for examples.
*
* The functions which compile GL functions into our display lists are named:
* void DLM_APIENTRY crdlm_<function name>
* where <function_name> is the Chromium function name (which in
* turn is the GL function name with the "gl" prefix removed).
*
* All these entry points require that a CRDLMContextState structure
* be created (with crDLMNewContext()) and assigned to the current
* thread (with crDLMSetCurrentState()).
*/
/*
* Begin compiling a list.
*/
void DLM_APIENTRY
crDLMNewList(GLuint listIdentifier, GLenum mode)
{
DLMListInfo *listInfo;
CRDLMContextState *listState = CURRENT_STATE();
/* Error checks: 0 is not a valid identifier, and
* we can't call NewList if NewList has been called
* more recently than EndList.
*
* The caller is expected to check for an improper
* mode parameter (GL_INVALID_ENUM), or for a NewList
* within a glBegin/glEnd (GL_INVALID_OPERATION).
*/
if (listState == NULL)
{
crWarning("DLM error: NewList(%d,%d) called with no current state (%s line %d)\n",
(int) listIdentifier, (int) mode, __FILE__, __LINE__);
return;
}
if (listIdentifier == 0)
{
crdlm_error(__LINE__, __FILE__, GL_INVALID_VALUE,
"NewList called with a list identifier of 0");
return;
}
if (listState->currentListInfo != NULL)
{
char msg[1000];
sprintf(msg, "NewList called with display list %d while display list %d was already open",
(int) listIdentifier, (int) listState->currentListIdentifier);
crdlm_error(__LINE__, __FILE__, GL_INVALID_OPERATION, msg);
return;
}
listInfo = (DLMListInfo *) crCalloc(sizeof(DLMListInfo));
if (!(listInfo))
{
char msg[1000];
sprintf(msg, "could not allocate %u bytes of memory in NewList",
(unsigned) sizeof(DLMListInfo));
crdlm_error(__LINE__, __FILE__, GL_OUT_OF_MEMORY, msg);
return;
}
listInfo->first = listInfo->last = NULL;
listInfo->stateFirst = listInfo->stateLast = NULL;
listInfo->references = crAllocHashtable();
if (!(listInfo->references))
{
crFree(listInfo);
crdlm_error(__LINE__, __FILE__, GL_OUT_OF_MEMORY,
"could not allocate memory in NewList");
return;
}
listInfo->numInstances = 0;
listInfo->listSent = GL_FALSE;
listInfo->bbox.xmin = FLT_MAX;
listInfo->bbox.xmax = -FLT_MAX;
listInfo->bbox.ymin = FLT_MAX;
listInfo->bbox.ymax = -FLT_MAX;
listInfo->bbox.zmin = FLT_MAX;
listInfo->bbox.zmax = -FLT_MAX;
listState->currentListInfo = listInfo;
listState->currentListIdentifier = listIdentifier;
listState->currentListMode = mode;
}
/* This small utility routine is used to traverse a buffer
* list, freeing each buffer. It is used to free the buffer
* list in the DLMListInfo structure, both when freeing the
* entire structure and when freeing just the retained content.
*/
static void free_instance_list(DLMInstanceList * instance)
{
while (instance)
{
DLMInstanceList *nextInstance = instance->next;
crFree(instance);
instance = nextInstance;
}
}
/* This utility routine frees a DLMListInfo structure and all
* of its components. It is used directly, when deleting a
* single list; it is also used as a callback function for
* hash tree operations (Free and Replace).
*
* The parameter is listed as a (void *) instead of a (DLMListInfo *)
* in order that the function can be used as a callback routine for
* the hash table functions. The (void *) designation causes no
* other harm, save disabling type-checking on the pointer argument
* of the function.
*/
void crdlm_free_list(void *parm)
{
DLMListInfo *listInfo = (DLMListInfo *) parm;
free_instance_list(listInfo->first);
listInfo->first = listInfo->last = NULL;
/* The references list has no allocated information; it's
* just a set of entries. So we don't need to free any
* information as each entry is deleted.
*/
crFreeHashtable(listInfo->references, NULL);
crFree(listInfo);
}
void DLM_APIENTRY crDLMEndList(void)
{
CRDLMContextState *listState = CURRENT_STATE();
/* Error check: cannot call EndList without a (successful)
* preceding NewList.
*
* The caller is expected to check for glNewList within
* a glBegin/glEnd sequence.
*/
if (listState == NULL)
{
crWarning("DLM error: EndList called with no current state (%s line %d)\n",
__FILE__, __LINE__);
return;
}
if (listState->currentListInfo == NULL)
{
crdlm_error(__LINE__, __FILE__, GL_INVALID_OPERATION,
"EndList called while no display list was open");
return;
}
DLM_LOCK(listState->dlm)
/* This function will either replace the list information that's
* already present with our new list information, freeing the
* former list information; or will add the new information
* to the set of display lists, depending on whether the
* list already exists or not.
*/
crHashtableReplace(listState->dlm->displayLists,
listState->currentListIdentifier,
listState->currentListInfo, crdlm_free_list);
DLM_UNLOCK(listState->dlm)
/* reset the current state to show the list had been ended */
listState->currentListIdentifier = 0;
listState->currentListInfo = NULL;
listState->currentListMode = GL_FALSE;
}
void DLM_APIENTRY crDLMDeleteLists(GLuint firstListIdentifier, GLsizei range)
{
CRDLMContextState *listState = CURRENT_STATE();
register int i;
if (listState == NULL)
{
crWarning
("DLM error: DeleteLists(%d,%d) called with no current state (%s line %d)\n",
(int) firstListIdentifier, (int) range, __FILE__, __LINE__);
return;
}
if (range < 0)
{
char msg[1000];
sprintf(msg, "DeleteLists called with range (%d) less than zero", (int) range);
crdlm_error(__LINE__, __FILE__, GL_INVALID_VALUE, msg);
return;
}
/* Interestingly, there doesn't seem to be an error for deleting
* display list 0, which cannot exist.
*
* We could delete the desired lists by walking the entire hash of
* display lists and looking for and deleting any in our range; or we
* could delete lists one by one. The former is faster if the hashing
* algorithm is inefficient or if we're deleting all or most of our
* lists; the latter is faster if we're deleting a relatively small
* number of lists.
*
* For now, we'll go with the latter; it's also easier to implement
* given the current functions available.
*/
DLM_LOCK(listState->dlm)
for (i = 0; i < range; i++)
{
crHashtableDelete(listState->dlm->displayLists,
firstListIdentifier + i, crdlm_free_list);
}
DLM_UNLOCK(listState->dlm)
}
GLboolean DLM_APIENTRY crDLMIsList(GLuint list)
{
CRDLMContextState *listState = CURRENT_STATE();
if (listState == NULL)
{
crWarning
("DLM error: IsLists(%d) called with no current state (%s line %d)\n",
(int) list, __FILE__, __LINE__);
return 0;
}
if (list == 0)
return GL_FALSE;
return crHashtableIsKeyUsed(listState->dlm->displayLists, list);
}
GLuint DLM_APIENTRY crDLMGenLists(GLsizei range)
{
CRDLMContextState *listState = CURRENT_STATE();
if (listState == NULL)
{
crWarning
("DLM error: GenLists(%d) called with no current state (%s line %d)\n",
(int) range, __FILE__, __LINE__);
return 0;
}
return crHashtableAllocKeys(listState->dlm->displayLists, range);
}
void DLM_APIENTRY crDLMListBase( GLuint base )
{
CRDLMContextState *listState = CURRENT_STATE();
if (listState == NULL)
{
crWarning
("DLM error: ListBase(%d) called with no current state (%s line %d)\n",
(int) base, __FILE__, __LINE__);
return;
}
listState->listBase = base;
}