state_init.c revision 81578c6df6541d965dcfe32dd0e0decb64a42284
/* Copyright (c) 2001, Stanford University
* All rights reserved
*
* See the file LICENSE.txt for information on redistributing this software.
*/
#include "state.h"
#include "cr_mem.h"
#include "cr_error.h"
#include "cr_spu.h"
#ifdef CHROMIUM_THREADSAFE
static bool __isContextTLSInited = false;
CRtsd __contextTSD;
#else
CRContext *__currentContext = NULL;
#endif
CRStateBits *__currentBits = NULL;
CRContext *g_pAvailableContexts[CR_MAX_CONTEXTS];
uint32_t g_cContexts = 0;
static CRSharedState *gSharedState=NULL;
static CRContext *defaultContext = NULL;
GLboolean g_bVBoxEnableDiffOnMakeCurrent = GL_TRUE;
/**
* Allocate a new shared state object.
* Contains texture objects, display lists, etc.
*/
static CRSharedState *
crStateAllocShared(void)
{
CRSharedState *s = (CRSharedState *) crCalloc(sizeof(CRSharedState));
if (s) {
s->textureTable = crAllocHashtable();
s->dlistTable = crAllocHashtable();
s->buffersTable = crAllocHashtable();
s->fbTable = crAllocHashtable();
s->rbTable = crAllocHashtable();
s->refCount = 1; /* refcount is number of contexts using this state */
s->saveCount = 0;
}
return s;
}
/**
* Callback used for crFreeHashtable().
*/
DECLEXPORT(void)
crStateDeleteTextureCallback(void *texObj)
{
#ifndef IN_GUEST
diff_api.DeleteTextures(1, &((CRTextureObj *)texObj)->hwid);
#endif
crStateDeleteTextureObject((CRTextureObj *) texObj);
}
#ifndef IN_GUEST
typedef struct CR_STATE_RELEASEOBJ
{
CRContext *pCtx;
CRSharedState *s;
} CR_STATE_RELEASEOBJ, *PCR_STATE_RELEASEOBJ;
static void ReleaseTextureCallback(unsigned long key, void *data1, void *data2)
{
PCR_STATE_RELEASEOBJ pData = (PCR_STATE_RELEASEOBJ)data2;
CRTextureObj *pObj = (CRTextureObj *)data1;
CR_STATE_SHAREDOBJ_USAGE_CLEAR(pObj, pData->pCtx);
if (!CR_STATE_SHAREDOBJ_USAGE_IS_USED(pObj))
crHashtableDelete(pData->s->textureTable, key, crStateDeleteTextureCallback);
}
static void ReleaseBufferObjectCallback(unsigned long key, void *data1, void *data2)
{
PCR_STATE_RELEASEOBJ pData = (PCR_STATE_RELEASEOBJ)data2;
CRBufferObject *pObj = (CRBufferObject *)data1;
CR_STATE_SHAREDOBJ_USAGE_CLEAR(pObj, pData->pCtx);
if (!CR_STATE_SHAREDOBJ_USAGE_IS_USED(pObj))
crHashtableDelete(pData->s->buffersTable, key, crStateFreeBufferObject);
}
static void ReleaseFBOCallback(unsigned long key, void *data1, void *data2)
{
PCR_STATE_RELEASEOBJ pData = (PCR_STATE_RELEASEOBJ)data2;
CRFramebufferObject *pObj = (CRFramebufferObject *)data1;
CR_STATE_SHAREDOBJ_USAGE_CLEAR(pObj, pData->pCtx);
if (!CR_STATE_SHAREDOBJ_USAGE_IS_USED(pObj))
crHashtableDelete(pData->s->fbTable, key, crStateFreeFBO);
}
static void ReleaseRBOCallback(unsigned long key, void *data1, void *data2)
{
PCR_STATE_RELEASEOBJ pData = (PCR_STATE_RELEASEOBJ)data2;
CRRenderbufferObject *pObj = (CRRenderbufferObject *)data1;
CR_STATE_SHAREDOBJ_USAGE_CLEAR(pObj, pData->pCtx);
if (!CR_STATE_SHAREDOBJ_USAGE_IS_USED(pObj))
crHashtableDelete(pData->s->rbTable, key, crStateFreeRBO);
}
#endif
/**
* Decrement shared state's refcount and delete when it hits zero.
*/
DECLEXPORT(void)
crStateFreeShared(CRContext *pContext, CRSharedState *s)
{
s->refCount--;
if (s->refCount <= 0) {
if (s==gSharedState)
{
gSharedState = NULL;
}
crFreeHashtable(s->textureTable, crStateDeleteTextureCallback);
crFreeHashtable(s->dlistTable, crFree); /* call crFree for each entry */
crFreeHashtable(s->buffersTable, crStateFreeBufferObject);
crFreeHashtable(s->fbTable, crStateFreeFBO);
crFreeHashtable(s->rbTable, crStateFreeRBO);
crFree(s);
}
#ifndef IN_GUEST
else
{
/* evaluate usage bits*/
CR_STATE_RELEASEOBJ CbData;
CbData.pCtx = pContext;
CbData.s = s;
crHashtableWalk(s->textureTable, ReleaseTextureCallback, &CbData);
crHashtableWalk(s->buffersTable, ReleaseBufferObjectCallback , &CbData);
crHashtableWalk(s->fbTable, ReleaseFBOCallback, &CbData);
crHashtableWalk(s->rbTable, ReleaseRBOCallback, &CbData);
}
#endif
}
DECLEXPORT(void) STATE_APIENTRY
crStateShareContext(GLboolean value)
{
CRContext *pCtx = GetCurrentContext();
CRASSERT(pCtx && pCtx->shared);
if (value)
{
if (pCtx->shared == gSharedState)
{
return;
}
crDebug("Context(%i) shared", pCtx->id);
if (!gSharedState)
{
gSharedState = pCtx->shared;
}
else
{
crStateFreeShared(pCtx, pCtx->shared);
pCtx->shared = gSharedState;
gSharedState->refCount++;
}
}
else
{
if (pCtx->shared != gSharedState)
{
return;
}
crDebug("Context(%i) unshared", pCtx->id);
if (gSharedState->refCount==1)
{
gSharedState = NULL;
}
else
{
pCtx->shared = crStateAllocShared();
pCtx->shared->id = pCtx->id;
crStateFreeShared(pCtx, gSharedState);
}
}
}
DECLEXPORT(GLboolean) STATE_APIENTRY
crStateContextIsShared(CRContext *pCtx)
{
return pCtx->shared==gSharedState;
}
DECLEXPORT(void) STATE_APIENTRY
crStateSetSharedContext(CRContext *pCtx)
{
if (gSharedState)
{
crWarning("crStateSetSharedContext: shared is being changed from %p to %p", gSharedState, pCtx->shared);
}
gSharedState = pCtx->shared;
}
#ifdef CHROMIUM_THREADSAFE
static void
crStateFreeContext(CRContext *ctx);
static DECLCALLBACK(void) crStateContextDtor(void *pvCtx)
{
crStateFreeContext((CRContext*)pvCtx);
}
#endif
/*
* Helper for crStateCreateContext, below.
*/
static CRContext *
crStateCreateContextId(int i, const CRLimitsState *limits,
GLint visBits, CRContext *shareCtx)
{
CRContext *ctx;
int j;
int node32 = i >> 5;
int node = i & 0x1f;
if (g_pAvailableContexts[i] != NULL)
{
crWarning("trying to create context with used id");
return NULL;
}
ctx = (CRContext *) crCalloc( sizeof( *ctx ) );
if (!ctx)
{
crWarning("failed to allocate context");
return NULL;
}
g_pAvailableContexts[i] = ctx;
++g_cContexts;
CRASSERT(g_cContexts < RT_ELEMENTS(g_pAvailableContexts));
ctx->id = i;
#ifdef CHROMIUM_THREADSAFE
VBoxTlsRefInit(ctx, crStateContextDtor);
#endif
ctx->flush_func = NULL;
for (j=0;j<CR_MAX_BITARRAY;j++){
if (j == node32) {
ctx->bitid[j] = (1 << node);
} else {
ctx->bitid[j] = 0;
}
ctx->neg_bitid[j] = ~(ctx->bitid[j]);
}
if (shareCtx) {
CRASSERT(shareCtx->shared);
ctx->shared = shareCtx->shared;
ctx->shared->refCount ++;
}
else {
ctx->shared = crStateAllocShared();
ctx->shared->id = ctx->id;
}
/* use Chromium's OpenGL defaults */
crStateLimitsInit( &(ctx->limits) );
crStateExtensionsInit( &(ctx->limits), &(ctx->extensions) );
crStateBufferObjectInit( ctx ); /* must precede client state init! */
crStateClientInit( ctx );
crStateBufferInit( ctx );
crStateCurrentInit( ctx );
crStateEvaluatorInit( ctx );
crStateFogInit( ctx );
crStateHintInit( ctx );
crStateLightingInit( ctx );
crStateLineInit( ctx );
crStateListsInit( ctx );
crStateMultisampleInit( ctx );
crStateOcclusionInit( ctx );
crStatePixelInit( ctx );
crStatePolygonInit( ctx );
crStatePointInit( ctx );
crStateProgramInit( ctx );
crStateRegCombinerInit( ctx );
crStateStencilInit( ctx );
crStateTextureInit( ctx );
crStateTransformInit( ctx );
crStateViewportInit ( ctx );
crStateFramebufferObjectInit(ctx);
crStateGLSLInit(ctx);
/* This has to come last. */
crStateAttribInit( &(ctx->attrib) );
ctx->renderMode = GL_RENDER;
/* Initialize values that depend on the visual mode */
if (visBits & CR_DOUBLE_BIT) {
ctx->limits.doubleBuffer = GL_TRUE;
}
if (visBits & CR_RGB_BIT) {
ctx->limits.redBits = 8;
ctx->limits.greenBits = 8;
ctx->limits.blueBits = 8;
if (visBits & CR_ALPHA_BIT) {
ctx->limits.alphaBits = 8;
}
}
else {
ctx->limits.indexBits = 8;
}
if (visBits & CR_DEPTH_BIT) {
ctx->limits.depthBits = 24;
}
if (visBits & CR_STENCIL_BIT) {
ctx->limits.stencilBits = 8;
}
if (visBits & CR_ACCUM_BIT) {
ctx->limits.accumRedBits = 16;
ctx->limits.accumGreenBits = 16;
ctx->limits.accumBlueBits = 16;
if (visBits & CR_ALPHA_BIT) {
ctx->limits.accumAlphaBits = 16;
}
}
if (visBits & CR_STEREO_BIT) {
ctx->limits.stereo = GL_TRUE;
}
if (visBits & CR_MULTISAMPLE_BIT) {
ctx->limits.sampleBuffers = 1;
ctx->limits.samples = 4;
ctx->multisample.enabled = GL_TRUE;
}
if (visBits & CR_OVERLAY_BIT) {
ctx->limits.level = 1;
}
return ctx;
}
/*@todo crStateAttribDestroy*/
static void
crStateFreeContext(CRContext *ctx)
{
#ifndef DEBUG_misha
CRASSERT(g_pAvailableContexts[ctx->id] == ctx);
#endif
if (g_pAvailableContexts[ctx->id] == ctx)
{
g_pAvailableContexts[ctx->id] = NULL;
--g_cContexts;
CRASSERT(g_cContexts < RT_ELEMENTS(g_pAvailableContexts));
}
else
{
#ifndef DEBUG_misha
crWarning("freeing context 0x%x, id(%d) not being in the context list", ctx, ctx->id);
#endif
}
crStateClientDestroy( ctx );
crStateLimitsDestroy( &(ctx->limits) );
crStateBufferObjectDestroy( ctx );
crStateEvaluatorDestroy( ctx );
crStateListsDestroy( ctx );
crStateLightingDestroy( ctx );
crStateOcclusionDestroy( ctx );
crStateProgramDestroy( ctx );
crStateTextureDestroy( ctx );
crStateTransformDestroy( ctx );
crStateFreeShared(ctx, ctx->shared);
crStateFramebufferObjectDestroy(ctx);
crStateGLSLDestroy(ctx);
if (ctx->buffer.pFrontImg) crFree(ctx->buffer.pFrontImg);
if (ctx->buffer.pBackImg) crFree(ctx->buffer.pBackImg);
crFree( ctx );
}
#ifdef CHROMIUM_THREADSAFE
# ifndef RT_OS_WINDOWS
static DECLCALLBACK(void) crStateThreadTlsDtor(void *pvValue)
{
CRContext *pCtx = (CRContext*)pvValue;
VBoxTlsRefRelease(pCtx);
}
# endif
#endif
/*
* Allocate the state (dirty) bits data structures.
* This should be called before we create any contexts.
* We'll also create the default/NULL context at this time and make
* it the current context by default. This means that if someone
* tries to set GL state before calling MakeCurrent() they'll be
* modifying the default state object, and not segfaulting on a NULL
* pointer somewhere.
*/
void crStateInit(void)
{
unsigned int i;
/* Purely initialize the context bits */
if (!__currentBits) {
__currentBits = (CRStateBits *) crCalloc( sizeof(CRStateBits) );
crStateClientInitBits( &(__currentBits->client) );
crStateLightingInitBits( &(__currentBits->lighting) );
} else
{
#ifndef DEBUG_misha
crWarning("State tracker is being re-initialized..\n");
#endif
}
for (i=0;i<CR_MAX_CONTEXTS;i++)
g_pAvailableContexts[i] = NULL;
g_cContexts = 0;
#ifdef CHROMIUM_THREADSAFE
if (!__isContextTLSInited)
{
# ifndef RT_OS_WINDOWS
/* tls destructor is implemented for all platforms except windows*/
crInitTSDF(&__contextTSD, crStateThreadTlsDtor);
# else
/* windows should do cleanup via DllMain THREAD_DETACH notification */
crInitTSD(&__contextTSD);
# endif
__isContextTLSInited = 1;
}
#endif
if (defaultContext) {
/* Free the default/NULL context.
* Ensures context bits are reset */
#ifdef CHROMIUM_THREADSAFE
SetCurrentContext(NULL);
VBoxTlsRefRelease(defaultContext);
#else
crStateFreeContext(defaultContext);
__currentContext = NULL;
#endif
}
/* Reset diff_api */
crMemZero(&diff_api, sizeof(SPUDispatchTable));
/* Allocate the default/NULL context */
CRASSERT(g_pAvailableContexts[0] == NULL);
defaultContext = crStateCreateContextId(0, NULL, CR_RGB_BIT, NULL);
CRASSERT(g_pAvailableContexts[0] == defaultContext);
CRASSERT(g_cContexts == 1);
#ifdef CHROMIUM_THREADSAFE
SetCurrentContext(defaultContext);
#else
__currentContext = defaultContext;
#endif
}
void crStateDestroy(void)
{
int i;
if (__currentBits)
{
crStateClientDestroyBits(&(__currentBits->client));
crStateLightingDestroyBits(&(__currentBits->lighting));
crFree(__currentBits);
__currentBits = NULL;
}
SetCurrentContext(NULL);
for (i = CR_MAX_CONTEXTS-1; i >= 0; i--)
{
if (g_pAvailableContexts[i])
{
#ifdef CHROMIUM_THREADSAFE
if (VBoxTlsRefIsFunctional(g_pAvailableContexts[i]))
VBoxTlsRefRelease(g_pAvailableContexts[i]);
#else
crStateFreeContext(g_pAvailableContexts[i]);
#endif
}
}
/* default context was stored in g_pAvailableContexts[0], so it was destroyed already */
defaultContext = NULL;
#ifdef CHROMIUM_THREADSAFE
crFreeTSD(&__contextTSD);
__isContextTLSInited = 0;
#endif
}
/*
* Notes on context switching and the "default context".
*
* See the paper "Tracking Graphics State for Networked Rendering"
* by Ian Buck, Greg Humphries and Pat Hanrahan for background
* information about how the state tracker and context switching
* works.
*
* When we make a new context current, we call crStateSwitchContext()
* in order to transform the 'from' context into the 'to' context
* (i.e. the old context to the new context). The transformation
* is accomplished by calling GL functions through the 'diff_api'
* so that the downstream GL machine (represented by the __currentContext
* structure) is updated to reflect the new context state. Finally,
* we point __currentContext to the new context.
*
* A subtle problem we have to deal with is context destruction.
* This issue arose while testing with Glean. We found that when
* the currently bound context was getting destroyed that state
* tracking was incorrect when a subsequent new context was activated.
* In DestroyContext, the __hwcontext was being set to NULL and effectively
* going away. Later in MakeCurrent we had no idea what the state of the
* downstream GL machine was (since __hwcontext was gone). This meant
* we had nothing to 'diff' against and the downstream GL machine was
* in an unknown state.
*
* The solution to this problem is the "default/NULL" context. The
* default context is created the first time CreateContext is called
* and is never freed. Whenever we get a crStateMakeCurrent(NULL) call
* or destroy the currently bound context in crStateDestroyContext()
* we call crStateSwitchContext() to switch to the default context and
* then set the __currentContext pointer to point to the default context.
* This ensures that the dirty bits are updated and the diff_api functions
* are called to keep the downstream GL machine in a known state.
* Finally, the __hwcontext variable is no longer needed now.
*
* Yeah, this is kind of a mind-bender, but it really solves the problem
* pretty cleanly.
*
* -Brian
*/
CRContext *
crStateCreateContext(const CRLimitsState *limits, GLint visBits, CRContext *share)
{
return crStateCreateContextEx(limits, visBits, share, -1);
}
CRContext *
crStateCreateContextEx(const CRLimitsState *limits, GLint visBits, CRContext *share, GLint presetID)
{
/* Must have created the default context via crStateInit() first */
CRASSERT(defaultContext);
if (presetID>0)
{
if(g_pAvailableContexts[presetID])
{
crWarning("requesting to create context with already allocated id");
return NULL;
}
}
else
{
int i;
for (i = 1 ; i < CR_MAX_CONTEXTS ; i++)
{
if (!g_pAvailableContexts[i])
{
presetID = i;
break;
}
}
if (presetID<=0)
{
crError( "Out of available contexts in crStateCreateContexts (max %d)",
CR_MAX_CONTEXTS );
/* never get here */
return NULL;
}
}
return crStateCreateContextId(presetID, limits, visBits, share);
}
void crStateDestroyContext( CRContext *ctx )
{
CRContext *current = GetCurrentContext();
if (current == ctx) {
/* destroying the current context - have to be careful here */
CRASSERT(defaultContext);
/* Check to see if the differencer exists first,
we may not have one, aka the packspu */
if (diff_api.AlphaFunc)
crStateSwitchContext(current, defaultContext);
#ifdef CHROMIUM_THREADSAFE
SetCurrentContext(defaultContext);
#else
__currentContext = defaultContext;
#endif
/* ensure matrix state is also current */
crStateMatrixMode(defaultContext->transform.matrixMode);
}
#ifdef CHROMIUM_THREADSAFE
VBoxTlsRefMarkDestroy(ctx);
VBoxTlsRefRelease(ctx);
#else
crStateFreeContext(ctx);
#endif
}
GLboolean crStateEnableDiffOnMakeCurrent(GLboolean fEnable)
{
GLboolean bOld = g_bVBoxEnableDiffOnMakeCurrent;
g_bVBoxEnableDiffOnMakeCurrent = fEnable;
return bOld;
}
void crStateMakeCurrent( CRContext *ctx )
{
CRContext *current = GetCurrentContext();
if (ctx == NULL)
ctx = defaultContext;
if (current == ctx)
return; /* no-op */
CRASSERT(ctx);
if (g_bVBoxEnableDiffOnMakeCurrent && current) {
/* Check to see if the differencer exists first,
we may not have one, aka the packspu */
if (diff_api.AlphaFunc)
crStateSwitchContext( current, ctx );
}
#ifdef CHROMIUM_THREADSAFE
SetCurrentContext(ctx);
#else
__currentContext = ctx;
#endif
/* ensure matrix state is also current */
crStateMatrixMode(ctx->transform.matrixMode);
}
/*
* As above, but don't call crStateSwitchContext().
*/
void crStateSetCurrent( CRContext *ctx )
{
CRContext *current = GetCurrentContext();
if (ctx == NULL)
ctx = defaultContext;
if (current == ctx)
return; /* no-op */
CRASSERT(ctx);
#ifdef CHROMIUM_THREADSAFE
SetCurrentContext(ctx);
#else
__currentContext = ctx;
#endif
/* ensure matrix state is also current */
crStateMatrixMode(ctx->transform.matrixMode);
}
CRContext *crStateGetCurrent(void)
{
return GetCurrentContext();
}
void crStateUpdateColorBits(void)
{
/* This is a hack to force updating the 'current' attribs */
CRStateBits *sb = GetCurrentBits();
FILLDIRTY(sb->current.dirty);
FILLDIRTY(sb->current.vertexAttrib[VERT_ATTRIB_COLOR0]);
}
void STATE_APIENTRY
crStateChromiumParameteriCR( GLenum target, GLint value )
{
/* This no-op function helps smooth code-gen */
}
void STATE_APIENTRY
crStateChromiumParameterfCR( GLenum target, GLfloat value )
{
/* This no-op function helps smooth code-gen */
}
void STATE_APIENTRY
crStateChromiumParametervCR( GLenum target, GLenum type, GLsizei count, const GLvoid *values )
{
/* This no-op function helps smooth code-gen */
}
void STATE_APIENTRY
crStateGetChromiumParametervCR( GLenum target, GLuint index, GLenum type, GLsizei count, GLvoid *values )
{
/* This no-op function helps smooth code-gen */
}
void STATE_APIENTRY
crStateReadPixels( GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid *pixels )
{
/* This no-op function helps smooth code-gen */
}
void crStateVBoxDetachThread()
{
/* release the context ref so that it can be freed */
SetCurrentContext(NULL);
}
void crStateVBoxAttachThread()
{
}
GLint crStateVBoxCreateContext( GLint con, const char * dpyName, GLint visual, GLint shareCtx )
{
return 0;
}
GLint crStateVBoxWindowCreate( GLint con, const char *dpyName, GLint visBits )
{
return 0;
}
void crStateVBoxWindowDestroy( GLint con, GLint window )
{
}
GLint crStateVBoxConCreate(struct VBOXUHGSMI *pHgsmi)
{
return 0;
}
void crStateVBoxConDestroy(GLint con)
{
}
void crStateVBoxConFlush(GLint con)
{
}