Jump to content

Multithreaded Resource Loading


StOneDOes
 Share

Recommended Posts

Can we please have a way for multithreaded resource loading eg. models, textures, sounds. I think it will get quite slow when stacking up a lot of resources to be loaded on the main thread, not to mention that you can't render anything while this is occurring.

Ideally, a thread pool class would be useful for this.

  • Upvote 1
Link to comment
Share on other sites

I'm not sure if I misunderstand what you mean "like that" - there's several resources that are needed to be loaded on startup that simply wouldn't do during gameplay or you would see clear hiccups in play, for example when a player picks up a weapon for the first time. And naturally, you load all resources for a map when you want to enter that map, but you don't want the main thread locked up, and game unresponsive during that time.

The hard drive is not the bottleneck, its the fact that the main thread is loading everything. Ideally, the main thread will sit idly by, showing some kind of loading animation, while the thread pool is loading all required resources. You could provide a non-fool-proof mechanism under which the developer provides the start point and end point essentially for loading, in which time it's not safe to render/play those given resource, so you can avoid mutex locks all over the place in your resource wrapper classes.

Link to comment
Share on other sites

7 hours ago, StOneDOes said:

Ideally, the main thread will sit idly by, showing some kind of loading animation, while the thread pool is loading all required resources.

That's basically how the rendering thread works right now. If the main thread stalls, the rendering thread just keeps drawing frames. You can use a shader to display a spinning indicator for a loading screen while the next level is loading.

  • Like 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Yes. Another option might be to use a callback with the scene load function and periodically check for window events in the callback.

It would probably even be fine to create a separate thread and load the scene on that thread, as long as you did not call any commands other than PeekEvent/WaitEvent in the main thread while that was running. Something like this:

while (PeekEvent()) WaitEvent(); // clear out events
auto thread = CreateThread(std::bind(&LoadScene, mapfile)); // create loading thread
while (thread->GetState() != THREAD_FINISHED)
{
	while (PeekEvent()) WaitEvent(); // process events so window keeps responding
	Sleep(10);
}

If you have any event hooks that execute 3D commands while the loading thread is running, it could cause big problems, so make sure you aren't doing anything based on keyboard input, for example, other than just setting some values like jumpkeypressed = true.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

13 hours ago, Josh said:

If you have any event hooks that execute 3D commands while the loading thread is running, it could cause big problems, so make sure you aren't doing anything based on keyboard input, for example, other than just setting some values like jumpkeypressed = true.

So we could use this same logic, but instead of just using a single thread; a thread pool. While we know we are in loading state, we simply just do not do anything involving resources, otherwise we would probably expect issues. Is your under-the-hood resource manager thread safe? If it is I can test this myself.

Link to comment
Share on other sites

9 hours ago, StOneDOes said:

Is your under-the-hood resource manager thread safe? If it is I can test this myself.

No, definitely not. If you start creating entities in the world on different threads it will cause very bad problems. Even if your tests work fine, it is going to eventually start crashing.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Right, so what would be the chances of you putting this on the to-do list? Because this is a big concern for me; Right now I have (I would say not a large amount) of different resources loaded just after the map has been loaded (I do have 2 high quality weapons), and it is taken almost 35 seconds in debug mode. I haven't written my cmakelists for release mode yet so I don't have a metric on that, but given that the map would be not even 10% done; it is a bit of a worry, and I haven't added any LODs to the existing models that I have (which could cause longer load times).

In Battlefield 4, it takes me less than 20 seconds to join a full server on a large conquest map. Now I know its not fair to compare a work in progress engine to arguably one of (if not) the best game engines ever made, but I'm just trying to put loading time into perspective. And I don't think it would be difficult to do, although obviously I can't see your implementation. I am more than happy to provide an efficient thread pool for this, but I can't handle the thread saftey side.

Just to note I have a AMD Ryzen 9 3900X processor, and wouldn't expect people to require something newer than this to play my game.

Link to comment
Share on other sites

I do not plan on attempting to create entities on another thread because it would cause an enormous amount of problems. Most multiplayer games have a pretty much static world and don't require the flexibility our entity system provides. If I were to implement something like what BF4 is probably doing, it would require a major tradeoff because things you might take for granted right now would suddenly be impossible.

It's not really a matter of one engine being better than the other, it's just that BF4 is probably highly specialized for one game, and compared to that my engine tends to favor flexibility and ease of use.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

35 minutes ago, StOneDOes said:

and it is taken almost 35 seconds in debug mode

How long does it take in release mode?

I think a better direction to look at in the future might be seeing if scene load times can be optimized at all. That's usually much easier than a complicated multithreaded system that will probably not work very well.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I mentioned BF4 just as an example of me saying, this is how much stuff is in that world/map and it doesn't take long, and its a very nonstatic / alive world. I don't think it would be a stretch to say that most modern game engines would use multiple threads for resource loading, or at least provide a means to do so.

One other suggestion that I have is, something that would hopefully be rather easy; If you were to provide some kind of function eg. Entity::CreateModelUnmanaged(). It takes a path to the file on disk loads it, and simply returns the new model, and does not touch your system or resource manager whatsoever. Then I can run this on multiple threads, store a whole collection of models on my end, and then when I'm done, on the main thread, I use some other function like World::ManageMyModel() which takes the shared pointer that I received earlier, and basically copies it into your resource manager, just like the existing CreateModel() function would do. That way I handle all the thread safety, and the engine remains hypothetically untouched? Admittedly, the longest load times are caused by weapons that are not actually in the map (or at least not yet), rather given to the player, so something like this would be super useful to do say on game startup.

As for how long release mode takes; I'll get back to you when I finish writing the release mode cmakelists ... I've been putting that off for a while :D

Link to comment
Share on other sites

Theoretically LoadModel() with the LOAD_UNMANAGED flag in a separate world might be safe, but I would not recommend this.

If there is a big difference between the release and debug load speeds, that indicates to me STL is likely the cause. I think we are better off identifying bottlenecks and targeting those. The problem resource loading stuff that could be multithreaded is likely already not to be a problem, just look how fast textures load in the latest builds of the editor. Likewise, mesh data is really unlikely to be forming a bottleneck unless STL is causing one, in which case the best solution would be our own resizable array class to replace STL vector. I had to write my own sorting code in some of the terrain system because the STL version was too slow in debug builds.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...