From acadf3f241be9270799d4dd1007303200d047fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Wed, 14 Feb 2007 17:46:54 +0100 Subject: [PATCH] wined3d: Replace the light chain with a hashmap. --- dlls/wined3d/device.c | 374 ++++++++++----------------------- dlls/wined3d/state.c | 12 +- dlls/wined3d/stateblock.c | 137 ++++++++---- dlls/wined3d/wined3d_private.h | 11 +- 4 files changed, 216 insertions(+), 318 deletions(-) diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 65af9eeb0a3..9a0b1b5db8e 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -584,6 +584,10 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateStateBlock(IWineD3DDevice* iface, D3DCREATEOBJECTINSTANCE(object, StateBlock) object->blockType = Type; + for(i = 0; i < LIGHTMAP_SIZE; i++) { + list_init(&object->lightMap[i]); + } + /* Special case - Used during initialization to produce a placeholder stateblock so other functions called can update a state block */ if (Type == WINED3DSBT_INIT) { @@ -613,7 +617,16 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateStateBlock(IWineD3DDevice* iface, TRACE("ALL => Pretend everything has changed\n"); stateblock_savedstates_set((IWineD3DStateBlock*) object, &object->changed, TRUE); - + + /* Lights are not part of the changed / set structure */ + for(j = 0; j < LIGHTMAP_SIZE; j++) { + struct list *e; + LIST_FOR_EACH(e, &object->lightMap[j]) { + PLIGHTINFOEL *light = LIST_ENTRY(e, PLIGHTINFOEL, entry); + light->changed = TRUE; + light->enabledChanged = TRUE; + } + } } else if (Type == WINED3DSBT_PIXELSTATE) { TRACE("PIXELSTATE => Pretend all pixel shates have changed\n"); @@ -673,33 +686,14 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateStateBlock(IWineD3DDevice* iface, } } - /* Duplicate light chain */ - { - PLIGHTINFOEL *src = NULL; - PLIGHTINFOEL *dst = NULL; - PLIGHTINFOEL *newEl = NULL; - src = This->stateBlock->lights; - object->lights = NULL; - - - while (src) { - newEl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL)); - if (newEl == NULL) return WINED3DERR_OUTOFVIDEOMEMORY; - memcpy(newEl, src, sizeof(PLIGHTINFOEL)); - newEl->prev = dst; - newEl->changed = TRUE; - newEl->enabledChanged = TRUE; - if (dst == NULL) { - object->lights = newEl; - } else { - dst->next = newEl; + for(j = 0; j < LIGHTMAP_SIZE; j++) { + struct list *e; + LIST_FOR_EACH(e, &object->lightMap[j]) { + PLIGHTINFOEL *light = LIST_ENTRY(e, PLIGHTINFOEL, entry); + light->changed = TRUE; + light->enabledChanged = TRUE; } - dst = newEl; - src = src->next; } - - } - } else { FIXME("Unrecognized state block type %d\n", Type); } @@ -2329,73 +2323,30 @@ static HRESULT WINAPI IWineD3DDeviceImpl_MultiplyTransform(IWineD3DDevice *iface static HRESULT WINAPI IWineD3DDeviceImpl_SetLight(IWineD3DDevice *iface, DWORD Index, CONST WINED3DLIGHT* pLight) { float rho; - PLIGHTINFOEL *object, *temp; + PLIGHTINFOEL *object = NULL; + UINT Hi = LIGHTMAP_HASHFUNC(Index); + struct list *e; IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; - TRACE("(%p) : Idx(%d), pLight(%p)\n", This, Index, pLight); + TRACE("(%p) : Idx(%d), pLight(%p). Hash index is %d\n", This, Index, pLight, Hi); - /* If recording state block, just add to end of lights chain */ - if (This->isRecordingState) { - object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL)); - if (NULL == object) { - return WINED3DERR_OUTOFVIDEOMEMORY; - } - memcpy(&object->OriginalParms, pLight, sizeof(WINED3DLIGHT)); - object->OriginalIndex = Index; - object->glIndex = -1; - object->changed = TRUE; - - /* Add to the END of the chain of lights changes to be replayed */ - if (This->updateStateBlock->lights == NULL) { - This->updateStateBlock->lights = object; - } else { - temp = This->updateStateBlock->lights; - while (temp->next != NULL) temp=temp->next; - temp->next = object; - } - TRACE("Recording... not performing anything more\n"); - return WINED3D_OK; + LIST_FOR_EACH(e, &This->updateStateBlock->lightMap[Hi]) { + object = LIST_ENTRY(e, PLIGHTINFOEL, entry); + if(object->OriginalIndex == Index) break; + object = NULL; } - /* Ok, not recording any longer so do real work */ - object = This->stateBlock->lights; - while (object != NULL && object->OriginalIndex != Index) object = object->next; - - /* If we didn't find it in the list of lights, time to add it */ - if (object == NULL) { - PLIGHTINFOEL *insertAt,*prevPos; - - object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL)); - if (NULL == object) { - return WINED3DERR_OUTOFVIDEOMEMORY; + if(!object) { + TRACE("Adding new light\n"); + object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); + if(!object) { + ERR("Out of memory error when allocating a light\n"); + return E_OUTOFMEMORY; } - object->OriginalIndex = Index; + list_add_head(&This->updateStateBlock->lightMap[Hi], &object->entry); object->glIndex = -1; - - /* Add it to the front of list with the idea that lights will be changed as needed - BUT after any lights currently assigned GL indexes */ - insertAt = This->stateBlock->lights; - prevPos = NULL; - while (insertAt != NULL && insertAt->glIndex != -1) { - prevPos = insertAt; - insertAt = insertAt->next; - } - - if (insertAt == NULL && prevPos == NULL) { /* Start of list */ - This->stateBlock->lights = object; - } else if (insertAt == NULL) { /* End of list */ - prevPos->next = object; - object->prev = prevPos; - } else { /* Middle of chain */ - if (prevPos == NULL) { - This->stateBlock->lights = object; - } else { - prevPos->next = object; - } - object->prev = prevPos; - object->next = insertAt; - insertAt->prev = object; - } + object->OriginalIndex = Index; + object->changed = TRUE; } /* Initialize the object */ @@ -2471,7 +2422,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetLight(IWineD3DDevice *iface, DWORD I } /* Update the live definitions if the light is currently assigned a glIndex */ - if (object->glIndex != -1) { + if (object->glIndex != -1 && !This->isRecordingState) { setup_light(iface, object->glIndex, object); } return WINED3D_OK; @@ -2480,11 +2431,15 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetLight(IWineD3DDevice *iface, DWORD I static HRESULT WINAPI IWineD3DDeviceImpl_GetLight(IWineD3DDevice *iface, DWORD Index, WINED3DLIGHT* pLight) { PLIGHTINFOEL *lightInfo = NULL; IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + DWORD Hi = LIGHTMAP_HASHFUNC(Index); + struct list *e; TRACE("(%p) : Idx(%d), pLight(%p)\n", This, Index, pLight); - /* Locate the light in the live lights */ - lightInfo = This->stateBlock->lights; - while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next; + LIST_FOR_EACH(e, &This->stateBlock->lightMap[Hi]) { + lightInfo = LIST_ENTRY(e, PLIGHTINFOEL, entry); + if(lightInfo->OriginalIndex == Index) break; + lightInfo = NULL; + } if (lightInfo == NULL) { TRACE("Light information requested but light not defined\n"); @@ -2502,38 +2457,19 @@ static HRESULT WINAPI IWineD3DDeviceImpl_GetLight(IWineD3DDevice *iface, DWORD I static HRESULT WINAPI IWineD3DDeviceImpl_SetLightEnable(IWineD3DDevice *iface, DWORD Index, BOOL Enable) { PLIGHTINFOEL *lightInfo = NULL; IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + UINT Hi = LIGHTMAP_HASHFUNC(Index); + struct list *e; TRACE("(%p) : Idx(%d), enable? %d\n", This, Index, Enable); /* Tests show true = 128...not clear why */ - Enable = Enable? 128: 0; - /* If recording state block, just add to end of lights chain with changedEnable set to true */ - if (This->isRecordingState) { - lightInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL)); - if (NULL == lightInfo) { - return WINED3DERR_OUTOFVIDEOMEMORY; - } - lightInfo->OriginalIndex = Index; - lightInfo->glIndex = -1; - lightInfo->enabledChanged = TRUE; - lightInfo->lightEnabled = Enable; - - /* Add to the END of the chain of lights changes to be replayed */ - if (This->updateStateBlock->lights == NULL) { - This->updateStateBlock->lights = lightInfo; - } else { - PLIGHTINFOEL *temp = This->updateStateBlock->lights; - while (temp->next != NULL) temp=temp->next; - temp->next = lightInfo; - } - TRACE("Recording... not performing anything more\n"); - return WINED3D_OK; + LIST_FOR_EACH(e, &This->updateStateBlock->lightMap[Hi]) { + lightInfo = LIST_ENTRY(e, PLIGHTINFOEL, entry); + if(lightInfo->OriginalIndex == Index) break; + lightInfo = NULL; } - - /* Not recording... So, locate the light in the live lights */ - lightInfo = This->stateBlock->lights; - while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next; + TRACE("Found light: %p\n", lightInfo); /* Special case - enabling an undefined light creates one with a strict set of parms! */ if (lightInfo == NULL) { @@ -2542,164 +2478,62 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetLightEnable(IWineD3DDevice *iface, D IWineD3DDeviceImpl_SetLight(iface, Index, &WINED3D_default_light); /* Search for it again! Should be fairly quick as near head of list */ - lightInfo = This->stateBlock->lights; - while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next; + LIST_FOR_EACH(e, &This->updateStateBlock->lightMap[Hi]) { + lightInfo = LIST_ENTRY(e, PLIGHTINFOEL, entry); + if(lightInfo->OriginalIndex == Index) break; + lightInfo = NULL; + } if (lightInfo == NULL) { FIXME("Adding default lights has failed dismally\n"); return WINED3DERR_INVALIDCALL; } } - /* OK, we now have a light... */ - if (!Enable) { - - /* If we are disabling it, check it was enabled, and - still only do something if it has assigned a glIndex (which it should have!) */ - if ((lightInfo->lightEnabled) && (lightInfo->glIndex != -1)) { - TRACE("Disabling light set up at gl idx %d\n", lightInfo->glIndex); - ENTER_GL(); - glDisable(GL_LIGHT0 + lightInfo->glIndex); - checkGLcall("glDisable GL_LIGHT0+Index"); - LEAVE_GL(); - } else { - TRACE("Nothing to do as light was not enabled\n"); - } - lightInfo->lightEnabled = Enable; - } else { - - /* We are enabling it. If it is enabled, it's really simple */ - if (lightInfo->lightEnabled) { - /* nop */ - TRACE("Nothing to do as light was enabled\n"); - - /* If it already has a glIndex, it's still simple */ - } else if (lightInfo->glIndex != -1) { - TRACE("Reusing light as already set up at gl idx %d\n", lightInfo->glIndex); - lightInfo->lightEnabled = Enable; - ENTER_GL(); - glEnable(GL_LIGHT0 + lightInfo->glIndex); - checkGLcall("glEnable GL_LIGHT0+Index already setup"); - LEAVE_GL(); - - /* Otherwise got to find space - lights are ordered gl indexes first */ - } else { - PLIGHTINFOEL *bsf = NULL; - PLIGHTINFOEL *pos = This->stateBlock->lights; - PLIGHTINFOEL *prev = NULL; - int Index= 0; - int glIndex = -1; - - /* Try to minimize changes as much as possible */ - while (pos != NULL && pos->glIndex != -1 && Index < This->maxConcurrentLights) { - - /* Try to remember which index can be replaced if necessary */ - if (bsf==NULL && !pos->lightEnabled) { - /* Found a light we can replace, save as best replacement */ - bsf = pos; - } - - /* Step to next space */ - prev = pos; - pos = pos->next; - Index ++; + lightInfo->enabledChanged = TRUE; + if(!Enable) { + if(lightInfo->glIndex != -1) { + if(!This->isRecordingState) { + ENTER_GL(); + glDisable(GL_LIGHT0 + lightInfo->glIndex); + checkGLcall("glDisable GL_LIGHT0+Index"); + LEAVE_GL(); } - /* If we have too many active lights, fail the call */ - if ((Index == This->maxConcurrentLights) && (bsf == NULL)) { - FIXME("Program requests too many concurrent lights.\n"); + This->stateBlock->activeLights[lightInfo->glIndex] = NULL; + lightInfo->glIndex = -1; + } else { + TRACE("Light already disabled, nothing to do\n"); + } + } else { + if (lightInfo->glIndex != -1) { + /* nop */ + TRACE("Nothing to do as light was enabled\n"); + } else { + int i; + /* Find a free gl light */ + for(i = 0; i < This->maxConcurrentLights; i++) { + if(This->stateBlock->activeLights[i] == NULL) { + This->stateBlock->activeLights[i] = lightInfo; + lightInfo->glIndex = i; + break; + } + } + if(lightInfo->glIndex == -1) { + ERR("Too many concurrently active lights\n"); return WINED3DERR_INVALIDCALL; + } - /* If we have allocated all lights, but not all are enabled, - reuse one which is not enabled */ - } else if (Index == This->maxConcurrentLights) { - /* use bsf - Simply swap the new light and the BSF one */ - PLIGHTINFOEL *bsfNext = bsf->next; - PLIGHTINFOEL *bsfPrev = bsf->prev; - - /* Sort out ends */ - if (lightInfo->next != NULL) lightInfo->next->prev = bsf; - if (bsf->prev != NULL) { - bsf->prev->next = lightInfo; - } else { - This->stateBlock->lights = lightInfo; - } - - /* If not side by side, lots of chains to update */ - if (bsf->next != lightInfo) { - lightInfo->prev->next = bsf; - bsf->next->prev = lightInfo; - bsf->next = lightInfo->next; - bsf->prev = lightInfo->prev; - lightInfo->next = bsfNext; - lightInfo->prev = bsfPrev; - - } else { - /* Simple swaps */ - bsf->prev = lightInfo; - bsf->next = lightInfo->next; - lightInfo->next = bsf; - lightInfo->prev = bsfPrev; - } - - - /* Update states */ - glIndex = bsf->glIndex; - bsf->glIndex = -1; - lightInfo->glIndex = glIndex; - lightInfo->lightEnabled = Enable; - - /* Finally set up the light in gl itself */ - TRACE("Replacing light which was set up at gl idx %d\n", lightInfo->glIndex); + /* i == lightInfo->glIndex */ + if(!This->isRecordingState) { + setup_light(iface, i, lightInfo); ENTER_GL(); - setup_light(iface, glIndex, lightInfo); - glEnable(GL_LIGHT0 + glIndex); - checkGLcall("glEnable GL_LIGHT0 new setup"); + glEnable(GL_LIGHT0 + i); + checkGLcall("glEnable(GL_LIGHT0 + i)"); LEAVE_GL(); - - /* If we reached the end of the allocated lights, with space in the - gl lights, setup a new light */ - } else if (pos->glIndex == -1) { - - /* We reached the end of the allocated gl lights, so already - know the index of the next one! */ - glIndex = Index; - lightInfo->glIndex = glIndex; - lightInfo->lightEnabled = Enable; - - /* In an ideal world, it's already in the right place */ - if (lightInfo->prev == NULL || lightInfo->prev->glIndex!=-1) { - /* No need to move it */ - } else { - /* Remove this light from the list */ - lightInfo->prev->next = lightInfo->next; - if (lightInfo->next != NULL) { - lightInfo->next->prev = lightInfo->prev; - } - - /* Add in at appropriate place (inbetween prev and pos) */ - lightInfo->prev = prev; - lightInfo->next = pos; - if (prev == NULL) { - This->stateBlock->lights = lightInfo; - } else { - prev->next = lightInfo; - } - if (pos != NULL) { - pos->prev = lightInfo; - } - } - - /* Finally set up the light in gl itself */ - TRACE("Defining new light at gl idx %d\n", lightInfo->glIndex); - ENTER_GL(); - setup_light(iface, glIndex, lightInfo); - glEnable(GL_LIGHT0 + glIndex); - checkGLcall("glEnable GL_LIGHT0 new setup"); - LEAVE_GL(); - } } } + return WINED3D_OK; } @@ -2707,17 +2541,22 @@ static HRESULT WINAPI IWineD3DDeviceImpl_GetLightEnable(IWineD3DDevice *iface, D PLIGHTINFOEL *lightInfo = NULL; IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + struct list *e; + UINT Hi = LIGHTMAP_HASHFUNC(Index); TRACE("(%p) : for idx(%d)\n", This, Index); - /* Locate the light in the live lights */ - lightInfo = This->stateBlock->lights; - while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next; + LIST_FOR_EACH(e, &This->stateBlock->lightMap[Hi]) { + lightInfo = LIST_ENTRY(e, PLIGHTINFOEL, entry); + if(lightInfo->OriginalIndex == Index) break; + lightInfo = NULL; + } if (lightInfo == NULL) { TRACE("Light enabled state requested but light not defined\n"); return WINED3DERR_INVALIDCALL; } - *pEnable = lightInfo->lightEnabled; + /* true is 128 according to SetLightEnable */ + *pEnable = lightInfo->glIndex != -1 ? 128 : 0; return WINED3D_OK; } @@ -4327,7 +4166,8 @@ static HRESULT WINAPI IWineD3DDeviceImpl_BeginStateBlock(IWineD3DDevice *iface) IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; IWineD3DStateBlockImpl *object; HRESULT temp_result; - + int i; + TRACE("(%p)\n", This); if (This->isRecordingState) { @@ -4346,7 +4186,11 @@ static HRESULT WINAPI IWineD3DDeviceImpl_BeginStateBlock(IWineD3DDevice *iface) object->blockType = WINED3DSBT_ALL; object->ref = 1; object->lpVtbl = &IWineD3DStateBlock_Vtbl; - + + for(i = 0; i < LIGHTMAP_SIZE; i++) { + list_init(&object->lightMap[i]); + } + temp_result = allocate_shader_constants(object); if (WINED3D_OK != temp_result) return temp_result; diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c index 9893dcbc780..ee898c24752 100644 --- a/dlls/wined3d/state.c +++ b/dlls/wined3d/state.c @@ -1987,7 +1987,7 @@ static void transform_view(DWORD state, IWineD3DStateBlockImpl *stateblock, Wine * NOTE2: Apparently texture transforms do NOT need reapplying */ - PLIGHTINFOEL *lightChain = NULL; + PLIGHTINFOEL *light = NULL; glMatrixMode(GL_MODELVIEW); checkGLcall("glMatrixMode(GL_MODELVIEW)"); @@ -1995,13 +1995,13 @@ static void transform_view(DWORD state, IWineD3DStateBlockImpl *stateblock, Wine checkGLcall("glLoadMatrixf(...)"); /* Reset lights. TODO: Call light apply func */ - lightChain = stateblock->lights; - while (lightChain && lightChain->glIndex != -1) { - glLightfv(GL_LIGHT0 + lightChain->glIndex, GL_POSITION, lightChain->lightPosn); + for(k = 0; k < stateblock->wineD3DDevice->maxConcurrentLights; k++) { + light = stateblock->activeLights[k]; + if(!light) continue; + glLightfv(GL_LIGHT0 + light->glIndex, GL_POSITION, light->lightPosn); checkGLcall("glLightfv posn"); - glLightfv(GL_LIGHT0 + lightChain->glIndex, GL_SPOT_DIRECTION, lightChain->lightDirn); + glLightfv(GL_LIGHT0 + light->glIndex, GL_SPOT_DIRECTION, light->lightDirn); checkGLcall("glLightfv dirn"); - lightChain = lightChain->next; } /* Reset Clipping Planes if clipping is enabled. TODO: Call clipplane apply func */ diff --git a/dlls/wined3d/stateblock.c b/dlls/wined3d/stateblock.c index 093c83d8714..6112f59249c 100644 --- a/dlls/wined3d/stateblock.c +++ b/dlls/wined3d/stateblock.c @@ -140,6 +140,7 @@ void stateblock_savedstates_set( void stateblock_copy( IWineD3DStateBlock* destination, IWineD3DStateBlock* source) { + int l; IWineD3DStateBlockImpl *This = (IWineD3DStateBlockImpl *)source; IWineD3DStateBlockImpl *Dest = (IWineD3DStateBlockImpl *)destination; @@ -164,7 +165,7 @@ void stateblock_copy( Dest->streamIsUP = This->streamIsUP; Dest->pIndexData = This->pIndexData; Dest->baseVertexIndex = This->baseVertexIndex; - Dest->lights = This->lights; + /* Dest->lights = This->lights; */ Dest->clip_status = This->clip_status; Dest->viewport = This->viewport; Dest->material = This->material; @@ -172,6 +173,25 @@ void stateblock_copy( Dest->glsl_program = This->glsl_program; memcpy(&Dest->scissorRect, &This->scissorRect, sizeof(Dest->scissorRect)); + /* Lights */ + memset(This->activeLights, 0, sizeof(This->activeLights)); + for(l = 0; l < LIGHTMAP_SIZE; l++) { + struct list *e1, *e2; + LIST_FOR_EACH_SAFE(e1, e2, &Dest->lightMap[l]) { + PLIGHTINFOEL *light = LIST_ENTRY(e1, PLIGHTINFOEL, entry); + list_remove(&light->entry); + HeapFree(GetProcessHeap(), 0, light); + } + + LIST_FOR_EACH(e1, &This->lightMap[l]) { + PLIGHTINFOEL *light = LIST_ENTRY(e1, PLIGHTINFOEL, entry), *light2; + light2 = HeapAlloc(GetProcessHeap(), 0, sizeof(*light)); + memcpy(light2, light, sizeof(*light)); + list_add_tail(&This->lightMap[l], &light2->entry); + if(light2->glIndex != -1) This->activeLights[light2->glIndex] = light2; + } + } + /* Fixed size arrays */ memcpy(Dest->vertexShaderConstantB, This->vertexShaderConstantB, sizeof(BOOL) * MAX_CONST_B); memcpy(Dest->vertexShaderConstantI, This->vertexShaderConstantI, sizeof(INT) * MAX_CONST_I * 4); @@ -230,10 +250,10 @@ static ULONG WINAPI IWineD3DStateBlockImpl_Release(IWineD3DStateBlock *iface) { if (!refCount) { constant_entry *constant, *constant2; + int counter; /* type 0 represents the primary stateblock, so free all the resources */ if (This->blockType == WINED3DSBT_INIT) { - int counter; FIXME("Releasing primary stateblock\n"); /* NOTE: according to MSDN: The application is responsible for making sure the texture references are cleared down */ @@ -248,6 +268,15 @@ static ULONG WINAPI IWineD3DStateBlockImpl_Release(IWineD3DStateBlock *iface) { } + for(counter = 0; counter < LIGHTMAP_SIZE; counter++) { + struct list *e1, *e2; + LIST_FOR_EACH_SAFE(e1, e2, &This->lightMap[counter]) { + PLIGHTINFOEL *light = LIST_ENTRY(e1, PLIGHTINFOEL, entry); + list_remove(&light->entry); + HeapFree(GetProcessHeap(), 0, light); + } + } + HeapFree(GetProcessHeap(), 0, This->vertexShaderConstantF); HeapFree(GetProcessHeap(), 0, This->set.vertexShaderConstantsF); HeapFree(GetProcessHeap(), 0, This->changed.vertexShaderConstantsF); @@ -298,23 +327,27 @@ static HRESULT WINAPI IWineD3DStateBlockImpl_Capture(IWineD3DStateBlock *iface) /* If not recorded, then update can just recapture */ if (/*TODO: 'magic' statetype, replace with BOOL This->blockType == D3DSBT_RECORDED */ 0) { IWineD3DStateBlockImpl* tmpBlock; - PLIGHTINFOEL *tmp = This->lights; + int i; IWineD3DDevice_CreateStateBlock((IWineD3DDevice *)This->wineD3DDevice, This->blockType, (IWineD3DStateBlock**) &tmpBlock, NULL/*parent*/); - /* Note just swap the light chains over so when deleting, the old one goes */ memcpy(This, tmpBlock, sizeof(IWineD3DStateBlockImpl)); - tmpBlock->lights = tmp; - /* Delete the temporary one (which points to the old light chain though */ + /* Move the light elements from the tmpBlock to This. No need to duplicate them, but they have to be removed from tmpBlock + * and the pointers updated for the base element in This. + * + * No need to update This->activeLights because the lights objects are untouched(same address) + */ + for(i = 0; i < LIGHTMAP_SIZE; i++) { + list_init(&This->lightMap[i]); /* This element contains rubish due to the memcpy */ + list_move_tail(&This->lightMap[i], &tmpBlock->lightMap[i]); /* Cleans the list entry in tmpBlock */ + } + IWineD3DStateBlock_Release((IWineD3DStateBlock *)tmpBlock); - /*IDirect3DDevice_DeleteStateBlock(pDevice, tmpBlock);*/ } else { unsigned int i, j; - PLIGHTINFOEL *src; - /* Recorded => Only update 'changed' values */ if (This->vertexShader != targetStateBlock->vertexShader) { TRACE("Updating vertex shader from %p to %p\n", This->vertexShader, targetStateBlock->vertexShader); @@ -363,37 +396,52 @@ static HRESULT WINAPI IWineD3DStateBlockImpl_Capture(IWineD3DStateBlock *iface) This->vertexShaderConstantB[i] = targetStateBlock->vertexShaderConstantB[i]; } } - + /* Lights... For a recorded state block, we just had a chain of actions to perform, so we need to walk that chain and update any actions which differ */ - src = This->lights; - while (src != NULL) { - PLIGHTINFOEL *realLight = NULL; + for(i = 0; i < LIGHTMAP_SIZE; i++) { + struct list *e, *f; + LIST_FOR_EACH(e, &This->lightMap[i]) { + BOOL updated = FALSE; + PLIGHTINFOEL *src = LIST_ENTRY(e, PLIGHTINFOEL, entry), *realLight; + if(!src->changed || !src->enabledChanged) continue; - /* Locate the light in the live lights */ - realLight = targetStateBlock->lights; - while (realLight != NULL && realLight->OriginalIndex != src->OriginalIndex) realLight = realLight->next; + /* Look up the light in the destination */ + LIST_FOR_EACH(f, &targetStateBlock->lightMap[i]) { + realLight = LIST_ENTRY(f, PLIGHTINFOEL, entry); + if(realLight->OriginalIndex == src->OriginalIndex) { + if(src->changed) { + memcpy(&src->OriginalParms, &realLight->OriginalParms, sizeof(src->OriginalParms)); + } + if(src->enabledChanged) { + /* Need to double check because enabledChanged does not catch enabled -> disabled -> enabled + * or disabled -> enabled -> disabled changes + */ + if(realLight->glIndex == -1 && src->glIndex != -1) { + /* Light disabled */ + This->activeLights[src->glIndex] = NULL; + } else if(realLight->glIndex != -1 && src->glIndex == -1){ + /* Light enabled */ + This->activeLights[realLight->glIndex] = src; + } + src->glIndex = realLight->glIndex; + } + updated = TRUE; + break; + } + } - /* If 'changed' then its a SetLight command. Rather than comparing to see - if the OriginalParms have changed and then copy them (twice through - memory) just do the copy */ - if (src->changed) { - - /* If the light exists, copy its parameters, otherwise copy the default parameters */ - const WINED3DLIGHT* params = realLight? &realLight->OriginalParms: &WINED3D_default_light; - TRACE("Updating lights for light %d\n", src->OriginalIndex); - memcpy(&src->OriginalParms, params, sizeof(*params)); + if(updated) { + /* Found a light, all done, proceed with next hash entry */ + continue; + } else if(src->changed) { + /* Otherwise assign defaul params */ + memcpy(&src->OriginalParms, &WINED3D_default_light, sizeof(src->OriginalParms)); + } else { + /* Not enabled by default */ + src->glIndex = -1; + } } - - /* If 'enabledchanged' then its a LightEnable command */ - if (src->enabledChanged) { - - /* If the light exists, check if it's enabled, otherwise default is disabled state */ - TRACE("Updating lightEnabled for light %d\n", src->OriginalIndex); - src->lightEnabled = realLight? realLight->lightEnabled: FALSE; - } - - src = src->next; } /* Recorded => Only update 'changed' values */ @@ -587,14 +635,19 @@ should really perform a delta so that only the changes get updated*/ if (/*TODO: 'magic' statetype, replace with BOOL This->blockType == D3DSBT_RECORDED || */This->blockType == WINED3DSBT_INIT || This->blockType == WINED3DSBT_ALL || This->blockType == WINED3DSBT_VERTEXSTATE) { + for(i = 0; i < LIGHTMAP_SIZE; i++) { + struct list *e; - PLIGHTINFOEL *toDo = This->lights; - while (toDo != NULL) { - if (toDo->changed) - IWineD3DDevice_SetLight(pDevice, toDo->OriginalIndex, &toDo->OriginalParms); - if (toDo->enabledChanged) - IWineD3DDevice_SetLightEnable(pDevice, toDo->OriginalIndex, toDo->lightEnabled); - toDo = toDo->next; + LIST_FOR_EACH(e, &This->lightMap[i]) { + PLIGHTINFOEL *light = LIST_ENTRY(e, PLIGHTINFOEL, entry); + + if(light->changed) { + IWineD3DDevice_SetLight(pDevice, light->OriginalIndex, &light->OriginalParms); + } + if(light->enabledChanged) { + IWineD3DDevice_SetLightEnable(pDevice, light->OriginalIndex, light->glIndex != -1); + } + } } /* Vertex Shader */ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index e21e5ce056b..3b30a3a2866 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -517,7 +517,6 @@ struct PLIGHTINFOEL { WINED3DLIGHT OriginalParms; /* Note D3D8LIGHT == D3D9LIGHT */ DWORD OriginalIndex; LONG glIndex; - BOOL lightEnabled; BOOL changed; BOOL enabledChanged; @@ -527,8 +526,7 @@ struct PLIGHTINFOEL { float exponent; float cutoff; - PLIGHTINFOEL *next; - PLIGHTINFOEL *prev; + struct list entry; }; /* The default light parameters */ @@ -1204,8 +1202,11 @@ struct IWineD3DStateBlockImpl /* Transform */ WINED3DMATRIX transforms[HIGHEST_TRANSFORMSTATE + 1]; - /* Lights */ - PLIGHTINFOEL *lights; /* NOTE: active GL lights must be front of the chain */ + /* Light hashmap . Collisions are handled using standard wine double linked lists */ +#define LIGHTMAP_SIZE 43 /* Use of a prime number recommended. Set to 1 for a linked list! */ +#define LIGHTMAP_HASHFUNC(x) ((x) % LIGHTMAP_SIZE) /* Primitive and simple function */ + struct list lightMap[LIGHTMAP_SIZE]; /* Mashmap containing the lights */ + PLIGHTINFOEL *activeLights[MAX_ACTIVE_LIGHTS]; /* Map of opengl lights to d3d lights */ /* Clipping */ double clipplane[MAX_CLIPPLANES][4];