perf: list ticking/dirty optimization

This commit is contained in:
cha0s 2019-05-03 13:26:51 -05:00
parent 9317c69c57
commit 9909b12c41
3 changed files with 53 additions and 30 deletions

View File

@ -278,6 +278,9 @@ export class Entity extends decorate(Resource) {
} }
instance.isDirty = false; instance.isDirty = false;
this.isDirty = true; this.isDirty = true;
if (this.is('listed')) {
this.list.markEntityDirty(this);
}
this._setInstanceState(state, type, instance); this._setInstanceState(state, type, instance);
} }
}); });

View File

@ -17,7 +17,9 @@ export class EntityList extends decorate(class {}) {
constructor() { constructor() {
super(); super();
this._afterDestructionTickers = []; this._afterDestructionTickers = [];
this._dirtyEntities = [];
this._entities = {}; this._entities = {};
this._flatEntities = [];
this._quadTree = new QuadTree(); this._quadTree = new QuadTree();
} }
@ -31,15 +33,19 @@ export class EntityList extends decorate(class {}) {
addEntity(entity) { addEntity(entity) {
const uuid = entity.instanceUuid; const uuid = entity.instanceUuid;
this._entities[uuid] = entity; this._entities[uuid] = entity;
this._flatEntities.push(entity);
this.state = this.state.set(uuid, entity.state); this.state = this.state.set(uuid, entity.state);
entity.addTrait('listed'); entity.addTrait('listed');
entity.list = this; entity.setIntoList(this);
entity.once('destroy', () => { entity.once('destroy', () => {
this.removeEntity(entity); this.removeEntity(entity);
// In the process of destroying, allow entities to specify tickers that // In the process of destroying, allow entities to specify tickers that
// must live on past destruction. // must live on past destruction.
const tickers = entity.invokeHookFlat('afterDestructionTickers'); const tickers = entity.invokeHookFlat('afterDestructionTickers');
this._afterDestructionTickers.push(...tickers); for (let i = 0; i < tickers.length; i++) {
const ticker = tickers[i];
this._afterDestructionTickers.push(ticker);
}
}); });
this.emit('entityAdded', entity); this.emit('entityAdded', entity);
} }
@ -51,11 +57,17 @@ export class EntityList extends decorate(class {}) {
} }
findEntity(uuid) { findEntity(uuid) {
if (this._entities[uuid]) { if (uuid in this._entities) {
return this._entities[uuid]; return this._entities[uuid];
} }
} }
markEntityDirty(entity) {
if (-1 === this._dirtyEntities.indexOf(entity)) {
this._dirtyEntities.push(entity);
}
}
patchStateStep(uuid, step) { patchStateStep(uuid, step) {
const entity = this._entities[uuid]; const entity = this._entities[uuid];
if ('/' === step.path) { if ('/' === step.path) {
@ -90,6 +102,8 @@ export class EntityList extends decorate(class {}) {
removeEntity(entity) { removeEntity(entity) {
const uuid = entity.instanceUuid; const uuid = entity.instanceUuid;
delete this._entities[uuid]; delete this._entities[uuid];
const index = this._flatEntities.indexOf(entity);
this._flatEntities.splice(index, 1);
this.state = this.state.delete(uuid); this.state = this.state.delete(uuid);
this.emit('entityRemoved', entity); this.emit('entityRemoved', entity);
if (entity.is('listed')) { if (entity.is('listed')) {
@ -99,24 +113,17 @@ export class EntityList extends decorate(class {}) {
tick(elapsed) { tick(elapsed) {
// Run after destruction tickers. // Run after destruction tickers.
this.tickAfterDestructionTickers(elapsed); if (this._afterDestructionTickers.length > 0) {
this.tickAfterDestructionTickers(elapsed);
}
// Run normal tickers. // Run normal tickers.
for (const uuid in this._entities) { for (let i = 0; i < this._flatEntities.length; i++) {
const entity = this._entities[uuid]; const entity = this._flatEntities[i];
entity.tick(elapsed); entity.tick(elapsed);
} }
// Update state. // Update state.
if (AVOCADO_SERVER) { if (AVOCADO_SERVER) {
this.state = this.state.withMutations((state) => { this.tickMutateState();
for (const uuid in this._entities) {
const entity = this._entities[uuid];
if (!entity.isDirty) {
continue;
}
entity.isDirty = false;
state.set(uuid, entity.state);
}
});
} }
} }
@ -135,6 +142,17 @@ export class EntityList extends decorate(class {}) {
} }
} }
tickMutateState(state) {
this.state = this.state.withMutations((state) => {
for (let i = 0; i < this._dirtyEntities.length; i++) {
const entity = this._dirtyEntities[i];
entity.isDirty = false;
state.set(entity.$$avocado_property_instanceUuid, entity.state);
}
});
this._dirtyEntities = [];
}
visibleEntities(query) { visibleEntities(query) {
const entities = []; const entities = [];
const entitiesTrack = []; const entitiesTrack = [];

View File

@ -5,29 +5,19 @@ import {Trait} from '../trait';
export class Listed extends Trait { export class Listed extends Trait {
initialize() { initialize() {
this._list = undefined; this.entity.list = undefined;
this.quadTreeAabb = []; this.quadTreeAabb = [];
this.quadTreeNodes = []; this.quadTreeNodes = [];
} }
destroy() { destroy() {
this.removeQuadTreeNodes(); this.removeQuadTreeNodes();
delete this._list; delete this.entity.list;
this.entity.emit('removedFromList'); this.entity.emit('removedFromList');
} }
get list() {
return this._list;
}
set list(list) {
this._list = list;
this.addQuadTreeNodes();
this.entity.emit('addedToList');
}
addQuadTreeNodes() { addQuadTreeNodes() {
const list = this._list; const list = this.entity.list;
if (!list) { if (!list) {
return; return;
} }
@ -54,7 +44,7 @@ export class Listed extends Trait {
} }
removeQuadTreeNodes() { removeQuadTreeNodes() {
const list = this._list; const list = this.entity.list;
if (!list) { if (!list) {
return; return;
} }
@ -97,5 +87,17 @@ export class Listed extends Trait {
} }
} }
methods() {
return {
setIntoList: (list) => {
this.entity.list = list;
this.addQuadTreeNodes();
this.entity.emit('addedToList');
},
};
}
} }