Jump to content
  • Advertisement
Sign in to follow this  
dutt

Grasping data-oriented design

This topic is 2547 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Lately I've read a lot about data-oriented design and thought I'd give this a shot but I'm having a spot of trouble with the actual implementation. As I've understood it from this not spending cycles on a lot of virtual calls is a big point and it seems a valid. Whether or not it's worth the trouble isn't interesting at this point, now I'm mainly interested in how I will do this.

I've got a baseclass and a number of subclasses, but I doubt I'll have any subsubclass(and so on...) . Each of the subclass need to be handled in a specific way, originally I'd have done something classic. A base class with a virtual function, subclasses and then keeping a list of the baseclass.

A basic hierarchy. So now I'm wondering how, or if it's possible, to do this without resorting to a list of each type of object and iterating over them one at a time.

Share this post


Link to post
Share on other sites
Advertisement

So now I'm wondering how, or if it's possible, to do this without resorting to a list of each type of object and iterating over them one at a time.


I'm not all that informed on the subject, but I thought that was the route for dod. Do everything you can with one type of data, before moving onto the next. Trying to keep what you need in cache and processing all of it, before swapping the cache for something else.

Share this post


Link to post
Share on other sites
tl;dr version: It's not so much that you make a list of each type and iterate over each list, it's more that you break each object up into its constituent components and then update lists of components.

Long version: Lets say are converting an OOP object hierarchy to DOD. In the OOP land you might have a hierarchy like this:

CEntity
- CRenderEntity
- CPhysicsEntity
- CCreature
- CAICreature
- CPlayer
- CProjectile
- CBackgroundGeometry


When you call Update any of these objects, the object is going to call its base class Update function, then do its own Update. For example, CProjectile::Update() might look something like this:

void CProjectile::Update() {
CPhysicsEntity::Update();
projectile_update_internal();
}


The series of function calls that happens after you call CProjectile::Update might end up like this:
CEntity::Update()
CRenderEntity::Update()
CPhsyicsEntity::Update()
CProjectile::projectile_update_internal()

So now if you have a hundred CProjectiles, and you update each on in a row, then you'll be doing something like CEntity::Update() CRenderEntity::Update() CPhysicsEntity::Update CProjectile::projectile_update_internal() CEntity::Update() CRenderEntity::Update() CPhysicsEntity::Update() ... etc.

A DOD structure would have you split your objects into components. Your CProjectile component would have a Physics component and a Renderable component and a Projectile component. The CPlayerCreature would have a Physics component, Renderable component, Creature component and PlayerCreature component. You can probably see how the rest of the objects would be componentized.

Now each object is basically a list of pointers to each component it has, and when you go to update all your game objects, you wouldn't iterate through a list of objects and update each object one at a time, you would instead update each type of component you have. You would iterate through a list of Renderable components and update all of those, then iterate through your list of Physics components and update those, then iterate through your list of Creature components, etc. In this way you can prevent unnecessary virtual function calls and help maintain some cache coherency.

Share this post


Link to post
Share on other sites

So now I'm wondering how, or if it's possible, to do this without resorting to a list of each type of object and iterating over them one at a time.


Might not be as bad as you think. It can be largely automated:


struct updater_base
{
virtual ~updater_base() { }
virtual void update() = 0;

// add extra virtual functions as needed
// ...
};

template<typename Updateable>
struct updater : updater_base
{
virtual void update()
{
for (std::size_t i = 0, n = items.size(); i != n; ++i)
items.update();
}
std::vector<Updateable> items;
};

struct moose
{
void update(); // <-- not virtual, but acts as such
// ...
};

struct goose
{
void update(); // <-- not virtual, but acts as such
// ...
};

// etc
// ...

int main()
{
std::vector<shared_ptr<updater_base> > updateables;

shared_ptr<updater<moose> > meese(new updater<moose>);
meese->items.push_back(moose());
meese->items.push_back(moose());
meese->items.push_back(moose());

shared_ptr<updater<goose> > geese(new updater<goose>);
geese->items.push_back(goose());
geese->items.push_back(goose());
geese->items.push_back(goose());

updateables.push_back(meese);
updateables.push_back(geese);

for (std::size_t i = 0, n = updateables.size(); i != n; ++i)
updateables.update(); // one virtual call per type.

return 0;
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!