AEFeinstein / AEFeinstein/Super-2024-Swadge-FW

Reference-counted Asset Loading

Đang mở
#418 5 bình luận 0 reaction 1 người được giao Được @dylwhich nhận Xem trên GitHub
Next Year Utility
Ngôn ngữ chính
C
Star
34
Fork
22
Merge trung bình
3 ngày 15 giờ
Pull request đã merge (30 ngày)
10

Mô tả

**Summary**
_What is the feature all about? Is it a game, a utility, an enhancement, or something else?_

This is a firmware utility that would simplify the process of loading and unloading assets, and eliminate a source of memory leaks. This utility would allow the declaration of a loader for a particular asset type. When the loader is used to allocate a resource, it performs a lookup in the hash table and loads the asset if it is not present yet. If the asset is present, the existing asset is returned instead of allocating a new one. Each asset's reference count is incremented every time the asset is loaded, and decremented any time the asset is unloaded. If the reference count reaches 0, the asset is unloaded and removed from the hash map. All assets could also be unloaded at once, which could be done when a mode about to exit or returning to its main menu.

Benefits of the system:
* Simplifies the process of loading and unloading assets greatly. The loader takes care of deciding when to actually allocate or free resources, and all assets can be force-unloaded on request which makes it much harder to accidentally forget one.
* Allows for saving memory when assets are no longer needed. Useful for when you need a variety of assets, but don't necessarily need all of them allocated at the same time.

Drawbacks:
* A bit more memory & CPU overhead than manually managing assets
* Can't modify returned values, e.g. for palette swapping, since they're shared

**Technical Spec**
_How will this be implemented?_
_What peripherals will be used, and how?_

The system would be composed of a "loader" struct, which contains:
* A hash map to hold all currently-allocated asset information
* A pointer to a function for loading the asset
* A pointer to a function for unloading the asset
* The size of the asset's container struct (e.g., of a `wsg_t`)

Each member of the hash map would be a dynamically-allocated struct that contains:
* The container struct
* The asset's filename/key (e.g. `kd0.wsg`)
* The reference count
This struct and its data (meaning the container struct and the key string data) would all be allocated simultaneously, reducing allocation overhead slightly.

The API of this utility would consist of these functions:

* `void initLoader(loader_t* loader, size_t dataSize, loaderLoadFn_t loadFn, loaderUnloadFn_t unloadFn)` -- Initializes the loader and its hash map, and sets the loading function and container size members.
* `void deinitLoader(loader_t* loader)` -- Unloads all assets, then deinitializes the loader and its hash map, zeroing out all fields.
* `const void* loadAsset(loader_t* loader, const char* filename)` -- Uses the loader `loader` to load (whoa) the asset with `filename`, and returns a pointer to the container struct (e.g. `const wsg_t*`). Calls `loadFn` if the asset is not yet loaded, and adds 1 to the reference count.
* `void unloadAsset(loader_t* loader, void* asset)` -- Unloads the asset at the given pointer. Subtracts 1 from the reference count. If the reference count is 0, calls `unloadFn` to unload the asset.
* `void unloadAll(loader_t* loader)` -- Unloads all assets, regardless of the reference count. Does _not_ deinitialize the hash map or any other members of the loader. All pointers previously returned by this loader are invalidated, so care must be taken not to dereference them after calling this function.

We would also create wrapper load/unload functions that return/accept the appropriate data type (e.g. `wsg_t*`) instead of `void*` for convenience and clarity. Optionally, we could replace the existing asset loading functions with ones that use a static loader internally, and rename the existing functions to something like, e.g. `bool loadWsgPrivate(wsg_t* wsg, const char* name)` which would not use the loader, and would allow the result data to be modified, unlike the result of the loader functions.

**Mockups**
_Paste any mockups of screens or anything else here._

Example of a WSG loader that uses the API:

```
static loader_t wsgLoader;
static bool wsgLoaderInit = false;

static bool doWsgLoad(void* wsg, const char* name)
{
return loadWsg((wsg_t*)wsg, name, true);
}

static void doWsgUnload(void* wsg)
{
return freeWsg((wsg_t*)wsg);
}

static void initWsgLoader(void)
{
if (!wsgLoaderInit)
{
initLoader(&wsgLoader, sizeof(wsg_t), doWsgLoad, doWsgUnload);
wsgLoaderInit = true;
}
}

const wsg_t* loadWsg(const char* name)
{
return (const wsg_t*)loadAsset(&wsgLoader, name);
}

void unloadWsg(const wsg_t* wsg)
{
unloadAsset(&wsgLoader, wsg);
}

void unloadAllWsgs(void)
{
unloadAll(&wsgLoader);
}
```

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.