diff options
Diffstat (limited to 'src/VBox/GuestHost/OpenGL/state_tracker/state_glsl.c')
-rw-r--r-- | src/VBox/GuestHost/OpenGL/state_tracker/state_glsl.c | 1527 |
1 files changed, 1527 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/OpenGL/state_tracker/state_glsl.c b/src/VBox/GuestHost/OpenGL/state_tracker/state_glsl.c new file mode 100644 index 00000000..5767ca91 --- /dev/null +++ b/src/VBox/GuestHost/OpenGL/state_tracker/state_glsl.c @@ -0,0 +1,1527 @@ +/* $Id: state_glsl.c $ */ + +/** @file + * VBox OpenGL: GLSL state tracking + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "state.h" +#include "state/cr_statetypes.h" +#include "state/cr_statefuncs.h" +#include "state_internals.h" +#include "cr_mem.h" +#include "cr_string.h" + +static CRGLSLShader* crStateGetShaderObj(GLuint id) +{ + CRContext *g = GetCurrentContext(); + + if (!g) + { + crWarning("crStateGetShaderObj called without current ctx"); + } + + return !g ? NULL : (CRGLSLShader *) crHashtableSearch(g->glsl.shaders, id); +} + +static CRGLSLProgram* crStateGetProgramObj(GLuint id) +{ + CRContext *g = GetCurrentContext(); + + if (!g) + { + crWarning("crStateGetProgramObj called without current ctx"); + } + + return !g ? NULL : (CRGLSLProgram *) crHashtableSearch(g->glsl.programs, id); +} + +static void crStateFreeGLSLShader(void *data) +{ + CRGLSLShader* pShader = (CRGLSLShader *) data; + + if (pShader->source) + crFree(pShader->source); + + crFree(pShader); +} + +#ifdef IN_GUEST +static void crStateFreeProgramAttribsLocationCache(CRGLSLProgram* pProgram) +{ + if (pProgram->pAttribs) crFree(pProgram->pAttribs); + + pProgram->pAttribs = NULL; + pProgram->cAttribs = 0; +} +#endif + +static void crStateFreeProgramAttribs(CRGLSLProgram* pProgram) +{ + GLuint i; + + for (i=0; i<pProgram->activeState.cAttribs; ++i) + { + crFree(pProgram->activeState.pAttribs[i].name); + } + + for (i=0; i<pProgram->currentState.cAttribs; ++i) + { + crFree(pProgram->currentState.pAttribs[i].name); + } + + if (pProgram->activeState.pAttribs) + crFree(pProgram->activeState.pAttribs); + + if (pProgram->currentState.pAttribs) + crFree(pProgram->currentState.pAttribs); + +#ifdef IN_GUEST + crStateFreeProgramAttribsLocationCache(pProgram); + + pProgram->bAttribsSynced = GL_FALSE; +#endif + +} + +static void crStateFreeProgramUniforms(CRGLSLProgram* pProgram) +{ + GLuint i; + + for (i=0; i<pProgram->cUniforms; ++i) + { + if (pProgram->pUniforms[i].name) crFree(pProgram->pUniforms[i].name); + if (pProgram->pUniforms[i].data) crFree(pProgram->pUniforms[i].data); + } + + if (pProgram->pUniforms) crFree(pProgram->pUniforms); + + pProgram->pUniforms = NULL; + pProgram->cUniforms = 0; + +#ifdef IN_GUEST + pProgram->bUniformsSynced = GL_FALSE; +#endif +} + +static void crStateShaderDecRefCount(void *data) +{ + CRGLSLShader *pShader = (CRGLSLShader *) data; + + CRASSERT(pShader->refCount>0); + + pShader->refCount--; + + if (0==pShader->refCount && pShader->deleted) + { + CRContext *g = GetCurrentContext(); + crHashtableDelete(g->glsl.shaders, pShader->id, crStateFreeGLSLShader); + } +} + +static void crStateFakeDecRefCountCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pShader = (CRGLSLShader *) data1; + CRContext *ctx = (CRContext*) data2; + CRGLSLShader *pRealShader; + (void) key; (void)ctx; + + pRealShader = crStateGetShaderObj(pShader->id); + + if (pRealShader) + { + crStateShaderDecRefCount(pRealShader); + } + else + { + crWarning("crStateFakeDecRefCountCB: NULL pRealShader"); + } +} + +static void crStateFreeGLSLProgram(void *data) +{ + CRGLSLProgram* pProgram = (CRGLSLProgram *) data; + + crFreeHashtable(pProgram->currentState.attachedShaders, crStateShaderDecRefCount); + + if (pProgram->activeState.attachedShaders) + { + CRContext *g = GetCurrentContext(); + crHashtableWalk(pProgram->activeState.attachedShaders, crStateFakeDecRefCountCB, g); + crFreeHashtable(pProgram->activeState.attachedShaders, crStateFreeGLSLShader); + } + + crStateFreeProgramAttribs(pProgram); + + crStateFreeProgramUniforms(pProgram); + + crFree(pProgram); +} + +DECLEXPORT(void) STATE_APIENTRY crStateGLSLInit(CRContext *ctx) +{ + ctx->glsl.shaders = crAllocHashtable(); + ctx->glsl.programs = crAllocHashtable(); + ctx->glsl.activeProgram = NULL; + ctx->glsl.bResyncNeeded = GL_FALSE; + + if (!ctx->glsl.shaders || !ctx->glsl.programs) + { + crWarning("crStateGLSLInit: Out of memory!"); + return; + } +} + +DECLEXPORT(void) STATE_APIENTRY crStateGLSLDestroy(CRContext *ctx) +{ + CRContext *g = GetCurrentContext(); + + /** @todo hack to allow crStateFreeGLSLProgram to work correctly, + as the current context isn't the one being destroyed*/ +#ifdef CHROMIUM_THREADSAFE + CRASSERT(g != ctx); + VBoxTlsRefAddRef(ctx); /* <- this is a hack to avoid subsequent SetCurrentContext(g) do recursive Destroy for ctx */ + if (g) + VBoxTlsRefAddRef(g); /* <- ensure the g is not destroyed by the following SetCurrentContext call */ + SetCurrentContext(ctx); +#else + __currentContext = ctx; +#endif + + crFreeHashtable(ctx->glsl.programs, crStateFreeGLSLProgram); + crFreeHashtable(ctx->glsl.shaders, crStateFreeGLSLShader); + +#ifdef CHROMIUM_THREADSAFE + SetCurrentContext(g); + if (g) + VBoxTlsRefRelease(g); + VBoxTlsRefRelease(ctx); /* <- restore back the cRefs (see above) */ +#else + __currentContext = g; +#endif + +} + +DECLEXPORT(GLuint) STATE_APIENTRY crStateGetShaderHWID(GLuint id) +{ + CRGLSLShader *pShader = crStateGetShaderObj(id); +#ifdef IN_GUEST + CRASSERT(!pShader || pShader->hwid == id); +#endif + return pShader ? pShader->hwid : 0; +} + +DECLEXPORT(GLuint) STATE_APIENTRY crStateGetProgramHWID(GLuint id) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(id); +#ifdef IN_GUEST + CRASSERT(!pProgram || pProgram->hwid == id); +#endif + return pProgram ? pProgram->hwid : 0; +} + +static void crStateCheckShaderHWIDCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pShader = (CRGLSLShader *) data1; + crCheckIDHWID_t *pParms = (crCheckIDHWID_t*) data2; + (void) key; + + if (pShader->hwid==pParms->hwid) + pParms->id = pShader->id; +} + +static void crStateCheckProgramHWIDCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLProgram *pProgram = (CRGLSLProgram *) data1; + crCheckIDHWID_t *pParms = (crCheckIDHWID_t*) data2; + (void) key; + + if (pProgram->hwid==pParms->hwid) + pParms->id = pProgram->id; +} + +DECLEXPORT(GLuint) STATE_APIENTRY crStateGLSLShaderHWIDtoID(GLuint hwid) +{ + CRContext *g = GetCurrentContext(); + crCheckIDHWID_t parms; + + parms.id = hwid; + parms.hwid = hwid; + + crHashtableWalk(g->glsl.shaders, crStateCheckShaderHWIDCB, &parms); + return parms.id; +} + +DECLEXPORT(GLuint) STATE_APIENTRY crStateGLSLProgramHWIDtoID(GLuint hwid) +{ + CRContext *g = GetCurrentContext(); + crCheckIDHWID_t parms; + + parms.id = hwid; + parms.hwid = hwid; + + crHashtableWalk(g->glsl.programs, crStateCheckProgramHWIDCB, &parms); + return parms.id; +} + +DECLEXPORT(GLuint) STATE_APIENTRY crStateDeleteObjectARB( VBoxGLhandleARB obj ) +{ + GLuint hwId = crStateGetProgramHWID(obj); + if (hwId) + { + crStateDeleteProgram(obj); + } + else + { + hwId = crStateGetShaderHWID(obj); + crStateDeleteShader(obj); + } + return hwId; +} + +DECLEXPORT(GLuint) STATE_APIENTRY crStateCreateShader(GLuint hwid, GLenum type) +{ + CRGLSLShader *pShader; + CRContext *g = GetCurrentContext(); + GLuint stateId = hwid; + +#ifdef IN_GUEST + CRASSERT(!crStateGetShaderObj(stateId)); +#else + /* the proogram and shader names must not intersect because DeleteObjectARB must distinguish between them + * see crStateDeleteObjectARB + * this is why use programs table for shader keys allocation */ + stateId = crHashtableAllocKeys(g->glsl.programs, 1); + if (!stateId) + { + crWarning("failed to allocate program key"); + return 0; + } + + Assert((pShader = crStateGetShaderObj(stateId)) == NULL); +#endif + + pShader = (CRGLSLShader *) crAlloc(sizeof(*pShader)); + if (!pShader) + { + crWarning("crStateCreateShader: Out of memory!"); + return 0; + } + + pShader->id = stateId; + pShader->hwid = hwid; + pShader->type = type; + pShader->source = NULL; + pShader->compiled = GL_FALSE; + pShader->deleted = GL_FALSE; + pShader->refCount = 0; + + crHashtableAdd(g->glsl.shaders, stateId, pShader); + + return stateId; +} + +DECLEXPORT(GLuint) STATE_APIENTRY crStateCreateProgram(GLuint hwid) +{ + CRGLSLProgram *pProgram; + CRContext *g = GetCurrentContext(); + GLuint stateId = hwid; + +#ifdef IN_GUEST + pProgram = crStateGetProgramObj(stateId); + if (pProgram) + { + crWarning("Program object %d already exists!", stateId); + crStateDeleteProgram(stateId); + CRASSERT(!crStateGetProgramObj(stateId)); + } +#else + stateId = crHashtableAllocKeys(g->glsl.programs, 1); + if (!stateId) + { + crWarning("failed to allocate program key"); + return 0; + } +#endif + + pProgram = (CRGLSLProgram *) crAlloc(sizeof(*pProgram)); + if (!pProgram) + { + crWarning("crStateCreateProgram: Out of memory!"); + return 0; + } + + pProgram->id = stateId; + pProgram->hwid = hwid; + pProgram->validated = GL_FALSE; + pProgram->linked = GL_FALSE; + pProgram->deleted = GL_FALSE; + pProgram->activeState.attachedShaders = NULL; + pProgram->currentState.attachedShaders = crAllocHashtable(); + + pProgram->activeState.cAttribs = 0; + pProgram->activeState.pAttribs = NULL; + pProgram->currentState.cAttribs = 0; + pProgram->currentState.pAttribs = NULL; + + pProgram->pUniforms = NULL; + pProgram->cUniforms = 0; + +#ifdef IN_GUEST + pProgram->pAttribs = NULL; + pProgram->cAttribs = 0; + + pProgram->bUniformsSynced = GL_FALSE; + pProgram->bAttribsSynced = GL_FALSE; +#endif + + crHashtableAdd(g->glsl.programs, stateId, pProgram); + + return stateId; +} + +DECLEXPORT(void) STATE_APIENTRY crStateCompileShader(GLuint shader) +{ + CRGLSLShader *pShader = crStateGetShaderObj(shader); + if (!pShader) + { + crWarning("Unknown shader %d", shader); + return; + } + + pShader->compiled = GL_TRUE; +} + +static void crStateDbgCheckNoProgramOfId(void *data) +{ + (void)data; + crError("Unexpected Program id"); +} + +DECLEXPORT(void) STATE_APIENTRY crStateDeleteShader(GLuint shader) +{ + CRGLSLShader *pShader = crStateGetShaderObj(shader); + if (!pShader) + { + crWarning("Unknown shader %d", shader); + return; + } + + pShader->deleted = GL_TRUE; + + if (0==pShader->refCount) + { + CRContext *g = GetCurrentContext(); + crHashtableDelete(g->glsl.shaders, shader, crStateFreeGLSLShader); + /* since we use programs table for key allocation key allocation, we need to + * free the key in the programs table. + * See comment in crStateCreateShader */ + crHashtableDelete(g->glsl.programs, shader, crStateDbgCheckNoProgramOfId); + } +} + +DECLEXPORT(void) STATE_APIENTRY crStateAttachShader(GLuint program, GLuint shader) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + CRGLSLShader *pShader; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + if (crHashtableSearch(pProgram->currentState.attachedShaders, shader)) + { + /*shader already attached to this program*/ + return; + } + + pShader = crStateGetShaderObj(shader); + + if (!pShader) + { + crWarning("Unknown shader %d", shader); + return; + } + + pShader->refCount++; + + crHashtableAdd(pProgram->currentState.attachedShaders, shader, pShader); +} + +DECLEXPORT(void) STATE_APIENTRY crStateDetachShader(GLuint program, GLuint shader) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + CRGLSLShader *pShader; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + pShader = (CRGLSLShader *) crHashtableSearch(pProgram->currentState.attachedShaders, shader); + if (!pShader) + { + crWarning("Shader %d isn't attached to program %d", shader, program); + return; + } + + crHashtableDelete(pProgram->currentState.attachedShaders, shader, NULL); + + CRASSERT(pShader->refCount>0); + pShader->refCount--; + + if (0==pShader->refCount) + { + CRContext *g = GetCurrentContext(); + crHashtableDelete(g->glsl.shaders, shader, crStateFreeGLSLShader); + } +} + +DECLEXPORT(void) STATE_APIENTRY crStateUseProgram(GLuint program) +{ + CRContext *g = GetCurrentContext(); + + if (program>0) + { + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + g->glsl.activeProgram = pProgram; + } + else + { + g->glsl.activeProgram = NULL; + } +} + +DECLEXPORT(void) STATE_APIENTRY crStateDeleteProgram(GLuint program) +{ + CRContext *g = GetCurrentContext(); + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + if (g->glsl.activeProgram == pProgram) + { + g->glsl.activeProgram = NULL; + } + + crHashtableDelete(g->glsl.programs, program, crStateFreeGLSLProgram); +} + +DECLEXPORT(void) STATE_APIENTRY crStateValidateProgram(GLuint program) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + pProgram->validated = GL_TRUE; +} + +static void crStateCopyShaderCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pRealShader = (CRGLSLShader *) data1; + CRGLSLProgram *pProgram = (CRGLSLProgram *) data2; + CRGLSLShader *pShader; + GLint sLen=0; + + CRASSERT(pRealShader); + pRealShader->refCount++; + + pShader = (CRGLSLShader *) crAlloc(sizeof(*pShader)); + if (!pShader) + { + crWarning("crStateCopyShaderCB: Out of memory!"); + return; + } + + crMemcpy(pShader, pRealShader, sizeof(*pShader)); + + diff_api.GetShaderiv(pShader->hwid, GL_SHADER_SOURCE_LENGTH, &sLen); + if (sLen>0) + { + pShader->source = (GLchar*) crAlloc(sLen); + diff_api.GetShaderSource(pShader->hwid, sLen, NULL, pShader->source); + } + + crHashtableAdd(pProgram->activeState.attachedShaders, key, pShader); +} + +DECLEXPORT(void) STATE_APIENTRY crStateLinkProgram(GLuint program) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + GLuint i; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + pProgram->linked = GL_TRUE; + + /*Free program's active state*/ + if (pProgram->activeState.attachedShaders) + { + crHashtableWalk(pProgram->activeState.attachedShaders, crStateFakeDecRefCountCB, NULL); + crFreeHashtable(pProgram->activeState.attachedShaders, crStateFreeGLSLShader); + pProgram->activeState.attachedShaders = NULL; + } + for (i=0; i<pProgram->activeState.cAttribs; ++i) + { + crFree(pProgram->activeState.pAttribs[i].name); + } + if (pProgram->activeState.pAttribs) crFree(pProgram->activeState.pAttribs); + + /*copy current state to active state*/ + crMemcpy(&pProgram->activeState, &pProgram->currentState, sizeof(CRGLSLProgramState)); + + pProgram->activeState.attachedShaders = crAllocHashtable(); + if (!pProgram->activeState.attachedShaders) + { + crWarning("crStateLinkProgram: Out of memory!"); + return; + } + crHashtableWalk(pProgram->currentState.attachedShaders, crStateCopyShaderCB, pProgram); + + /*that's not a bug, note the memcpy above*/ + if (pProgram->activeState.pAttribs) + { + pProgram->activeState.pAttribs = (CRGLSLAttrib *) crAlloc(pProgram->activeState.cAttribs * sizeof(CRGLSLAttrib)); + } + + for (i=0; i<pProgram->activeState.cAttribs; ++i) + { + crMemcpy(&pProgram->activeState.pAttribs[i], &pProgram->currentState.pAttribs[i], sizeof(CRGLSLAttrib)); + pProgram->activeState.pAttribs[i].name = crStrdup(pProgram->currentState.pAttribs[i].name); + } + +#ifdef IN_GUEST + crStateFreeProgramAttribsLocationCache(pProgram); +#endif + + crStateFreeProgramUniforms(pProgram); +} + +DECLEXPORT(void) STATE_APIENTRY crStateBindAttribLocation(GLuint program, GLuint index, const char * name) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + GLuint i; + CRGLSLAttrib *pAttribs; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + if (index>=CR_MAX_VERTEX_ATTRIBS) + { + crWarning("crStateBindAttribLocation: Index too big %d", index); + return; + } + + for (i=0; i<pProgram->currentState.cAttribs; ++i) + { + if (!crStrcmp(pProgram->currentState.pAttribs[i].name, name)) + { + pProgram->currentState.pAttribs[i].index = index; + return; + } + } + + pAttribs = (CRGLSLAttrib*) crAlloc((pProgram->currentState.cAttribs+1)*sizeof(CRGLSLAttrib)); + if (!pAttribs) + { + crWarning("crStateBindAttribLocation: Out of memory!"); + return; + } + + if (pProgram->currentState.cAttribs) + { + crMemcpy(&pAttribs[0], &pProgram->currentState.pAttribs[0], pProgram->currentState.cAttribs*sizeof(CRGLSLAttrib)); + } + pAttribs[pProgram->currentState.cAttribs].index = index; + pAttribs[pProgram->currentState.cAttribs].name = crStrdup(name); + + pProgram->currentState.cAttribs++; + if (pProgram->currentState.pAttribs) crFree(pProgram->currentState.pAttribs); + pProgram->currentState.pAttribs = pAttribs; +} + +DECLEXPORT(GLint) STATE_APIENTRY crStateGetUniformSize(GLenum type) +{ + GLint size; + + switch (type) + { + case GL_FLOAT: + size = 1; + break; + case GL_FLOAT_VEC2: + size = 2; + break; + case GL_FLOAT_VEC3: + size = 3; + break; + case GL_FLOAT_VEC4: + size = 4; + break; + case GL_INT: + size = 1; + break; + case GL_INT_VEC2: + size = 2; + break; + case GL_INT_VEC3: + size = 3; + break; + case GL_INT_VEC4: + size = 4; + break; + case GL_BOOL: + size = 1; + break; + case GL_BOOL_VEC2: + size = 2; + break; + case GL_BOOL_VEC3: + size = 3; + break; + case GL_BOOL_VEC4: + size = 4; + break; + case GL_FLOAT_MAT2: + size = 8; + break; + case GL_FLOAT_MAT3: + size = 12; + break; + case GL_FLOAT_MAT4: + size = 16; + break; + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_RECT_ARB: + case GL_SAMPLER_2D_RECT_SHADOW_ARB: + size = 1; + break; +#ifdef CR_OPENGL_VERSION_2_1 + case GL_FLOAT_MAT2x3: + size = 8; + break; + case GL_FLOAT_MAT2x4: + size = 8; + break; + case GL_FLOAT_MAT3x2: + size = 12; + break; + case GL_FLOAT_MAT3x4: + size = 12; + break; + case GL_FLOAT_MAT4x2: + size = 16; + break; + case GL_FLOAT_MAT4x3: + size = 16; + break; +#endif + default: + crWarning("crStateGetUniformSize: unknown uniform type 0x%x", (GLint)type); + size = 16; + break; + } + + return size; +} + +DECLEXPORT(GLboolean) STATE_APIENTRY crStateIsIntUniform(GLenum type) +{ + if (GL_INT==type + || GL_INT_VEC2==type + || GL_INT_VEC3==type + || GL_INT_VEC4==type + || GL_BOOL==type + || GL_BOOL_VEC2==type + || GL_BOOL_VEC3==type + || GL_BOOL_VEC4==type + || GL_SAMPLER_1D==type + || GL_SAMPLER_2D==type + || GL_SAMPLER_3D==type + || GL_SAMPLER_CUBE==type + || GL_SAMPLER_1D_SHADOW==type + || GL_SAMPLER_2D_SHADOW==type + || GL_SAMPLER_2D_RECT_ARB==type + || GL_SAMPLER_2D_RECT_SHADOW_ARB==type) + { + return GL_TRUE; + } + else return GL_FALSE; +} + +DECLEXPORT(GLboolean) STATE_APIENTRY crStateIsProgramUniformsCached(GLuint program) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + + if (!pProgram) + { + WARN(("Unknown program %d", program)); + return GL_FALSE; + } + +#ifdef IN_GUEST + return pProgram->bUniformsSynced; +#else + WARN(("crStateIsProgramUniformsCached called on host side!!")); + return GL_FALSE; +#endif +} + +DECLEXPORT(GLboolean) STATE_APIENTRY crStateIsProgramAttribsCached(GLuint program) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + + if (!pProgram) + { + WARN(("Unknown program %d", program)); + return GL_FALSE; + } + +#ifdef IN_GUEST + return pProgram->bAttribsSynced; +#else + WARN(("crStateIsProgramAttribsCached called on host side!!")); + return GL_FALSE; +#endif +} + +/** @todo one of those functions should ignore uniforms starting with "gl"*/ + +#ifdef IN_GUEST +DECLEXPORT(void) STATE_APIENTRY +crStateGLSLProgramCacheUniforms(GLuint program, GLsizei cbData, GLvoid *pData) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + char *pCurrent = pData; + GLsizei cbRead, cbName; + GLuint i; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + if (pProgram->bUniformsSynced) + { + crWarning("crStateGLSLProgramCacheUniforms: this shouldn't happen!"); + crStateFreeProgramUniforms(pProgram); + } + + if (cbData<(GLsizei)sizeof(GLsizei)) + { + crWarning("crStateGLSLProgramCacheUniforms: data too short"); + return; + } + + pProgram->cUniforms = ((GLsizei*)pCurrent)[0]; + pCurrent += sizeof(GLsizei); + cbRead = sizeof(GLsizei); + + /*crDebug("crStateGLSLProgramCacheUniforms: %i active uniforms", pProgram->cUniforms);*/ + + if (pProgram->cUniforms) + { + pProgram->pUniforms = crAlloc(pProgram->cUniforms*sizeof(CRGLSLUniform)); + if (!pProgram->pUniforms) + { + crWarning("crStateGLSLProgramCacheUniforms: no memory"); + pProgram->cUniforms = 0; + return; + } + } + + for (i=0; i<pProgram->cUniforms; ++i) + { + cbRead += sizeof(GLuint)+sizeof(GLsizei); + if (cbRead>cbData) + { + crWarning("crStateGLSLProgramCacheUniforms: out of data reading uniform %i", i); + return; + } + pProgram->pUniforms[i].data = NULL; + pProgram->pUniforms[i].location = ((GLint*)pCurrent)[0]; + pCurrent += sizeof(GLint); + cbName = ((GLsizei*)pCurrent)[0]; + pCurrent += sizeof(GLsizei); + + cbRead += cbName; + if (cbRead>cbData) + { + crWarning("crStateGLSLProgramCacheUniforms: out of data reading uniform's name %i", i); + return; + } + + pProgram->pUniforms[i].name = crStrndup(pCurrent, cbName); + pCurrent += cbName; + + /*crDebug("crStateGLSLProgramCacheUniforms: uniform[%i]=%d, %s", i, pProgram->pUniforms[i].location, pProgram->pUniforms[i].name);*/ + } + + pProgram->bUniformsSynced = GL_TRUE; + + CRASSERT((pCurrent-((char*)pData))==cbRead); + CRASSERT(cbRead==cbData); +} + +DECLEXPORT(void) STATE_APIENTRY +crStateGLSLProgramCacheAttribs(GLuint program, GLsizei cbData, GLvoid *pData) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + char *pCurrent = pData; + GLsizei cbRead, cbName; + GLuint i; + + if (!pProgram) + { + WARN(("Unknown program %d", program)); + return; + } + + if (pProgram->bAttribsSynced) + { + WARN(("crStateGLSLProgramCacheAttribs: this shouldn't happen!")); + crStateFreeProgramAttribsLocationCache(pProgram); + } + + if (cbData<(GLsizei)sizeof(GLsizei)) + { + WARN(("crStateGLSLProgramCacheAttribs: data too short")); + return; + } + + pProgram->cAttribs = ((GLsizei*)pCurrent)[0]; + pCurrent += sizeof(GLsizei); + cbRead = sizeof(GLsizei); + + crDebug("crStateGLSLProgramCacheAttribs: %i active attribs", pProgram->cAttribs); + + if (pProgram->cAttribs) + { + pProgram->pAttribs = crAlloc(pProgram->cAttribs*sizeof(CRGLSLAttrib)); + if (!pProgram->pAttribs) + { + WARN(("crStateGLSLProgramCacheAttribs: no memory")); + pProgram->cAttribs = 0; + return; + } + } + + for (i=0; i<pProgram->cAttribs; ++i) + { + cbRead += sizeof(GLuint)+sizeof(GLsizei); + if (cbRead>cbData) + { + crWarning("crStateGLSLProgramCacheAttribs: out of data reading attrib %i", i); + return; + } + pProgram->pAttribs[i].index = ((GLint*)pCurrent)[0]; + pCurrent += sizeof(GLint); + cbName = ((GLsizei*)pCurrent)[0]; + pCurrent += sizeof(GLsizei); + + cbRead += cbName; + if (cbRead>cbData) + { + crWarning("crStateGLSLProgramCacheAttribs: out of data reading attrib's name %i", i); + return; + } + + pProgram->pAttribs[i].name = crStrndup(pCurrent, cbName); + pCurrent += cbName; + + crDebug("crStateGLSLProgramCacheAttribs: attribs[%i]=%d, %s", i, pProgram->pAttribs[i].index, pProgram->pAttribs[i].name); + } + + pProgram->bAttribsSynced = GL_TRUE; + + CRASSERT((pCurrent-((char*)pData))==cbRead); + CRASSERT(cbRead==cbData); +} +#else /* IN_GUEST */ +static GLboolean crStateGLSLProgramCacheOneUniform(GLuint location, GLsizei cbName, GLchar *pName, + char **pCurrent, GLsizei *pcbWritten, GLsizei maxcbData) +{ + *pcbWritten += sizeof(GLint)+sizeof(GLsizei)+cbName; + if (*pcbWritten>maxcbData) + { + crWarning("crStateGLSLProgramCacheUniforms: buffer too small"); + crFree(pName); + return GL_FALSE; + } + + /*crDebug("crStateGLSLProgramCacheUniforms: uniform[%i]=%s.", location, pName);*/ + + ((GLint*)*pCurrent)[0] = location; + *pCurrent += sizeof(GLint); + ((GLsizei*)*pCurrent)[0] = cbName; + *pCurrent += sizeof(GLsizei); + crMemcpy(*pCurrent, pName, cbName); + *pCurrent += cbName; + + return GL_TRUE; +} + +DECLEXPORT(void) STATE_APIENTRY +crStateGLSLProgramCacheUniforms(GLuint program, GLsizei maxcbData, GLsizei *cbData, GLvoid *pData) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + GLint maxUniformLen = 0, activeUniforms=0, fakeUniformsCount, i, j; + char *pCurrent = pData; + GLsizei cbWritten; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + /* + * OpenGL spec says about GL_ACTIVE_UNIFORM_MAX_LENGTH: + * "If no active uniform variable exist, 0 is returned." + */ + diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen); + if (maxUniformLen > 0) + diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_UNIFORMS, &activeUniforms); + + *cbData = 0; + + cbWritten = sizeof(GLsizei); + if (cbWritten>maxcbData) + { + crWarning("crStateGLSLProgramCacheUniforms: buffer too small"); + return; + } + ((GLsizei*)pCurrent)[0] = activeUniforms; + fakeUniformsCount = activeUniforms; + pCurrent += sizeof(GLsizei); + + crDebug("crStateGLSLProgramCacheUniforms: %i active uniforms", activeUniforms); + + if (activeUniforms>0) + { + /*+8 to make sure our array uniforms with higher indices and [] will fit in as well*/ + GLchar *name = (GLchar *) crAlloc(maxUniformLen+8); + GLenum type; + GLint size; + GLsizei cbName; + GLint location; + + if (!name) + { + crWarning("crStateGLSLProgramCacheUniforms: no memory"); + return; + } + + for (i=0; i<activeUniforms; ++i) + { + diff_api.GetActiveUniform(pProgram->hwid, i, maxUniformLen, &cbName, &size, &type, name); + location = diff_api.GetUniformLocation(pProgram->hwid, name); + + if (!crStateGLSLProgramCacheOneUniform(location, cbName, name, &pCurrent, &cbWritten, maxcbData)) + return; + + /* Only one active uniform variable will be reported for a uniform array by glGetActiveUniform, + * so we insert fake elements for other array elements. + */ + if (size!=1) + { + char *pIndexStr = crStrchr(name, '['); + GLint firstIndex=1; + fakeUniformsCount += size; + + crDebug("crStateGLSLProgramCacheUniforms: expanding array uniform, size=%i", size); + + /*For array uniforms it's valid to query location of 1st element as both uniform and uniform[0]. + *The name returned by glGetActiveUniform is driver dependent, + *atleast it's with [0] on win/ati and without [0] on linux/nvidia. + */ + if (!pIndexStr) + { + pIndexStr = name+cbName; + firstIndex=0; + } + else + { + cbName = pIndexStr-name; + if (!crStateGLSLProgramCacheOneUniform(location, cbName, name, &pCurrent, &cbWritten, maxcbData)) + return; + } + + for (j=firstIndex; j<size; ++j) + { + sprintf(pIndexStr, "[%i]", j); + cbName = crStrlen(name); + + location = diff_api.GetUniformLocation(pProgram->hwid, name); + + if (!crStateGLSLProgramCacheOneUniform(location, cbName, name, &pCurrent, &cbWritten, maxcbData)) + return; + } + } + } + + crFree(name); + } + + if (fakeUniformsCount!=activeUniforms) + { + ((GLsizei*)pData)[0] = fakeUniformsCount; + crDebug("FakeCount %i", fakeUniformsCount); + } + + *cbData = cbWritten; + + CRASSERT((pCurrent-((char*)pData))==cbWritten); +} + +static GLboolean crStateGLSLProgramCacheOneAttrib(GLuint location, GLsizei cbName, GLchar *pName, + char **pCurrent, GLsizei *pcbWritten, GLsizei maxcbData) +{ + *pcbWritten += sizeof(GLint)+sizeof(GLsizei)+cbName; + if (*pcbWritten>maxcbData) + { + WARN(("crStateGLSLProgramCacheOneAttrib: buffer too small")); + crFree(pName); + return GL_FALSE; + } + + crDebug("crStateGLSLProgramCacheOneAttrib: attrib[%i]=%s.", location, pName); + + ((GLint*)*pCurrent)[0] = location; + *pCurrent += sizeof(GLint); + ((GLsizei*)*pCurrent)[0] = cbName; + *pCurrent += sizeof(GLsizei); + crMemcpy(*pCurrent, pName, cbName); + *pCurrent += cbName; + + return GL_TRUE; +} + +DECLEXPORT(void) STATE_APIENTRY +crStateGLSLProgramCacheAttribs(GLuint program, GLsizei maxcbData, GLsizei *cbData, GLvoid *pData) +{ + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + GLint maxAttribLen, activeAttribs=0, fakeAttribsCount, i, j; + char *pCurrent = pData; + GLsizei cbWritten; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return; + } + + diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttribLen); + diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_ATTRIBUTES, &activeAttribs); + + *cbData = 0; + + cbWritten = sizeof(GLsizei); + if (cbWritten>maxcbData) + { + crWarning("crStateGLSLProgramCacheAttribs: buffer too small"); + return; + } + ((GLsizei*)pCurrent)[0] = activeAttribs; + fakeAttribsCount = activeAttribs; + pCurrent += sizeof(GLsizei); + + crDebug("crStateGLSLProgramCacheAttribs: %i active attribs", activeAttribs); + + if (activeAttribs>0) + { + /*+8 to make sure our array attribs with higher indices and [] will fit in as well*/ + GLchar *name = (GLchar *) crAlloc(maxAttribLen+8); + GLenum type; + GLint size; + GLsizei cbName; + GLint location; + + if (!name) + { + crWarning("crStateGLSLProgramCacheAttribs: no memory"); + return; + } + + for (i=0; i<activeAttribs; ++i) + { + diff_api.GetActiveAttrib(pProgram->hwid, i, maxAttribLen, &cbName, &size, &type, name); + location = diff_api.GetAttribLocation(pProgram->hwid, name); + + if (!crStateGLSLProgramCacheOneAttrib(location, cbName, name, &pCurrent, &cbWritten, maxcbData)) + return; + + /* Only one active attrib variable will be reported for a attrib array by glGetActiveAttrib, + * so we insert fake elements for other array elements. + */ + if (size!=1) + { + char *pIndexStr = crStrchr(name, '['); + GLint firstIndex=1; + fakeAttribsCount += size; + + crDebug("crStateGLSLProgramCacheAttribs: expanding array attrib, size=%i", size); + + /*For array attribs it's valid to query location of 1st element as both attrib and attrib[0]. + *The name returned by glGetActiveAttrib is driver dependent, + *atleast it's with [0] on win/ati and without [0] on linux/nvidia. + */ + if (!pIndexStr) + { + pIndexStr = name+cbName; + firstIndex=0; + } + else + { + cbName = pIndexStr-name; + if (!crStateGLSLProgramCacheOneAttrib(location, cbName, name, &pCurrent, &cbWritten, maxcbData)) + return; + } + + for (j=firstIndex; j<size; ++j) + { + sprintf(pIndexStr, "[%i]", j); + cbName = crStrlen(name); + + location = diff_api.GetAttribLocation(pProgram->hwid, name); + + if (!crStateGLSLProgramCacheOneAttrib(location, cbName, name, &pCurrent, &cbWritten, maxcbData)) + return; + } + } + } + + crFree(name); + } + + if (fakeAttribsCount!=activeAttribs) + { + ((GLsizei*)pData)[0] = fakeAttribsCount; + crDebug("FakeCount %i", fakeAttribsCount); + } + + *cbData = cbWritten; + + CRASSERT((pCurrent-((char*)pData))==cbWritten); +} +#endif + +DECLEXPORT(GLint) STATE_APIENTRY crStateGetUniformLocation(GLuint program, const char * name) +{ +#ifdef IN_GUEST + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + GLint result=-1; + GLuint i; + + if (!pProgram) + { + crWarning("Unknown program %d", program); + return -1; + } + + if (!pProgram->bUniformsSynced) + { + crWarning("crStateGetUniformLocation called for uncached uniforms"); + return -1; + } + + for (i=0; i<pProgram->cUniforms; ++i) + { + if (!crStrcmp(name, pProgram->pUniforms[i].name)) + { + result = pProgram->pUniforms[i].location; + break; + } + } + + return result; +#else + crWarning("crStateGetUniformLocation called on host side!!"); + return -1; +#endif +} + +DECLEXPORT(GLint) STATE_APIENTRY crStateGetAttribLocation(GLuint program, const char * name) +{ +#ifdef IN_GUEST + CRGLSLProgram *pProgram = crStateGetProgramObj(program); + GLint result=-1; + GLuint i; + + if (!pProgram) + { + WARN(("Unknown program %d", program)); + return -1; + } + + if (!pProgram->bAttribsSynced) + { + WARN(("crStateGetAttribLocation called for uncached attribs")); + return -1; + } + + for (i=0; i<pProgram->cAttribs; ++i) + { + if (!crStrcmp(name, pProgram->pAttribs[i].name)) + { + result = pProgram->pAttribs[i].index; + break; + } + } + + return result; +#else + crWarning("crStateGetAttribLocation called on host side!!"); + return -1; +#endif +} + +static void crStateGLSLCreateShadersCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pShader = (CRGLSLShader*) data1; + CRContext *ctx = (CRContext *) data2; + (void)ctx; (void)key; + + pShader->hwid = diff_api.CreateShader(pShader->type); +} + +static void crStateFixAttachedShaderHWIDsCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pShader = (CRGLSLShader*) data1; + CRGLSLShader *pRealShader; + CRContext *pCtx = (CRContext *) data2; + + pRealShader = (CRGLSLShader *) crHashtableSearch(pCtx->glsl.shaders, key); + CRASSERT(pRealShader); + + pShader->hwid = pRealShader->hwid; +} + +static void crStateGLSLSyncShadersCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pShader = (CRGLSLShader*) data1; + (void) key; + (void) data2; + + if (pShader->source) + { + diff_api.ShaderSource(pShader->hwid, 1, (const char**)&pShader->source, NULL); + if (pShader->compiled) + diff_api.CompileShader(pShader->hwid); + crFree(pShader->source); + pShader->source = NULL; + } + + if (pShader->deleted) + diff_api.DeleteShader(pShader->hwid); +} + +static void crStateAttachShaderCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pShader = (CRGLSLShader*) data1; + CRGLSLProgram *pProgram = (CRGLSLProgram *) data2; + (void) key; + + if (pShader->source) + { + diff_api.ShaderSource(pShader->hwid, 1, (const char**)&pShader->source, NULL); + if (pShader->compiled) + diff_api.CompileShader(pShader->hwid); + } + + diff_api.AttachShader(pProgram->hwid, pShader->hwid); +} + +static void crStateDetachShaderCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLShader *pShader = (CRGLSLShader*) data1; + CRGLSLProgram *pProgram = (CRGLSLProgram *) data2; + (void) key; + + diff_api.DetachShader(pProgram->hwid, pShader->hwid); +} + +static void crStateGLSLCreateProgramCB(unsigned long key, void *data1, void *data2) +{ + CRGLSLProgram *pProgram = (CRGLSLProgram*) data1; + CRContext *ctx = (CRContext *) data2; + GLuint i; + (void)key; + + pProgram->hwid = diff_api.CreateProgram(); + + if (pProgram->linked) + { + CRASSERT(pProgram->activeState.attachedShaders); + + crHashtableWalk(pProgram->activeState.attachedShaders, crStateFixAttachedShaderHWIDsCB, ctx); + crHashtableWalk(pProgram->activeState.attachedShaders, crStateAttachShaderCB, pProgram); + + for (i=0; i<pProgram->activeState.cAttribs; ++i) + { + diff_api.BindAttribLocation(pProgram->hwid, pProgram->activeState.pAttribs[i].index, pProgram->activeState.pAttribs[i].name); + } + + if (pProgram->validated) + diff_api.ValidateProgram(pProgram->hwid); + + diff_api.LinkProgram(pProgram->hwid); + } + + diff_api.UseProgram(pProgram->hwid); + + for (i=0; i<pProgram->cUniforms; ++i) + { + GLint location; + GLfloat *pFdata = (GLfloat*)pProgram->pUniforms[i].data; + GLint *pIdata = (GLint*)pProgram->pUniforms[i].data; + + location = diff_api.GetUniformLocation(pProgram->hwid, pProgram->pUniforms[i].name); + switch (pProgram->pUniforms[i].type) + { + case GL_FLOAT: + diff_api.Uniform1fv(location, 1, pFdata); + break; + case GL_FLOAT_VEC2: + diff_api.Uniform2fv(location, 1, pFdata); + break; + case GL_FLOAT_VEC3: + diff_api.Uniform3fv(location, 1, pFdata); + break; + case GL_FLOAT_VEC4: + diff_api.Uniform4fv(location, 1, pFdata); + break; + case GL_INT: + case GL_BOOL: + diff_api.Uniform1iv(location, 1, pIdata); + break; + case GL_INT_VEC2: + case GL_BOOL_VEC2: + diff_api.Uniform2iv(location, 1, pIdata); + break; + case GL_INT_VEC3: + case GL_BOOL_VEC3: + diff_api.Uniform3iv(location, 1, pIdata); + break; + case GL_INT_VEC4: + case GL_BOOL_VEC4: + diff_api.Uniform4iv(location, 1, pIdata); + break; + case GL_FLOAT_MAT2: + diff_api.UniformMatrix2fv(location, 1, GL_FALSE, pFdata); + break; + case GL_FLOAT_MAT3: + diff_api.UniformMatrix3fv(location, 1, GL_FALSE, pFdata); + break; + case GL_FLOAT_MAT4: + diff_api.UniformMatrix4fv(location, 1, GL_FALSE, pFdata); + break; + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_RECT_ARB: + case GL_SAMPLER_2D_RECT_SHADOW_ARB: + diff_api.Uniform1iv(location, 1, pIdata); + break; +#ifdef CR_OPENGL_VERSION_2_1 + case GL_FLOAT_MAT2x3: + diff_api.UniformMatrix2x3fv(location, 1, GL_FALSE, pFdata); + break; + case GL_FLOAT_MAT2x4: + diff_api.UniformMatrix2x4fv(location, 1, GL_FALSE, pFdata); + break; + case GL_FLOAT_MAT3x2: + diff_api.UniformMatrix3x2fv(location, 1, GL_FALSE, pFdata); + break; + case GL_FLOAT_MAT3x4: + diff_api.UniformMatrix3x4fv(location, 1, GL_FALSE, pFdata); + break; + case GL_FLOAT_MAT4x2: + diff_api.UniformMatrix4x2fv(location, 1, GL_FALSE, pFdata); + break; + case GL_FLOAT_MAT4x3: + diff_api.UniformMatrix4x3fv(location, 1, GL_FALSE, pFdata); + break; +#endif + default: + crWarning("crStateGLSLCreateProgramCB: unknown uniform type 0x%x", (GLint)pProgram->pUniforms[i].type); + break; + } + crFree(pProgram->pUniforms[i].data); + crFree(pProgram->pUniforms[i].name); + } /*for (i=0; i<pProgram->cUniforms; ++i)*/ + if (pProgram->pUniforms) crFree(pProgram->pUniforms); + pProgram->pUniforms = NULL; + pProgram->cUniforms = 0; + + crHashtableWalk(pProgram->activeState.attachedShaders, crStateDetachShaderCB, pProgram); + crHashtableWalk(pProgram->currentState.attachedShaders, crStateAttachShaderCB, pProgram); +} + +DECLEXPORT(void) STATE_APIENTRY crStateGLSLSwitch(CRContext *from, CRContext *to) +{ + GLboolean fForceUseProgramSet = GL_FALSE; + if (to->glsl.bResyncNeeded) + { + to->glsl.bResyncNeeded = GL_FALSE; + + crHashtableWalk(to->glsl.shaders, crStateGLSLCreateShadersCB, to); + + crHashtableWalk(to->glsl.programs, crStateGLSLCreateProgramCB, to); + + /* crStateGLSLCreateProgramCB changes the current program, ensure we have the proper program re-sored */ + fForceUseProgramSet = GL_TRUE; + + crHashtableWalk(to->glsl.shaders, crStateGLSLSyncShadersCB, NULL); + } + + if (to->glsl.activeProgram != from->glsl.activeProgram || fForceUseProgramSet) + { + diff_api.UseProgram(to->glsl.activeProgram ? to->glsl.activeProgram->hwid : 0); + } +} |