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
This commit is contained in:
Sylvain Becker 2020-09-07 18:50:30 +02:00
parent cce6c60518
commit ebc12a2fd2
4 changed files with 67 additions and 13 deletions

View File

@ -80,7 +80,9 @@ typedef struct SDL_Surface
/** information needed for surfaces requiring locks */ /** information needed for surfaces requiring locks */
int locked; /**< Read-only */ 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 */ /** clipping information */
SDL_Rect clip_rect; /**< Read-only */ SDL_Rect clip_rect; /**< Read-only */

View File

@ -1023,6 +1023,62 @@ SDL_AllocBlitMap(void)
return (map); 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 void
SDL_InvalidateMap(SDL_BlitMap * map) SDL_InvalidateMap(SDL_BlitMap * map)
{ {
@ -1030,10 +1086,8 @@ SDL_InvalidateMap(SDL_BlitMap * map)
return; return;
} }
if (map->dst) { if (map->dst) {
/* Release our reference to the surface - see the note below */ /* Un-register from the destination surface */
if (--map->dst->refcount <= 0) { SDL_ListRemove((SDL_ListNode **)&(map->dst->list_blitmap), map);
SDL_FreeSurface(map->dst);
}
} }
map->dst = NULL; map->dst = NULL;
map->src_palette_version = 0; map->src_palette_version = 0;
@ -1104,14 +1158,8 @@ SDL_MapSurface(SDL_Surface * src, SDL_Surface * dst)
map->dst = dst; map->dst = dst;
if (map->dst) { if (map->dst) {
/* Keep a reference to this surface so it doesn't get deleted /* Register BlitMap to the destination surface, to be invalidated when needed */
while we're still pointing at it. SDL_ListAdd((SDL_ListNode **)&(map->dst->list_blitmap), map);
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;
} }
if (dstfmt->palette) { if (dstfmt->palette) {

View File

@ -37,6 +37,8 @@ extern void SDL_InvalidateMap(SDL_BlitMap * map);
extern int SDL_MapSurface(SDL_Surface * src, SDL_Surface * dst); extern int SDL_MapSurface(SDL_Surface * src, SDL_Surface * dst);
extern void SDL_FreeBlitMap(SDL_BlitMap * map); extern void SDL_FreeBlitMap(SDL_BlitMap * map);
extern void SDL_InvalidateAllBlitMap(SDL_Surface *surface);
/* Miscellaneous functions */ /* Miscellaneous functions */
extern void SDL_DitherColors(SDL_Color * colors, int bpp); extern void SDL_DitherColors(SDL_Color * colors, int bpp);
extern Uint8 SDL_FindColor(SDL_Palette * pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a); extern Uint8 SDL_FindColor(SDL_Palette * pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a);

View File

@ -1328,6 +1328,8 @@ SDL_FreeSurface(SDL_Surface * surface)
} }
SDL_InvalidateMap(surface->map); SDL_InvalidateMap(surface->map);
SDL_InvalidateAllBlitMap(surface);
if (--surface->refcount > 0) { if (--surface->refcount > 0) {
return; return;
} }