Sign in to follow this  
3DModelerMan

Data oriented design in games

Recommended Posts

I'm learning about data oriented design, and I've already figured it out a bit. I decided I'd start a discussion about it because I've never really seen anyone talking about it. For me, component based entity systems are easy to understand in a data oriented way. It basically turns game entities and components into dumb containers of data, that are operated on by systems. However, what about other systems in games? How can data oriented design be applied to them? Resource loading seems like a good easy candidate, you could group resources together in the same areas in memory based on the type of resource, the same way that you group components based on types.

 

One of the ones I can't figure out though is the event system/message pump. With message pumps you have a bunch of objects (messages) being sent around to a bunch of other polymorphic types (listeners) which seems like it's against the whole idea of data oriented design. The only idea I have would be if an event system was made so that the same event types were all grouped into the same memory areas, with each event type being processed in groups and sent to listeners. Does this violate data oriented principles? 

 

What other systems can you think of that present interesting challenges for data-oriented design? Networking would be one that I can think of off the top of my head. And what about parallelism? I've heard it's a major benefit of data-oriented design, but with objects having dependencies on each other and systems being able to operate on multiple components (the rendering system would need to access the transform component) don't you just end up with a synchronization nightmare like with regular object oriented design?

Share this post


Link to post
Share on other sites

a.) Don't use DOD just because it is "nice". If it doesn't fit, use something else. Make the decision on a per-problem basis.

 

b.) Ideally you apply the same execution (read once from memory and then often from instruction cache) onto sequentially organized data (some reads from memory and a great percentage of data cache hits). When you have inhomogeneous data, e.g. the command stream for the low-level rendering system, i-cache hits may be less frequently, but storing the commands as blobs utilizing a stack/linear/sequential allocator helps greatly for data cache hits.

 

c.) An event oriented design is data-driven but not data-oriented, so to say. You cannot handle events by sorting by type, because events usually need to / should be processed in timestamped order. However, treating them as blobs and using a ring buffer allocator as storage still allows you to use an approach very close to those introduced in b.)

 

d.) Consider to drop the use of an event system if possible. An event system is a tool to deal with asynchronous, err, events. A game is an application that can be well structured (see e.g. Jason Gregory's book except on Game Engine Architecture about the game loop) in an overall synchronous way.

 

e.) The above has another effect, too: You can rely on certain sub-systems to have completed when calling other sub-systems to begin, weakening / removing some need for high-level synchronization.

 

f.) Parallelism can be done using vector processing (SSE, GPU) on the one hand or multi-threading on the other hand. Vector processing does not need more synchronization than scalar processing, but depends heavily on data structuring.

 

g.) Multi-threading, on the other hand, is a totally other beast. I second the meaning that multi-threading is complicated in general. However, multi-threading can be better controlled if being done for example so: The main thread does nothing than collecting input events from the OS / hardware and pushes them into a synchronized pipe (this is mainly done so to guarantee that no input gets lost, and some OS's require their event loop to be processed on the main thread). Another thread runs the game loop (inclusive reading input from said input pipe), and at last as a result fills in a render command queue. Another thread reads the render command queue and feeds the graphics API. To be efficient, the render queue is double buffered, of course.

Edited by haegarr

Share this post


Link to post
Share on other sites

What about the cache performance of more complex game systems? For example: an object with a Health component is hit by one with a Damage component. If the components are updated by type, then when we check collisions for all the Health components, we also have to check how much damage to apply (from the Damage component attached to the game object that hit the Health component's object). Is this still a predictable enough access pattern for the CPU cache? Or would jumping back and forth between accessing the Health and the Damage components cause lots of cache misses?

Share this post


Link to post
Share on other sites

Is there a reason you wouldn't just have the damage component send a message to the health component telling it to subtract some amount? Or could you just get around the back-and-forth by updating the damage components first, and then the health components?

 

I've never worked with a component-entity system (always just used standard OOP), but it's something that I'm curious about and starting to think about incorporating.

Share this post


Link to post
Share on other sites

What about the cache performance of more complex game systems? For example: an object with a Health component is hit by one with a Damage component. If the components are updated by type, then when we check collisions for all the Health components, we also have to check how much damage to apply (from the Damage component attached to the game object that hit the Health component's object). Is this still a predictable enough access pattern for the CPU cache? Or would jumping back and forth between accessing the Health and the Damage components cause lots of cache misses?

IMHO this cannot be answered clearly because "it depends"! How much does it cost to bring data in a well defined order opposed to what performance can be gained?

 

Let's think of an FPS. There are up to 10 characters, damage is applied suddenly by shooting. Your game loop runs 60 times per second. Even if each character would shoot 2 times per second you get an average of 1 shot per 3 loop iterations. That is hardly something worth any effort for optimization. That doesn't mean that e.g. Health isn't stored well organized in a table inside a Soundness sub-system.

 

Now let's think of an RPG with 80+ NPCs, and poisoning, illness, bleeding, curses, and what else that can be applied by both the players as well as NPCs. Further there are buffs and de-buffs and such. In other words, damage may occur not (only) suddenly but lastingly ((or need it to be "lasting" only?)). We can think of tables for health values as well as different sorts of lasting damage, hosted in a damage sub-system and ordered the same way, and being processed on an update() invocation basis. This still will probably not make much a difference in performance, but it is relatively easy to implement that way.

 

Notice that the detection of sudden damage should not belong to Damage and Health directly but to the more low-level CollisionVolume stuff. Damage is then an effect caused as collision response, so to say. If you need to decouple collision detection from damage application, you can again use queues that will be written by the collision detection and read by the aforementioned update() method.

 

My 2 €-Cents. 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this