[1/10] WineD3D: Replace the light chain with a hashmap

Stefan Dösinger stefan at codeweavers.com
Wed Feb 14 10:46:54 CST 2007


Lights are indentified by an index from 0 to MAX_DWORD, a range that is too 
big for an array. On the other hand, I dislike the idea of using a linked 
list because apps can define a quite huge number of lights, so I decided to 
use a (primitive) hashmap. By rewriting the light organisation a few bugs 
with the light list and stateblocks are fixed too.
-------------- next part --------------
From 4fff08547106025766453b4033f952d0ba950bb7 Mon Sep 17 00:00:00 2001
From: Stefan Doesinger <stefan at codeweavers.com>
Date: Fri, 2 Feb 2007 12:09:31 +0100
Subject: [PATCH] WineD3D: Replace the light chain with a hashmap

---
 dlls/wined3d/device.c          |  360 +++++++++++----------------------------
 dlls/wined3d/state.c           |   12 +-
 dlls/wined3d/stateblock.c      |  139 +++++++++++-----
 dlls/wined3d/wined3d_private.h |   11 +-
 4 files changed, 210 insertions(+), 312 deletions(-)

diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 65af9ee..9a0b1b5 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) {
+    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 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();
+            This->stateBlock->activeLights[lightInfo->glIndex] = NULL;
+            lightInfo->glIndex = -1;
         } else {
-            TRACE("Nothing to do as light was not enabled\n");
+            TRACE("Light already disabled, nothing to do\n");
         }
-        lightInfo->lightEnabled = Enable;
     } else {
-
-        /* We are enabling it. If it is enabled, it's really simple */
-        if (lightInfo->lightEnabled) {
+        if (lightInfo->glIndex != -1) {
             /* 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;
+            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;
                 }
-
-                /* Step to next space */
-                prev = pos;
-                pos = pos->next;
-                Index ++;
             }
-
-            /* If we have too many active lights, fail the call */
-            if ((Index == This->maxConcurrentLights) && (bsf == NULL)) {
-                FIXME("Program requests too many concurrent lights.\n");
+            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);
-                ENTER_GL();
-                setup_light(iface, glIndex, lightInfo);
-                glEnable(GL_LIGHT0 + glIndex);
-                checkGLcall("glEnable GL_LIGHT0 new setup");
-                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);
+            /* 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();
-
             }
         }
     }
+
     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 9893dcb..ee898c2 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 093c83d..6112f59 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;
-
-            /* Locate the light in the live lights */
-            realLight = targetStateBlock->lights;
-            while (realLight != NULL && realLight->OriginalIndex != src->OriginalIndex) realLight = realLight->next;
-
-            /* 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 'enabledchanged' then its a LightEnable command */
-            if (src->enabledChanged) {
+        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;
+
+                /* 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 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;
+                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;
+                }
             }
-
-            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;
+
+            LIST_FOR_EACH(e, &This->lightMap[i]) {
+                PLIGHTINFOEL *light = LIST_ENTRY(e, PLIGHTINFOEL, entry);
 
-        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;
+                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 e21e5ce..3b30a3a 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];
-- 
1.4.4.3



More information about the wine-patches mailing list