Installer
J'ai une architecture de composant d'entité où les entités peuvent avoir un ensemble d'attributs (qui sont des données pures sans comportement) et il existe des systèmes qui exécutent la logique d'entité qui agissent sur ces données. Essentiellement, dans un peu de pseudo-code:
Entity
{
id;
map<id_type, Attribute> attributes;
}
System
{
update();
vector<Entity> entities;
}
Un système qui se déplace le long de toutes les entités à un rythme constant pourrait être
MovementSystem extends System
{
update()
{
for each entity in entities
position = entity.attributes["position"];
position += vec3(1,1,1);
}
}
Essentiellement, j'essaie de paralléliser update () aussi efficacement que possible. Cela peut être fait en exécutant des systèmes entiers en parallèle ou en donnant à chaque mise à jour () d'un système deux composants afin que différents threads puissent exécuter la mise à jour du même système, mais pour un sous-ensemble différent d'entités enregistrées avec ce système.
Problème
Dans le cas du MovementSystem montré, la parallélisation est triviale. Étant donné que les entités ne dépendent pas les unes des autres et ne modifient pas les données partagées, nous pourrions simplement déplacer toutes les entités en parallèle.
Cependant, ces systèmes nécessitent parfois que les entités interagissent les unes avec les autres (lecture / écriture de données de / vers), parfois au sein du même système, mais souvent entre différents systèmes qui dépendent les uns des autres.
Par exemple, dans un système de physique, les entités peuvent parfois interagir les unes avec les autres. Deux objets entrent en collision, leurs positions, vitesses et autres attributs sont lus à partir d'eux, sont mis à jour, puis les attributs mis à jour sont réécrits dans les deux entités.
Et avant que le système de rendu dans le moteur puisse commencer à rendre des entités, il doit attendre que les autres systèmes terminent l'exécution pour s'assurer que tous les attributs pertinents sont ce dont ils ont besoin.
Si nous essayons de paralléliser aveuglément cela, cela conduira à des conditions de course classiques où différents systèmes peuvent lire et modifier des données en même temps.
Idéalement, il existerait une solution où tous les systèmes pourraient lire les données de toutes les entités qu'ils souhaitent, sans avoir à se soucier que d'autres systèmes modifient ces mêmes données en même temps, et sans que le programmeur se soucie de l'ordre correct de l'exécution et de la parallélisation de ces systèmes manuellement (ce qui n'est parfois même pas possible).
Dans une implémentation de base, cela pourrait être réalisé en plaçant simplement toutes les lectures et écritures de données dans des sections critiques (en les gardant avec des mutex). Mais cela induit une grande quantité de temps d'exécution et n'est probablement pas adapté aux applications sensibles aux performances.
Solution?
À mon avis, une solution possible serait un système où la lecture / la mise à jour et l'écriture des données sont séparées, de sorte que dans une phase coûteuse, les systèmes ne lisent que les données et calculent ce dont ils ont besoin pour calculer, mettent en cache les résultats, puis écrivent tous les données modifiées reviennent aux entités cibles dans une passe d'écriture distincte. Tous les systèmes agiraient sur les données dans l'état où elles se trouvaient au début de la trame, puis avant la fin de la trame, lorsque tous les systèmes ont terminé la mise à jour, une passe d'écriture sérialisée se produit où la mise en cache résulte de tous les différents les systèmes sont itérés et réécrits aux entités cibles.
Ceci est basé sur l'idée (peut-être fausse?) Que la victoire de la parallélisation facile pourrait être suffisamment importante pour surpasser le coût (à la fois en termes de performances d'exécution et de surcharge de code) de la mise en cache des résultats et de la passe d'écriture.
La question
Comment un tel système pourrait-il être mis en œuvre pour obtenir des performances optimales? Quels sont les détails d'implémentation d'un tel système et quelles sont les conditions préalables pour un système Entity-Component qui souhaite utiliser cette solution?