Impostare
Ho un'architettura a componenti di entità in cui le entità possono avere un insieme di attributi (che sono dati puri senza comportamento) e esistono sistemi che eseguono la logica di entità che agiscono su quei dati. In sostanza, in qualche pseudo-codice:
Entity
{
id;
map<id_type, Attribute> attributes;
}
System
{
update();
vector<Entity> entities;
}
Potrebbe essere un sistema che si sposta semplicemente lungo tutte le entità a un ritmo costante
MovementSystem extends System
{
update()
{
for each entity in entities
position = entity.attributes["position"];
position += vec3(1,1,1);
}
}
In sostanza, sto cercando di parallelizzare update () nel modo più efficiente possibile. Questo può essere fatto eseguendo interi sistemi in parallelo o dando a ciascun aggiornamento () di un sistema un paio di componenti in modo che thread diversi possano eseguire l'aggiornamento dello stesso sistema, ma per un diverso sottoinsieme di entità registrate con quel sistema.
Problema
Nel caso del MovementSystem mostrato, la parallelizzazione è banale. Poiché le entità non dipendono l'una dall'altra e non modificano i dati condivisi, potremmo semplicemente spostare tutte le entità in parallelo.
Tuttavia, a volte questi sistemi richiedono che le entità interagiscano tra loro (leggano / scrivano i dati da / a), a volte all'interno dello stesso sistema, ma spesso tra sistemi diversi che dipendono l'uno dall'altro.
Ad esempio, in un sistema fisico a volte le entità possono interagire tra loro. Due oggetti si scontrano, le loro posizioni, velocità e altri attributi vengono letti da essi, vengono aggiornati e quindi gli attributi aggiornati vengono riscritti in entrambe le entità.
E prima che il sistema di rendering nel motore possa iniziare a renderizzare le entità, deve attendere che altri sistemi completino l'esecuzione per assicurarsi che tutti gli attributi rilevanti siano quelli che devono essere.
Se proviamo a parallelizzare ciecamente questo, porteremo a condizioni di gara classiche in cui diversi sistemi possono leggere e modificare i dati contemporaneamente.
Idealmente, esisterebbe una soluzione in cui tutti i sistemi possano leggere i dati da qualsiasi entità desideri, senza doversi preoccupare di altri sistemi che modificano gli stessi dati contemporaneamente e senza che il programmatore si preoccupi di ordinare correttamente l'esecuzione e la parallelizzazione di questi sistemi manualmente (che a volte potrebbe non essere nemmeno possibile).
In un'implementazione di base, questo potrebbe essere ottenuto semplicemente inserendo tutte le letture e le scritture di dati in sezioni critiche (proteggendole con mutex). Ma ciò induce una grande quantità di sovraccarico di runtime e probabilmente non è adatto per applicazioni sensibili alle prestazioni.
Soluzione?
A mio avviso, una possibile soluzione sarebbe un sistema in cui la lettura / l'aggiornamento e la scrittura dei dati sono separati, in modo che in una fase costosa i sistemi leggano solo i dati e calcolino ciò di cui hanno bisogno per calcolare, in qualche modo memorizzare nella cache i risultati e quindi scrivere tutto i dati modificati ritornano alle entità target in un passaggio di scrittura separato. Tutti i sistemi agirebbero sui dati nello stato in cui si trovavano all'inizio del frame e quindi prima della fine del frame, quando tutti i sistemi hanno terminato l'aggiornamento, si verifica un passaggio di scrittura serializzato in cui i risultati memorizzati nella cache provengono da tutti i diversi i sistemi vengono ripetuti e riscritti nelle entità target.
Questo si basa sull'idea (forse sbagliata?) Che la vincita alla semplice parallelizzazione potrebbe essere abbastanza grande da superare il costo (sia in termini di prestazioni di runtime sia di sovraccarico di codice) della cache dei risultati e del passaggio di scrittura.
La domanda
Come potrebbe essere implementato un tale sistema per ottenere prestazioni ottimali? Quali sono i dettagli di implementazione di un tale sistema e quali sono i prerequisiti per un sistema Entity-Component che desidera utilizzare questa soluzione?