/* $Id: state_glsl.c $ */ /** @file * VBox OpenGL: GLSL state tracking */ /* * Copyright (C) 2009-2012 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); } static void crStateFreeProgramAttribs(CRGLSLProgram* pProgram) { GLuint i; for (i=0; iactiveState.cAttribs; ++i) { crFree(pProgram->activeState.pAttribs[i].name); } for (i=0; icurrentState.cAttribs; ++i) { crFree(pProgram->currentState.pAttribs[i].name); } if (pProgram->activeState.pAttribs) crFree(pProgram->activeState.pAttribs); if (pProgram->currentState.pAttribs) crFree(pProgram->currentState.pAttribs); } static void crStateFreeProgramUniforms(CRGLSLProgram* pProgram) { GLuint i; for (i=0; icUniforms; ++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; 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->bUniformsSynced = 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) { 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; iactiveState.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; iactiveState.cAttribs; ++i) { crMemcpy(&pProgram->activeState.pAttribs[i], &pProgram->currentState.pAttribs[i], sizeof(CRGLSLAttrib)); pProgram->activeState.pAttribs[i].name = crStrdup(pProgram->currentState.pAttribs[i].name); } 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; icurrentState.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) { crWarning("Unknown program %d", program); return GL_FALSE; } #ifdef IN_GUEST return pProgram->bUniformsSynced; #else crWarning("crStateIsProgramUniformsCached 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 (cbDatacUniforms = ((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; icUniforms; ++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); } #else 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, activeUniforms=0, fakeUniformsCount, i, j; char *pCurrent = pData; GLsizei cbWritten; if (!pProgram) { crWarning("Unknown program %d", program); return; } diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen); 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; ihwid, 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; jhwid, 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); } #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; icUniforms; ++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 } static void crStateGLSLCreateShadersCB(unsigned long key, void *data1, void *data2) { CRGLSLShader *pShader = (CRGLSLShader*) data1; CRContext *ctx = (CRContext *) data2; 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; 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; iactiveState.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; icUniforms; ++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; icUniforms; ++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); } }