Enabled SDL_image's support for PNG and TIFF files and improved the

AssetManager with a hashing function, a linked list structure to store
the hashes as well as a reference counter to support multiple users
sharing the same asset.
This commit is contained in:
abdelrahman committed 2022-09-24 04:17:16 +01:00
1 parent 3f71cf0f3f
commit dfee36ffe9
12 files changed
+292 -60

No files matched your search

+155 -8
View File
@@ -1,5 +1,6 @@
#include "AssetManager.h"
#include "Renderer.h"
#include "SDL_error.h"
#include "SDL_image.h"
#include "SDL_mixer.h"
#include "SDL_render.h"
@@ -8,6 +9,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -21,13 +23,128 @@ typedef struct asset {
} data;
} Asset;
typedef struct id_node IDNode;
struct id_node {
AssetID id;
IDNode *next;
};
typedef struct id_node_list {
IDNode *start_node;
size_t node_count;
} IDNodeList;
struct asset_manager {
size_t count;
size_t deleted_count;
AssetID reusable_ids[MAX_ASSET_COUNT];
Asset assets[MAX_ASSET_COUNT];
size_t user_counter[MAX_ASSET_COUNT];
IDNodeList hashed_path_ids[MAX_ASSET_COUNT];
};
size_t hash_path(const char *path) {
size_t i = 0;
for (size_t j = 0; path[j]; ++j) {
i += (size_t)path[j] * HASH_PRIME_01 % MAX_ASSET_COUNT;
}
i = (i * HASH_PRIME_02) % MAX_ASSET_COUNT;
return i;
}
void insert_id_node(IDNodeList *node_list, AssetID id) {
if (node_list->node_count == 0) {
node_list->start_node = malloc(sizeof(IDNode));
node_list->start_node->id = id;
node_list->start_node->next = NULL;
node_list->node_count = 1;
return;
}
IDNode *last_node = node_list->start_node;
for (size_t i = 1; i < node_list->node_count; ++i) {
last_node = last_node->next;
}
last_node->next = malloc(sizeof(IDNode));
last_node->next->id = id;
last_node->next->next = NULL;
++(node_list->node_count);
}
void remove_id_node(IDNodeList *node_list, AssetID id) {
if (node_list->node_count == 0) {
return;
} else if (node_list->node_count == 1) {
free(node_list->start_node);
node_list->start_node = NULL;
node_list->node_count = 0;
return;
}
IDNode *node = NULL;
IDNode *prev = NULL;
IDNode *current = node_list->start_node;
for (size_t i = 0; i < node_list->node_count; ++i) {
if (current->id == id) {
node = current;
break;
}
prev = current;
current = current->next;
}
if (node) {
IDNode *next = node->next;
if (!prev) {
// Handle the case when there is no previous node, which means that node
// is actually the start node in the list.
free(node_list->start_node);
node_list->start_node = next;
} else {
free(prev->next);
prev->next = next;
}
--(node_list->node_count);
}
}
AssetID get_asset_id_from_id_node(AssetManager *am, IDNodeList *node_list,
const char *path) {
if (node_list->node_count == 0) {
return INVALID_ID;
}
IDNode *id_node = node_list->start_node;
for (size_t i = 0; i < node_list->node_count; ++i) {
if (!id_node) {
break;
}
Asset asset = am->assets[id_node->id];
if (strcmp(asset.path, path) == 0) {
return id_node->id;
}
id_node = id_node->next;
}
return INVALID_ID;
}
AssetManager *create_asset_manager() {
AssetManager *am = (AssetManager *)malloc(sizeof(AssetManager));
if (!am) {
@@ -38,6 +155,8 @@ AssetManager *create_asset_manager() {
am->deleted_count = 0;
memset(am->reusable_ids, 0, sizeof(am->reusable_ids));
memset(am->assets, 0, sizeof(am->assets));
memset(am->user_counter, 0, sizeof(am->user_counter));
memset(am->hashed_path_ids, 0, sizeof(am->hashed_path_ids));
return am;
}
@@ -45,16 +164,21 @@ AssetManager *create_asset_manager() {
AssetID load_asset_to_manager(AssetManager *am, const char *path,
AssetType type, Renderer *renderer) {
if (!am || type == ASSET_TYPE_COUNT) {
return -1;
return INVALID_ID;
}
size_t hash_index = hash_path(path);
AssetID existing_id =
get_asset_id_from_id_node(am, &(am->hashed_path_ids[hash_index]), path);
if (existing_id != INVALID_ID) {
++(am->user_counter[existing_id]);
return existing_id;
}
Asset asset = {};
bool loaded = true;
// TODO (Abdelrahman): Implement a way to make sure that an asset with the
// same path is not already loaded. If the user attempts to load an asset
// that already exists, the function should return -1 to indicate an error
switch (type) {
case ASSET_TYPE_IMAGE:
if (!renderer) {
@@ -65,6 +189,7 @@ AssetID load_asset_to_manager(AssetManager *am, const char *path,
asset.data.texture = load_texture(renderer, path);
if (!(asset.data.texture)) {
printf("Error loading %s: %s\n", path, SDL_GetError());
loaded = false;
}
@@ -81,7 +206,7 @@ AssetID load_asset_to_manager(AssetManager *am, const char *path,
}
if (!loaded) {
return -1;
return INVALID_ID;
}
AssetID id;
@@ -94,6 +219,10 @@ AssetID load_asset_to_manager(AssetManager *am, const char *path,
id = (am->count)++;
}
insert_id_node(&(am->hashed_path_ids[hash_index]), id);
am->user_counter[id] = 1;
am->assets[id].type = type;
am->assets[id].path = (char *)malloc(strlen(path) + 1);
@@ -118,11 +247,24 @@ AssetID load_asset_to_manager(AssetManager *am, const char *path,
return id;
}
void remove_asset_from_manager(AssetManager *am, AssetID id) {
if (!am || am->count == 0) {
void remove_asset_from_manager(AssetManager *am, AssetID *id_ptr) {
AssetID id = *id_ptr;
if (!am || am->count == 0 || id == INVALID_ID || !(am->assets[id].path)) {
return;
}
--(am->user_counter[id]);
*id_ptr = INVALID_ID;
// If there are still other objects using the asset, don't delete it
if (am->user_counter[id] > 0) {
return;
}
size_t hash_index = hash_path(am->assets[id].path);
remove_id_node(&(am->hashed_path_ids[hash_index]), id);
free(am->assets[id].path);
am->assets[id].path = 0;
@@ -176,6 +318,11 @@ void destroy_asset_manager(AssetManager *am) {
}
for (size_t i = 0; i < am->count; ++i) {
am->user_counter[i] = 0;
size_t hash_index = hash_path(am->assets[i].path);
remove_id_node(&(am->hashed_path_ids[hash_index]), i);
free(am->assets[i].path);
am->assets[i].path = NULL;