Commit ebc12a2fd2bb692908884e7db6cc935941a591f2

Sylvain Becker 2020-09-07T18:50:30

SDL_Surface refcount: destination surface keep track of surfaces that are mapped to it and automatically invalidate them when it is freed - refcount is kept so that an external application can still create a reference to SDL_Surface. - lock_data was un-used and is now renamed and used as a list keep track of the blitmap

diff --git a/include/SDL_surface.h b/include/SDL_surface.h
index 0f11d17..2bffb81 100644
--- a/include/SDL_surface.h
+++ b/include/SDL_surface.h
@@ -80,7 +80,9 @@ typedef struct SDL_Surface
 
     /** information needed for surfaces requiring locks */
     int locked;                 /**< Read-only */
-    void *lock_data;            /**< Read-only */
+
+    /** list of BlitMap that hold a reference to this surface */
+    void *list_blitmap;         /**< Private */
 
     /** clipping information */
     SDL_Rect clip_rect;         /**< Read-only */
diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c
index 77de36c..84b6932 100644
--- a/src/video/SDL_pixels.c
+++ b/src/video/SDL_pixels.c
@@ -1023,6 +1023,62 @@ SDL_AllocBlitMap(void)
     return (map);
 }
 
+
+typedef struct SDL_ListNode
+{
+    void *entry;
+    struct SDL_ListNode *next;
+} SDL_ListNode;
+
+void
+SDL_InvalidateAllBlitMap(SDL_Surface *surface)
+{
+    SDL_ListNode *l = surface->list_blitmap;
+
+    surface->list_blitmap = NULL;
+
+    while (l) {
+        SDL_ListNode *tmp = l;
+        SDL_InvalidateMap((SDL_BlitMap *)l->entry);
+        l = l->next;
+        SDL_free(tmp);
+    }
+}
+
+static void SDL_ListAdd(SDL_ListNode **head, void *ent);
+static void SDL_ListRemove(SDL_ListNode **head, void *ent);
+
+void
+SDL_ListAdd(SDL_ListNode **head, void *ent)
+{
+    SDL_ListNode *node = SDL_malloc(sizeof (*node));
+
+    if (node == NULL) {
+        SDL_OutOfMemory();
+        return;
+    }
+
+    node->entry = ent;
+    node->next = *head;
+    *head = node;
+}
+
+void
+SDL_ListRemove(SDL_ListNode **head, void *ent)
+{
+    SDL_ListNode **ptr = head;
+
+    while (*ptr) {
+        if ((*ptr)->entry == ent) {
+            SDL_ListNode *tmp = *ptr;
+            *ptr = (*ptr)->next;
+            SDL_free(tmp);
+            return;
+        }
+        ptr = &(*ptr)->next;
+    }
+}
+
 void
 SDL_InvalidateMap(SDL_BlitMap * map)
 {
@@ -1030,10 +1086,8 @@ SDL_InvalidateMap(SDL_BlitMap * map)
         return;
     }
     if (map->dst) {
-        /* Release our reference to the surface - see the note below */
-        if (--map->dst->refcount <= 0) {
-            SDL_FreeSurface(map->dst);
-        }
+        /* Un-register from the destination surface */
+        SDL_ListRemove((SDL_ListNode **)&(map->dst->list_blitmap), map);
     }
     map->dst = NULL;
     map->src_palette_version = 0;
@@ -1104,14 +1158,8 @@ SDL_MapSurface(SDL_Surface * src, SDL_Surface * dst)
     map->dst = dst;
 
     if (map->dst) {
-        /* Keep a reference to this surface so it doesn't get deleted
-           while we're still pointing at it.
-
-           A better method would be for the destination surface to keep
-           track of surfaces that are mapped to it and automatically
-           invalidate them when it is freed, but this will do for now.
-        */
-        ++map->dst->refcount;
+        /* Register BlitMap to the destination surface, to be invalidated when needed */
+        SDL_ListAdd((SDL_ListNode **)&(map->dst->list_blitmap), map);
     }
 
     if (dstfmt->palette) {
diff --git a/src/video/SDL_pixels_c.h b/src/video/SDL_pixels_c.h
index 8f511c0..9ff590e 100644
--- a/src/video/SDL_pixels_c.h
+++ b/src/video/SDL_pixels_c.h
@@ -37,6 +37,8 @@ extern void SDL_InvalidateMap(SDL_BlitMap * map);
 extern int SDL_MapSurface(SDL_Surface * src, SDL_Surface * dst);
 extern void SDL_FreeBlitMap(SDL_BlitMap * map);
 
+extern void SDL_InvalidateAllBlitMap(SDL_Surface *surface);
+
 /* Miscellaneous functions */
 extern void SDL_DitherColors(SDL_Color * colors, int bpp);
 extern Uint8 SDL_FindColor(SDL_Palette * pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c
index 3ec9093..5539465 100644
--- a/src/video/SDL_surface.c
+++ b/src/video/SDL_surface.c
@@ -1328,6 +1328,8 @@ SDL_FreeSurface(SDL_Surface * surface)
     }
     SDL_InvalidateMap(surface->map);
 
+    SDL_InvalidateAllBlitMap(surface);
+
     if (--surface->refcount > 0) {
         return;
     }