Shaarigan said:
So to let me summarize that, you have trouble with resource loading?
If so then let me introduce you to the wonderful world of resource/asset managing. Usually if you require something to exist, a mesh, material, shader, texture but also some level geometry you have two options:
- Load everything on the same thread which requested it (slow, game starts to stutter)
- Dedicate everything to an authority which takes care of such resource requests
You don't want to take the first one so we go for the second one. Those authorities are usually resource/asset manager classes. Those classes act on another thread (so you are async anyways) and take care of let's say priority but also lifetime of the resource requested. (There is btw. a good post about resource loading some threads below this one). What it does is locating the resource on the disk, usually it is placed along with other resources in a package which is loaded in chunks into memory to access the resources required. Loading a resource is never suspended at certain point and it is never exclusive. I guess this is the issue you are running into in your design, exclusive resource access which is strictly coupled to a single instance. Instead a cache is maintained which keeps track of loaded resources and their lifetime. If the cache runs to a limit, it executes a cleanup which removes unused assets with the highest lifetime from memory for as long as there are still unused assets present or the memory limit is reached where it can stop.
The problem you might run into sooner or later is: what happens if you have a resource used on a single object in the game only and it is swapping in and out of sight frequently? Do you always trie to erase the memory and load the entire file again from disk, of course you won't! Else your game is considered to bug and break on 50% of all players because people are not acting like you expect them to.
So instead of loading an asset for single objects, you have to load them into a cache and pass a handle like struct to the caller which you maintain in the resource manager. That handle usually points to a default asset (like the puprle textures you see when a load fails) and is replaced immediately after the resource has finished loading.
If this isn't the case and you have a different issue, please explain in more detail how you expect it to work
Hmm, interesting about the resource with a cache, I will look at it.
Problem is, the engine is a little bit old and I don't have the whole source of it, it's a single-thread designed ( or looks like that ), so I need to check if it is possible to load from an async thread meshes, material, textures.
Right now my approach is to first read files from disk in another thread ( because can take some time ), allocate memory for it, and add it to a list when it's done, and in the main thread, before render, I just iterate through that list, get the bytes read, put them into the stream and for example creates texture from stream, that way, if a file is too big, it willn't impact in the game FPS, because was load in another thread.
The engine have the texture class, it's like a shared pointer, u create the texture the first time, then it will create a counter with all the references it has, when the counter go back to 0, then it will destroy the texture, but yeah, if only a single object of the game use one texture, and it's swapping in and out of sight frequently, I always destroy it, read memory from disk and load it again, prob I need to do some cache system for it.
Also, I use the async IO thread to read files, one good example is the map sectors, I have multiple files sector0 ~ 10 from map 1, the code before readed those sectors and started to load the sector, if the file was too big that takes 300ms to read ( I'm using it as an example ) the game will freeze for those 300ms until the sector was read from disk.
What I do now, is send a request to the async IO thread with a callback, so if the file takes 300ms to read from disk, I don't care, that will be done inside another thread, and when bytes are read, I just call the callback, copy the memory and set flag “read = true”, now when main thread updates the loading sector system, it will check if ( read == true ) { StartLoadingSector(); }, my issue is, what happen if I left the sector, while it was being read? The async IO doesn't know the file isn't needed anymore, so it keeps reading it, when read is done, it try's to do the callback to a deleted pointer.
That's my issue with every file request I do, if I don't need the file anymore, how to avoid doing the callback to a null object ( because objects are deleted in the main thread, while async IO is reading files )