I have 2 things implemented in this algorithm ... A "least recently used cache or
", that monitors which was the vera image that was least recently used. The index (handle) pointing to the images are dynamically allocated in VRAM through a heap manager. So, when the drawing engine is trying to draw an image of a sprite, it checks if this sprite image is already in the lru cache.
If it is in the lru-cache, it will just re-use the image already in VRAM.
If it is not in the lru cache, it will loop ... until the required image can be put into vram. How?
It loops, freeing the last image in the lru cache (so the least recently used image) from vram. It deletes this entry from the lru cache and then frees the image from vram.
Then it tries to best-fit the new image in vram (it checks if there is space for it). If that best-fit search fails, (due to the least recently used image freed space made available being too small), it retries freeing the least recently used image from the lru cache and freeing vram of that image.
This until the image could be successfully best fitted in vram by the heap manager, and then the image is copied into the vram dynamically, and added to the lru cache as the most recently used image.
Images are copied from BRAM into VRAM using indeed, some sort of a copy funciton, which i worked on very hard to get it optimal. I still need to work on this copy module.
See below the lru cache core utilization logic for managing the images.
vera_sprite_image_offset sprite_image_cache_vram(fe_sprite_index_t fe_sprite_index, unsigned char fe_sprite_image_index) {
// check if the image in vram is in use where the fe_sprite_vram_image_index is pointing to.
// if this vram_image_used is false, that means that the image in vram is not in use anymore (not displayed or destroyed).
unsigned int image_index = sprite_cache.offset[fe_sprite_index] + fe_sprite_image_index;
// We retrieve the image from BRAM from the sprite_control bank.
// TODO: what if there are more sprite control data than that can fit into one CX16 bank?
bank_push_set_bram(fe.bram_sprite_control);
heap_bram_fb_handle_t handle_bram = sprite_bram_handles[image_index];
bank_pull_bram();
// We declare temporary variables for the vram memory handles.
lru_cache_data_t vram_handle;
vram_bank_t vram_bank;
vram_offset_t vram_offset;
// We check if there is a cache hit?
lru_cache_index_t vram_index = lru_cache_index(&sprite_cache_vram, image_index);
lru_cache_data_t lru_cache_data;
vera_sprite_image_offset sprite_offset;
if (vram_index != 0xFF) {
// So we have a cache hit, so we can re-use the same image from the cache and we win time!
vram_handle = lru_cache_get(&sprite_cache_vram, vram_index);
vram_bank = vera_heap_data_get_bank(VERA_HEAP_SEGMENT_SPRITES, vram_handle);
vram_offset = vera_heap_data_get_offset(VERA_HEAP_SEGMENT_SPRITES, vram_handle);
sprite_offset = vera_sprite_get_image_offset(vram_bank, vram_offset);
} else {
// The idea of this section is to free up lru_cache and/or vram memory until there is sufficient space available.
// The size requested contains the required size to be allocated on vram.
vera_heap_size_int_t vram_size_required = sprite_cache.size[fe_sprite_index];
// We check if the vram heap has sufficient memory available for the size requested.
// We also check if the lru cache has sufficient elements left to contain the new sprite image.
bool vram_has_free = vera_heap_has_free(VERA_HEAP_SEGMENT_SPRITES, vram_size_required);
bool lru_cache_not_free = lru_cache_max(&sprite_cache_vram);
// Free up the lru_cache and vram memory until the requested size is available!
// This ensures that vram has sufficient place to allocate the new sprite image.
while(lru_cache_not_free || !vram_has_free) {
// If the cache is at it's maximum, before we can add a new element, we must remove the least used image.
// We search for the least used image in vram.
lru_cache_key_t vram_last = lru_cache_last(&sprite_cache_vram);
// We delete the least used image from the vram cache, and this function returns the stored vram handle obtained by the vram heap manager.
vram_handle = lru_cache_delete(&sprite_cache_vram, vram_last);
if(vram_handle==0xFFFF) {
gotoxy(0,59);
printf("error! vram_handle is nothing!");
}
// And we free the vram heap with the vram handle that we received.
// But before we can free the heap, we must first convert back from teh sprite offset to the vram address.
// And then to a valid vram handle :-).
vera_heap_free(VERA_HEAP_SEGMENT_SPRITES, vram_handle);
vram_has_free = vera_heap_has_free(VERA_HEAP_SEGMENT_SPRITES, vram_size_required);
}
// Now that we are sure that there is sufficient space in vram and on the cache, we allocate a new element.
// Dynamic allocation of sprites in vera vram.
vram_handle = vera_heap_alloc(VERA_HEAP_SEGMENT_SPRITES, (unsigned long)sprite_cache.size[fe_sprite_index]);
vram_bank = vera_heap_data_get_bank(VERA_HEAP_SEGMENT_SPRITES, vram_handle);
vram_offset = vera_heap_data_get_offset(VERA_HEAP_SEGMENT_SPRITES, vram_handle);
memcpy_vram_bram(vram_bank, vram_offset, heap_bram_fb_bank_get(handle_bram), (bram_ptr_t)heap_bram_fb_ptr_get(handle_bram), sprite_cache.size[fe_sprite_index]);
sprite_offset = vera_sprite_get_image_offset(vram_bank, vram_offset);
lru_cache_insert(&sprite_cache_vram, image_index, vram_handle);
}
// We return the image offset in vram of the sprite to be drawn.
// This offset is used by the vera image set offset function to directly change the image displayed of the sprite!
return sprite_offset;
}