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:
1 parent
3f71cf0f3f
commit
dfee36ffe9
12 files changed
+292
-60
No files matched your search
+155
-8
@@ -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;
|
||||
|
||||
|
||||
Reference in new issue
Block a user