From ebc12a2fd2bb692908884e7db6cc935941a591f2 Mon Sep 17 00:00:00 2001 From: Sylvain Becker Date: Mon, 7 Sep 2020 18:50:30 +0200 Subject: [PATCH] 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 --- include/SDL_surface.h | 4 ++- src/video/SDL_pixels.c | 72 +++++++++++++++++++++++++++++++++------- src/video/SDL_pixels_c.h | 2 ++ src/video/SDL_surface.c | 2 ++ 4 files changed, 67 insertions(+), 13 deletions(-) diff --git a/include/SDL_surface.h b/include/SDL_surface.h index 0f11d178e..2bffb8115 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 77de36c51..84b693217 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 8f511c0d2..9ff590e0c 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 3ec9093cf..553946516 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; }