summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostServices/SharedOpenGL/dlm/dlm.c')
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c
new file mode 100644
index 00000000..8ea6c69c
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c
@@ -0,0 +1,575 @@
+/* $Id: dlm.c $ */
+
+#include <float.h>
+#include "cr_dlm.h"
+#include "cr_mem.h"
+#include "dlm.h"
+
+/**
+ * \mainpage Dlm
+ *
+ * \section DlmIntroduction Introduction
+ *
+ * Chromium consists of all the top-level files in the cr
+ * directory. The dlm module basically takes care of API dispatch,
+ * and OpenGL state management.
+ *
+ */
+
+/**
+ * Module globals: the current DLM state, bound either to each thread, or
+ * to a global.
+ */
+#ifdef CHROMIUM_THREADSAFE
+CRtsd CRDLMTSDKey;
+#else
+CRDLMContextState *CRDLMCurrentState = NULL;
+#endif
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+
+/*************************************************************************/
+
+#ifdef CHROMIUM_THREADSAFE
+/**
+ * This is the thread-specific destructor function for the
+ * data used in the DLM. It's very simple: if a thread exits
+ * that has DLM-specific data, the data represents the listState
+ * for the thread. All data and buffers associated with the list
+ * can be deleted, and the structure itself can be freed.
+ *
+ * Most Chromium threads don't have such things; but then,
+ * if a thread dies elsewhere in Chromium, huge buffers
+ * of information won't still be floating around in
+ * unrecoverable allocated areas, either.
+ */
+static void threadDestructor(void *tsd)
+{
+ CRDLMContextState *listState = (CRDLMContextState *)tsd;
+
+ if (listState)
+ {
+ //if (listState->currentListInfo)
+ // crdlm_free_list(listState->currentListInfo);
+
+ crFree(listState);
+ }
+}
+#endif
+
+/**
+ * This function creates and initializes a new display list
+ * manager. It returns a pointer to the manager, or NULL in
+ * the case of insufficient memory. The dispatch table pointer
+ * is passed in to allow the utilities to muck with the table
+ * to gain functional control when GL calls are made.
+ */
+CRDLM DLM_APIENTRY *crDLMNewDLM(unsigned int userConfigSize, const CRDLMConfig *userConfig)
+{
+ CRDLM *dlm;
+
+ /* This is the default configuration. We'll overwrite it later
+ * with user-supplied configuration information.
+ */
+ CRDLMConfig config = {
+ CRDLM_DEFAULT_BUFFERSIZE,
+ };
+
+ dlm = crAlloc(sizeof(*dlm));
+ if (!dlm) {
+ return NULL;
+ }
+
+ /* Start off by initializing all entries that require further
+ * memory allocation, so we can free up all the memory if there's
+ * a problem.
+ */
+ if (!(dlm->displayLists = crAllocHashtable())) {
+ crFree(dlm);
+ return NULL;
+ }
+
+ /* The creator counts as the first user. */
+ dlm->userCount = 1;
+
+#ifdef CHROMIUM_THREADSAFE
+ /* This mutex ensures that only one thread is changing the displayLists
+ * hash at a time. Note that we may also need a mutex to guarantee that
+ * the hash is not changed by one thread while another thread is
+ * traversing it; this issue has not yet been resolved.
+ */
+ crInitMutex(&(dlm->dlMutex));
+
+ /* Although the thread-specific data (TSD) functions will initialize
+ * the thread key themselves when needed, those functions do not allow
+ * us to specify a thread destructor. Since a thread could potentially
+ * exit with considerable memory allocated (e.g. if a thread exits
+ * after it has issued NewList but before EndList, and while there
+ * are considerable content buffers allocated), I do the initialization
+ * myself, in order to be able to reclaim those resources if a thread
+ * exits.
+ */
+ crInitTSDF(&(dlm->tsdKey), threadDestructor);
+ crInitTSD(&CRDLMTSDKey);
+#endif
+
+ /* Copy over any appropriate configuration values */
+ if (userConfig != NULL) {
+ /* Copy over as much configuration information as is provided.
+ * Note that if the CRDLMConfig structure strictly grows, this
+ * allows forward compatability - routines compiled with
+ * older versions of the structure will only initialize that
+ * section of the structure that they know about.
+ */
+ crMemcpy((void *)&config, (void *) userConfig,
+ MIN(userConfigSize, sizeof(config)));
+ }
+ dlm->bufferSize = config.bufferSize;
+
+ /* Return the pointer to the newly-allocated display list manager */
+ return dlm;
+}
+
+void DLM_APIENTRY crDLMUseDLM(CRDLM *dlm)
+{
+ DLM_LOCK(dlm);
+ dlm->userCount++;
+ DLM_UNLOCK(dlm);
+}
+
+/**
+ * This routine is called when a context or thread is done with a DLM.
+ * It maintains an internal count of users, and will only actually destroy
+ * itself when no one is still using the DLM.
+ */
+void DLM_APIENTRY crDLMFreeDLM(CRDLM *dlm, SPUDispatchTable *dispatchTable)
+{
+ /* We're about to change the displayLists hash; lock it first */
+ DLM_LOCK(dlm)
+
+ /* Decrement the user count. If the user count has gone to
+ * 0, then free the rest of the DLM. Otherwise, other
+ * contexts or threads are still using this DLM; keep
+ * it around.
+ */
+ dlm->userCount--;
+ if (dlm->userCount == 0) {
+
+ crFreeHashtableEx(dlm->displayLists, crdlmFreeDisplayListResourcesCb, dispatchTable);
+ dlm->displayLists = NULL;
+
+ /* Must unlock before freeing the mutex */
+ DLM_UNLOCK(dlm)
+
+#ifdef CHROMIUM_THREADSAFE
+ /* We release the mutex here; we really should delete the
+ * thread data key, but there's no utility in Chromium to
+ * do this.
+ *
+ * Note that, should one thread release the entire DLM
+ * while other threads still believe they are using it,
+ * any other threads that have current display lists (i.e.
+ * have issued glNewList more recently than glEndList)
+ * will be unable to reclaim their (likely very large)
+ * content buffers, as there will be no way to reclaim
+ * the thread-specific data.
+ *
+ * On the other hand, if one thread really does release
+ * the DLM while other threads still believe they are
+ * using it, unreclaimed memory is the least of the
+ * application's problems...
+ */
+ crFreeMutex(&(dlm->dlMutex));
+
+ /* We free the TSD key here as well. Note that this will
+ * strand any threads that still have thread-specific data
+ * tied to this key; but as stated above, if any threads
+ * still do have thread-specific data attached to this DLM,
+ * they're in big trouble anyway.
+ */
+ crFreeTSD(&(dlm->tsdKey));
+ crFreeTSD(&CRDLMTSDKey);
+#endif
+
+ /* Free the master record, and we're all done. */
+ crFree(dlm);
+ }
+ else {
+ /* We're keeping the DLM around for other users. Unlock it,
+ * but retain its memory and display lists.
+ */
+ DLM_UNLOCK(dlm)
+ }
+}
+
+/**
+ * The actual run-time state of a DLM is bound to a context
+ * (because each context can be used by at most one thread at
+ * a time, and a thread can only use one context at a time,
+ * while multiple contexts can use the same DLM).
+ * This creates the structure required to hold the state, and
+ * returns it to the caller, who should store it with any other
+ * context-specific information.
+ */
+
+CRDLMContextState DLM_APIENTRY *crDLMNewContext(CRDLM *dlm)
+{
+ CRDLMContextState *state;
+
+ /* Get a record for our own internal state structure */
+ state = (CRDLMContextState *)crAlloc(sizeof(CRDLMContextState));
+ if (!state) {
+ return NULL;
+ }
+
+ state->dlm = dlm;
+ state->currentListIdentifier = 0;
+ state->currentListInfo = NULL;
+ state->currentListMode = GL_FALSE;
+ state->listBase = 0;
+
+ /* Increment the use count of the DLM provided. This guarantees that
+ * the DLM won't be released until all the contexts have released it.
+ */
+ crDLMUseDLM(dlm);
+
+ return state;
+}
+
+
+/**
+ * This routine should be called when a MakeCurrent changes the current
+ * context. It sets the thread data (or global data, in an unthreaded
+ * environment) appropriately; this in turn changes the behavior of
+ * the installed DLM API functions.
+ */
+void DLM_APIENTRY crDLMSetCurrentState(CRDLMContextState *state)
+{
+ CRDLMContextState *currentState = CURRENT_STATE();
+ if (currentState != state) {
+ SET_CURRENT_STATE(state);
+ }
+}
+
+CRDLMContextState DLM_APIENTRY *crDLMGetCurrentState(void)
+{
+ return CURRENT_STATE();
+}
+
+/**
+ * This routine, of course, is used to release a DLM context when it
+ * is no longer going to be used.
+ */
+
+void DLM_APIENTRY crDLMFreeContext(CRDLMContextState *state, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+
+ /* If we're currently using this context, release it first */
+ if (listState == state)
+ crDLMSetCurrentState(NULL);
+
+ /* Try to free the DLM. This will either decrement the use count,
+ * or will actually free the DLM, if we were the last user.
+ */
+ crDLMFreeDLM(state->dlm, dispatchTable);
+ state->dlm = NULL;
+
+ /* If any buffers still remain (e.g. because there was an open
+ * display list), remove those as well.
+ */
+ if (state->currentListInfo)
+ {
+ crdlmFreeDisplayListResourcesCb((void *)state->currentListInfo, (void *)dispatchTable);
+ state->currentListInfo = NULL;
+ }
+ state->currentListIdentifier = 0;
+
+ /* Free the state record itself */
+ crFree(state);
+}
+
+
+/**
+ * This function can be used if the caller wishes to free up the
+ * potentially considerable resources used to store the display list
+ * content, without losing the rest of the display list management.
+ * For one example, consider an SPU that conditionally sends its
+ * input stream to multiple servers. It could broadcast all display
+ * lists to all servers, or it could only send display lists to servers
+ * that need them. After all servers have the display list, the SPU
+ * may wish to release the resources used to manage the content.
+ */
+CRDLMError DLM_APIENTRY crDLMDeleteListContent(CRDLM *dlm, unsigned long listIdentifier)
+{
+ DLMListInfo *listInfo;
+ DLMInstanceList *instance;
+
+ listInfo = (DLMListInfo *) crHashtableSearch(dlm->displayLists, listIdentifier);
+ if (listInfo && (instance = listInfo->first)) {
+ while (instance) {
+ DLMInstanceList *nextInstance;
+ nextInstance = instance->next;
+ crFree(instance);
+ instance = nextInstance;
+ }
+ listInfo->first = listInfo->last = NULL;
+ }
+ return GL_NO_ERROR;
+}
+
+/**
+ *
+ * Playback/execute a list.
+ * dlm - the display list manager context
+ * listIdentifier - the display list ID (as specified by app) to playback
+ * dispatchTable - the GL dispatch table to jump through as we execute commands
+ */
+void DLM_APIENTRY crDLMReplayDLMList(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ DLMListInfo *listInfo;
+
+ listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier);
+ if (listInfo) {
+ DLMInstanceList *instance = listInfo->first;
+ while (instance) {
+ /* mutex, to make sure another thread doesn't change the list? */
+ /* For now, leave it alone. */
+ (*instance->execute)(instance, dispatchTable);
+ instance = instance->next;
+ }
+ }
+}
+
+/* Playback/execute a list in the current DLM */
+void DLM_APIENTRY crDLMReplayList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState)
+ crDLMReplayDLMList(listState->dlm, listIdentifier, dispatchTable);
+}
+
+/*
+ * Playback/execute the state changing portions of a list.
+ * dlm - the display list manager context
+ * listIdentifier - the display list ID (as specified by app) to playback
+ * dispatchTable - the GL dispatch table to jump through as we execute commands
+ */
+void DLM_APIENTRY crDLMReplayDLMListState(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ DLMListInfo *listInfo;
+
+ listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier);
+ if (listInfo) {
+ DLMInstanceList *instance = listInfo->stateFirst;
+ while (instance) {
+ /* mutex, to make sure another thread doesn't change the list? */
+ /* For now, leave it alone. */
+ (*instance->execute)(instance, dispatchTable);
+ instance = instance->stateNext;
+ }
+ }
+}
+
+void DLM_APIENTRY crDLMReplayListState(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState)
+ crDLMReplayDLMListState(listState->dlm, listIdentifier, dispatchTable);
+}
+
+/* This is a switch statement that lists every "type" value valid for a
+ * glCallLists() function call, with code for decoding the subsequent
+ * values correctly. It uses the current value of the EXPAND() macro,
+ * which must expand into an appropriate action to be taken.
+ * Its codification here allows for multiple uses.
+ */
+#define CALL_LISTS_SWITCH(type, defaultAction) \
+ switch (type) {\
+ EXPAND(GL_BYTE, GLbyte *, *p, p++)\
+ EXPAND(GL_UNSIGNED_BYTE, GLubyte *, *p, p++)\
+ EXPAND(GL_SHORT, GLshort *, *p, p++)\
+ EXPAND(GL_UNSIGNED_SHORT, GLushort *, *p, p++)\
+ EXPAND(GL_INT, GLint *, *p, p++)\
+ EXPAND(GL_FLOAT, GLfloat *, *p, p++)\
+ EXPAND(GL_2_BYTES, unsigned char *, 256*p[0] + p[1], p += 2)\
+ EXPAND(GL_3_BYTES, unsigned char *, 65536*p[0] + 256*p[1] + p[2], p += 3)\
+ EXPAND(GL_4_BYTES, unsigned char *, 16777216*p[0] + 65536*p[1] + 256*p[2] + p[3], p += 4)\
+ default:\
+ defaultAction;\
+ }
+
+void DLM_APIENTRY crDLMReplayDLMLists(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ unsigned long listId;
+ CRDLMContextState *listState = CURRENT_STATE();
+
+#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \
+ case TYPENAME: {\
+ TYPE p = (TYPE)lists;\
+ while (n--) {\
+ listId = listState->listBase + (unsigned long) (REFERENCE);\
+ crDLMReplayDLMList(dlm, listId, dispatchTable);\
+ INCREMENT;\
+ }\
+ break;\
+ }
+
+ CALL_LISTS_SWITCH(type, break)
+#undef EXPAND
+
+}
+
+void DLM_APIENTRY crDLMReplayLists(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMReplayDLMLists(listState->dlm, n, type, lists, dispatchTable);
+ }
+}
+
+void DLM_APIENTRY crDLMReplayDLMListsState(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ unsigned long listId;
+ CRDLMContextState *listState = CURRENT_STATE();
+
+#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \
+ case TYPENAME: {\
+ TYPE p = (TYPE)lists;\
+ while (n--) {\
+ listId = listState->listBase + (unsigned long) (REFERENCE);\
+ crDLMReplayDLMListState(dlm, listId, dispatchTable);\
+ INCREMENT;\
+ }\
+ break;\
+ }
+
+ CALL_LISTS_SWITCH(type, break)
+#undef EXPAND
+
+}
+
+void DLM_APIENTRY crDLMReplayListsState(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMReplayDLMListsState(listState->dlm, n, type, lists, dispatchTable);
+ }
+}
+
+/* When we compiled the display list, we packed all pixel data
+ * tightly. When we execute the display list, we have to make
+ * sure that the client state reflects that the pixel data is
+ * tightly packed, or it will be interpreted incorrectly.
+ */
+void DLM_APIENTRY crDLMSetupClientState(SPUDispatchTable *dispatchTable)
+{
+ dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+}
+
+void DLM_APIENTRY crDLMRestoreClientState(CRClientState *clientState, SPUDispatchTable *dispatchTable)
+{
+ if (clientState) {
+ dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, clientState->unpack.rowLength);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, clientState->unpack.skipPixels);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, clientState->unpack.skipRows);
+ dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, clientState->unpack.alignment);
+ }
+}
+
+void DLM_APIENTRY crDLMSendDLMList(CRDLM *dlm, unsigned long listIdentifier,
+ SPUDispatchTable *dispatchTable)
+{
+ dispatchTable->NewList(listIdentifier, GL_COMPILE);
+ crDLMReplayDLMList(dlm, listIdentifier, dispatchTable);
+ dispatchTable->EndList();
+}
+
+void DLM_APIENTRY crDLMSendList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMSendDLMList(listState->dlm, listIdentifier, dispatchTable);
+ }
+}
+
+struct sendListsCallbackParms {
+ CRDLM *dlm;
+ SPUDispatchTable *dispatchTable;
+};
+
+static void sendListsCallback(unsigned long key, void *data, void *dataPtr2)
+{
+ struct sendListsCallbackParms *parms = (struct sendListsCallbackParms *)dataPtr2;
+
+ crDLMSendDLMList(parms->dlm, key, parms->dispatchTable);
+}
+
+void DLM_APIENTRY crDLMSendAllDLMLists(CRDLM *dlm, SPUDispatchTable *dispatchTable)
+{
+ struct sendListsCallbackParms parms;
+
+ /* This is how we pass our parameter information to the callback routine -
+ * through a pointer to this local structure.
+ */
+ parms.dlm = dlm;
+ parms.dispatchTable = dispatchTable;
+
+ crHashtableWalk(dlm->displayLists, sendListsCallback, (void *)&parms);
+}
+
+void DLM_APIENTRY crDLMSendAllLists(SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMSendAllDLMLists(listState->dlm, dispatchTable);
+ }
+}
+
+/** Another clever callback arrangement to get the desired data. */
+struct getRefsCallbackParms {
+ int remainingOffset;
+ int remainingCount;
+ unsigned int *buffer;
+ int totalCount;
+};
+
+/*
+ * Return id of list currently being compiled. Returns 0 of there's no
+ * current DLM state, or if no list is being compiled.
+ */
+GLuint DLM_APIENTRY crDLMGetCurrentList(void)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ return listState ? listState->currentListIdentifier : 0;
+}
+
+/*
+ * Return mode of list currently being compiled. Should be
+ * GL_FALSE if no list is being compiled, or GL_COMPILE if a
+ * list is being compiled but not executed, or GL_COMPILE_AND_EXECUTE
+ * if a list is being compiled and executed.
+ */
+GLenum DLM_APIENTRY crDLMGetCurrentMode(void)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ return listState ? listState->currentListMode : 0;
+}
+
+
+static CRDLMErrorCallback ErrorCallback = NULL;
+
+void DLM_APIENTRY crDLMErrorFunction(CRDLMErrorCallback callback)
+{
+ ErrorCallback = callback;
+}
+
+void crdlm_error(int line, const char *file, GLenum error, const char *info)
+{
+ if (ErrorCallback)
+ (*ErrorCallback)(line, file, error, info);
+}