less cache, more compute

This commit is contained in:
Josh Deprez 2021-09-24 17:01:17 +10:00
parent adf933f497
commit 7b6dd76f79

View file

@ -36,10 +36,9 @@ type DrawDAG struct {
Hides Hides
dag dag
boxCache map[DrawBoxer]geom.Box // used to find components that moved boxCache map[DrawBoxer]geom.Box // used to find components that moved
chunks map[image.Point]drawerSet // chunk coord -> drawers with bounding rects intersecting chunk chunks map[image.Point]drawerSet // chunk coord -> drawers with bounding rects intersecting chunk
chunksRev map[DrawBoxer]image.Rectangle // comopnent -> rectangle of chunk coords game *Game
game *Game
} }
// Draw draws everything in the DAG in topological order. // Draw draws everything in the DAG in topological order.
@ -111,7 +110,6 @@ func (d *DrawDAG) Prepare(game *Game) error {
d.dag = make(dag) d.dag = make(dag)
d.boxCache = make(map[DrawBoxer]geom.Box) d.boxCache = make(map[DrawBoxer]geom.Box)
d.chunks = make(map[image.Point]drawerSet) d.chunks = make(map[image.Point]drawerSet)
d.chunksRev = make(map[DrawBoxer]image.Rectangle)
d.game = game d.game = game
// Because Game.LoadAndPrepare calls Prepare in a post-order walk, all the // Because Game.LoadAndPrepare calls Prepare in a post-order walk, all the
@ -132,18 +130,19 @@ func (d *DrawDAG) String() string { return "DrawDAG" }
func (d *DrawDAG) Update() error { func (d *DrawDAG) Update() error {
// Re-evaluate bounding boxes for all descendants. If a box has changed, // Re-evaluate bounding boxes for all descendants. If a box has changed,
// fix up the edges by removing and re-adding the vertex. // fix up the edges by removing and re-adding the vertex.
// Thanks once again to postorder traversal, this happens after all // Thanks once again to postorder traversal in Game.Update, this happens
// descendant updates. // after all descendant updates.
// TODO: more flexible update ordering system...
var readd []DrawBoxer var readd []DrawBoxer
for db, bb := range d.boxCache { for db, bb := range d.boxCache {
nbb := db.BoundingBox() nbb := db.BoundingBox()
if bb != nbb { if bb != nbb {
d.Unregister(db) d.unregisterOne(db)
readd = append(readd, db) readd = append(readd, db)
} }
} }
for _, db := range readd { for _, db := range readd {
d.Register(db, nil) d.registerOne(db)
} }
return nil return nil
} }
@ -190,34 +189,34 @@ func (d *DrawDAG) registerOne(x DrawBoxer) {
// Update the reverse chunk map // Update the reverse chunk map
xbr := xb.BoundingRect(d.game.Projection) xbr := xb.BoundingRect(d.game.Projection)
revr := image.Rectangle{ min := xbr.Min.Div(d.ChunkSize)
Min: xbr.Min.Div(d.ChunkSize), max := xbr.Max.Sub(image.Pt(1, 1)).Div(d.ChunkSize)
Max: xbr.Max.Sub(image.Pt(1, 1)).Div(d.ChunkSize),
}
d.chunksRev[x] = revr
// Find possible edges between x and items in the overlapping cells. // Find possible edges between x and items in the overlapping chunks.
// First, a set of all the items in those cells. // First, a set of all the items in those chunks.
cand := make(drawerSet) cand := make(drawerSet)
var p image.Point var p image.Point
for p.Y = revr.Min.Y; p.Y <= revr.Max.Y; p.Y++ { for p.Y = min.Y; p.Y <= max.Y; p.Y++ {
for p.X = revr.Min.X; p.X <= revr.Max.X; p.X++ { for p.X = min.X; p.X <= max.X; p.X++ {
cell := d.chunks[p] chunk := d.chunks[p]
if cell == nil { if chunk == nil {
cell = make(drawerSet) d.chunks[p] = drawerSet{x: {}}
d.chunks[p] = cell continue
} }
// Merge cell contents into cand // Merge chunk contents into cand
for c := range cell { for c := range chunk {
cand[c] = struct{}{} cand[c] = struct{}{}
} }
// Add x to cell // Add x to chunk
cell[x] = struct{}{} chunk[x] = struct{}{}
} }
} }
// Add edges between x and elements of cand // Add edges between x and elements of cand
πsign := d.game.Projection.Sign() πsign := d.game.Projection.Sign()
for c := range cand { for c := range cand {
if x == c {
continue
}
y := c.(DrawBoxer) y := c.(DrawBoxer)
// Bounding rectangle overlap test // Bounding rectangle overlap test
// No overlap, no edge. // No overlap, no edge.
@ -251,14 +250,14 @@ func (d *DrawDAG) Unregister(component interface{}) {
func (d *DrawDAG) unregisterOne(x DrawBoxer) { func (d *DrawDAG) unregisterOne(x DrawBoxer) {
// Remove from chunk map // Remove from chunk map
revr := d.chunksRev[x] xbr := d.boxCache[x].BoundingRect(d.game.Projection)
for j := revr.Min.Y; j <= revr.Max.Y; j++ { min := xbr.Min.Div(d.ChunkSize)
for i := revr.Min.X; i <= revr.Max.X; i++ { max := xbr.Max.Sub(image.Pt(1, 1)).Div(d.ChunkSize)
for j := min.Y; j <= max.Y; j++ {
for i := min.X; i <= max.X; i++ {
delete(d.chunks[image.Pt(i, j)], x) delete(d.chunks[image.Pt(i, j)], x)
} }
} }
// Remove from reverse chunk map
delete(d.chunksRev, x)
// Remove from box cache // Remove from box cache
delete(d.boxCache, x) delete(d.boxCache, x)
// Remove from DAG // Remove from DAG