Merge pull request #198 from Phlosioneer/remove-belt-cache

Optimize belt cache
This commit is contained in:
tobspr 2020-06-24 20:26:22 +02:00 committed by GitHub
commit 12927ec0ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 43 deletions

View File

@ -5,6 +5,7 @@ import { BaseItem } from "../base_item";
import { Vector, enumDirection } from "../../core/vector";
import { Math_PI, Math_sin, Math_cos } from "../../core/builtins";
import { globalConfig } from "../../core/config";
import { Entity } from "../entity";
export class BeltComponent extends Component {
static getId() {
@ -12,6 +13,7 @@ export class BeltComponent extends Component {
}
static getSchema() {
// The followUpCache field is not serialized.
return {
direction: types.string,
sortedItems: types.array(types.pair(types.float, types.obj(gItemRegistry))),
@ -34,6 +36,9 @@ export class BeltComponent extends Component {
/** @type {Array<[number, BaseItem]>} */
this.sortedItems = [];
/** @type {Entity} */
this.followUpCache = null;
}
/**

View File

@ -13,14 +13,13 @@ import { MetaBeltBaseBuilding } from "../buildings/belt_base";
import { defaultBuildingVariant } from "../meta_building";
import { GameRoot } from "../root";
import { createLogger } from "../../core/logging";
import { Rectangle } from "../../core/rectangle";
const BELT_ANIM_COUNT = 6;
const SQRT_2 = Math_sqrt(2);
const logger = createLogger("belt");
/** @typedef {Array<{ entity: Entity, followUp: Entity }>} BeltCache */
export class BeltSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [BeltComponent]);
@ -66,9 +65,8 @@ export class BeltSystem extends GameSystemWithFilter {
this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this);
this.cacheNeedsUpdate = true;
/** @type {BeltCache} */
this.beltCache = [];
/** @type {Rectangle[]} */
this.smallUpdateAreas = [];
}
/**
@ -119,6 +117,12 @@ export class BeltSystem extends GameSystemWithFilter {
}
}
}
// Optimize for the common case of adding or removing buildings one at a time.
// Clicking and dragging can fire up to 4 create/destroy signals.
if (this.cacheNeedsUpdate) {
this.smallUpdateAreas.push(affectedArea.expandedInAllDirections(1));
}
}
draw(parameters) {
@ -163,42 +167,28 @@ export class BeltSystem extends GameSystemWithFilter {
return null;
}
/**
* Adds a single entity to the cache
* @param {Entity} entity
* @param {BeltCache} cache
* @param {Set} visited
*/
computeSingleBeltCache(entity, cache, visited) {
// Check for double visit
if (visited.has(entity.uid)) {
return;
}
visited.add(entity.uid);
const followUp = this.findFollowUpEntity(entity);
if (followUp) {
// Process followup first
this.computeSingleBeltCache(followUp, cache, visited);
}
cache.push({ entity, followUp });
}
computeBeltCache() {
logger.log("Updating belt cache");
let cache = [];
let visited = new Set();
for (let i = 0; i < this.allEntities.length; ++i) {
this.computeSingleBeltCache(this.allEntities[i], cache, visited);
if (this.smallUpdateAreas.length <= 4) {
for (let i = 0; i < this.smallUpdateAreas.length; ++i) {
const area = this.smallUpdateAreas[i];
for (let x = area.x; x < area.right(); ++x) {
for (let y = area.y; y < area.bottom(); ++y) {
const tile = this.root.map.getTileContentXY(x, y);
if (tile && tile.components.Belt) {
tile.components.Belt.followUpCache = this.findFollowUpEntity(tile);
}
}
}
}
} else {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
entity.components.Belt.followUpCache = this.findFollowUpEntity(entity);
}
}
assert(
cache.length === this.allEntities.length,
"Belt cache mismatch: Has " + cache.length + " entries but should have " + this.allEntities.length
);
this.beltCache = cache;
this.smallUpdateAreas = [];
}
update() {
@ -217,8 +207,8 @@ export class BeltSystem extends GameSystemWithFilter {
beltSpeed *= 100;
}
for (let i = 0; i < this.beltCache.length; ++i) {
const { entity, followUp } = this.beltCache[i];
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const beltComp = entity.components.Belt;
const items = beltComp.sortedItems;
@ -244,8 +234,8 @@ export class BeltSystem extends GameSystemWithFilter {
maxProgress = 1 - globalConfig.itemSpacingOnBelts;
} else {
// Otherwise our progress depends on the follow up
if (followUp) {
const spacingOnBelt = followUp.components.Belt.getDistanceToFirstItemCenter();
if (beltComp.followUpCache) {
const spacingOnBelt = beltComp.followUpCache.components.Belt.getDistanceToFirstItemCenter();
maxProgress = Math.min(2, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt);
// Useful check, but hurts performance
@ -270,8 +260,8 @@ export class BeltSystem extends GameSystemWithFilter {
progressAndItem[0] = Math.min(maxProgress, progressAndItem[0] + speedMultiplier * beltSpeed);
if (progressAndItem[0] >= 1.0) {
if (followUp) {
const followUpBelt = followUp.components.Belt;
if (beltComp.followUpCache) {
const followUpBelt = beltComp.followUpCache.components.Belt;
if (followUpBelt.canAcceptItem()) {
followUpBelt.takeItem(progressAndItem[1], progressAndItem[0] - 1.0);
items.splice(itemIndex, 1);