/** * @file heap.c * @brief Implements the heap subsystem. */ #include #include #include #include #include #include #include #include #include akgl_Actor akgl_heap_actors[AKGL_MAX_HEAP_ACTOR]; akgl_Sprite akgl_heap_sprites[AKGL_MAX_HEAP_SPRITE]; akgl_SpriteSheet akgl_heap_spritesheets[AKGL_MAX_HEAP_SPRITESHEET]; akgl_Character akgl_heap_characters[AKGL_MAX_HEAP_CHARACTER]; akgl_String akgl_heap_strings[AKGL_MAX_HEAP_STRING]; akgl_CollisionProxy akgl_heap_collision_proxies[AKGL_MAX_HEAP_COLLISION_PROXY]; akgl_CollisionCell akgl_heap_collision_cells[AKGL_MAX_HEAP_COLLISION_CELL]; akerr_ErrorContext *akgl_heap_init(void) { PREPARE_ERROR(errctx); int i = 0; PASS(errctx, akgl_heap_init_actor()); for ( i = 0; i < AKGL_MAX_HEAP_SPRITE; i++) { memset(&akgl_heap_sprites[i], 0x00, sizeof(akgl_Sprite)); } for ( i = 0; i < AKGL_MAX_HEAP_SPRITESHEET; i++) { memset(&akgl_heap_spritesheets[i], 0x00, sizeof(akgl_SpriteSheet)); } for ( i = 0; i < AKGL_MAX_HEAP_CHARACTER; i++) { memset(&akgl_heap_characters[i], 0x00, sizeof(akgl_Character)); } for ( i = 0; i < AKGL_MAX_HEAP_STRING; i++) { memset(&akgl_heap_strings[i], 0x00, sizeof(akgl_String)); } for ( i = 0; i < AKGL_MAX_HEAP_COLLISION_PROXY; i++) { memset(&akgl_heap_collision_proxies[i], 0x00, sizeof(akgl_CollisionProxy)); akgl_heap_collision_proxies[i].first = -1; } PASS(errctx, akgl_heap_init_collision_cells()); SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_init_actor(void) { PREPARE_ERROR(errctx); for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++) { memset(&akgl_heap_actors[i], 0x00, sizeof(akgl_Actor)); } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_next_actor(akgl_Actor **dest) { PREPARE_ERROR(errctx); for (int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) { if ( akgl_heap_actors[i].refcount != 0 ) { continue; } *dest = &akgl_heap_actors[i]; SUCCEED_RETURN(errctx); } FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused actor on the heap"); } akerr_ErrorContext *akgl_heap_next_sprite(akgl_Sprite **dest) { PREPARE_ERROR(errctx); for (int i = 0; i < AKGL_MAX_HEAP_SPRITE; i++ ) { if ( akgl_heap_sprites[i].refcount != 0 ) { continue; } *dest = &akgl_heap_sprites[i]; SUCCEED_RETURN(errctx); } FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused sprite on the heap"); } akerr_ErrorContext *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest) { PREPARE_ERROR(errctx); for (int i = 0; i < AKGL_MAX_HEAP_SPRITESHEET; i++ ) { if ( akgl_heap_spritesheets[i].refcount != 0 ) { continue; } *dest = &akgl_heap_spritesheets[i]; SUCCEED_RETURN(errctx); } FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused spritesheet on the heap"); } akerr_ErrorContext *akgl_heap_next_character(akgl_Character **dest) { PREPARE_ERROR(errctx); for (int i = 0; i < AKGL_MAX_HEAP_CHARACTER; i++ ) { if ( akgl_heap_characters[i].refcount != 0 ) { continue; } *dest = &akgl_heap_characters[i]; SUCCEED_RETURN(errctx); } FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused character on the heap"); } akerr_ErrorContext *akgl_heap_next_string(akgl_String **dest) { PREPARE_ERROR(errctx); for (int i = 0; i < AKGL_MAX_HEAP_STRING; i++ ) { if ( akgl_heap_strings[i].refcount != 0 ) { continue; } *dest = &akgl_heap_strings[i]; akgl_heap_strings[i].refcount += 1; SUCCEED_RETURN(errctx); } FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused string on the heap"); } akerr_ErrorContext *akgl_heap_release_actor(akgl_Actor *ptr) { int i = 0; PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "NULL actor reference"); if ( ptr->refcount > 0 ) { ptr->refcount -= 1; } if ( ptr->refcount == 0 ) { for ( i = 0; i < AKGL_ACTOR_MAX_CHILDREN; i++ ) { if ( ptr->children[i] != NULL ) { PASS(errctx, akgl_heap_release_actor(ptr->children[i])); } } /* * The proxy goes with the actor. It holds a borrowed `owner` pointer * into the slot about to be zeroed, so leaving it registered would give * the broad phase a proxy whose owner reads as a free actor -- and the * next contact against it would report a collision with nothing. */ if ( ptr->proxy != NULL ) { PASS(errctx, akgl_heap_release_collision_proxy(ptr->proxy)); ptr->proxy = NULL; } SDL_ClearProperty(AKGL_REGISTRY_ACTOR, (char *)&ptr->name); memset(ptr, 0x00, sizeof(akgl_Actor)); } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_release_character(akgl_Character *ptr) { PREPARE_ERROR(errctx); akgl_Iterator opflags; FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "NULL character reference"); AKGL_BITMASK_CLEAR(opflags.flags); if ( ptr->refcount > 0 ) { ptr->refcount -= 1; } if ( ptr->refcount == 0 ) { // Give back every sprite reference akgl_character_sprite_add took, and // the property set holding the map, before the slot is zeroed. Without // this a game that loads and releases characters level by level // exhausts the sprite pool and leaks one SDL_PropertiesID per // character. akgl_character_state_sprites_iterate exists for exactly // this walk and simply was never called from here. if ( ptr->state_sprites != 0 ) { AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_RELEASE); SDL_EnumerateProperties( ptr->state_sprites, &akgl_character_state_sprites_iterate, (void *)&opflags ); SDL_DestroyProperties(ptr->state_sprites); ptr->state_sprites = 0; } SDL_ClearProperty(AKGL_REGISTRY_CHARACTER, (char *)&ptr->name); memset(ptr, 0x00, sizeof(akgl_Character)); } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_release_sprite(akgl_Sprite *ptr) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL sprite reference"); if ( ptr->refcount > 0 ) { ptr->refcount -= 1; } if ( ptr->refcount == 0 ) { SDL_ClearProperty(AKGL_REGISTRY_SPRITE, (char *)&ptr->name); memset(ptr, 0x00, sizeof(akgl_Sprite)); } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_release_spritesheet(akgl_SpriteSheet *ptr) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL spritesheet reference"); if ( ptr->refcount > 0 ) { ptr->refcount -= 1; } if ( ptr->refcount == 0 ) { // TODO : If we go threaded, make sure this is only happening on the main thread SDL_ClearProperty(AKGL_REGISTRY_SPRITESHEET, (char *)&ptr->name); if ( ptr-> texture != NULL ) SDL_DestroyTexture(ptr->texture); ptr->texture = NULL; memset(ptr, 0x00, sizeof(akgl_SpriteSheet)); } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_release_string(akgl_String *ptr) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL string reference"); if ( ptr->refcount > 0 ) { ptr->refcount -= 1; } if ( ptr->refcount == 0 ) { memset(&ptr->data, 0x00, AKGL_MAX_STRING_LENGTH); } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_next_collision_proxy(akgl_CollisionProxy **dest) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination reference"); for (int i = 0; i < AKGL_MAX_HEAP_COLLISION_PROXY; i++ ) { if ( akgl_heap_collision_proxies[i].refcount != 0 ) { continue; } *dest = &akgl_heap_collision_proxies[i]; SUCCEED_RETURN(errctx); } FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused collision proxy on the heap"); } akerr_ErrorContext *akgl_heap_release_collision_proxy(akgl_CollisionProxy *ptr) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "NULL collision proxy reference"); if ( ptr->refcount > 0 ) { ptr->refcount -= 1; } if ( ptr->refcount == 0 ) { memset(ptr, 0x00, sizeof(akgl_CollisionProxy)); } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_next_collision_cell(akgl_CollisionCell **dest) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination reference"); for (int i = 0; i < AKGL_MAX_HEAP_COLLISION_CELL; i++ ) { if ( akgl_heap_collision_cells[i].refcount != 0 ) { continue; } *dest = &akgl_heap_collision_cells[i]; SUCCEED_RETURN(errctx); } FAIL_RETURN(errctx, AKGL_ERR_HEAP, "Unable to find unused collision cell entry on the heap"); } akerr_ErrorContext *akgl_heap_release_collision_cell(akgl_CollisionCell *ptr) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "NULL collision cell reference"); if ( ptr->refcount > 0 ) { ptr->refcount -= 1; } if ( ptr->refcount == 0 ) { memset(ptr, 0x00, sizeof(akgl_CollisionCell)); ptr->next = -1; ptr->prev = -1; ptr->ownernext = -1; ptr->cell = -1; } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_heap_init_collision_cells(void) { PREPARE_ERROR(errctx); for ( int i = 0; i < AKGL_MAX_HEAP_COLLISION_CELL; i++ ) { memset(&akgl_heap_collision_cells[i], 0x00, sizeof(akgl_CollisionCell)); akgl_heap_collision_cells[i].next = -1; akgl_heap_collision_cells[i].prev = -1; akgl_heap_collision_cells[i].ownernext = -1; akgl_heap_collision_cells[i].cell = -1; } SUCCEED_RETURN(errctx); }