From 589c94e64b1c94d8947d37c37bf32ab3a702bedc Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Sun, 22 Aug 2021 15:15:29 +0200 Subject: [PATCH] gpu: optimize atlas packer for smallest sizes Previously, the packer optimized for the smalles space where the image fits. Empirically, smaller atlases can be achieved by greedily picking the smallest total area. Signed-off-by: Elias Naur --- gpu/pack.go | 78 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/gpu/pack.go b/gpu/pack.go index c4dbaadb..abf738c7 100644 --- a/gpu/pack.go +++ b/gpu/pack.go @@ -46,40 +46,54 @@ func (p *packer) newPage() { } func (p *packer) tryAdd(s image.Point) (placement, bool) { - // Go backwards to prioritize smaller spaces first. - for i := len(p.spaces) - 1; i >= 0; i-- { - space := p.spaces[i] + var ( + bestIdx = -1 + bestSpace image.Rectangle + bestSize = image.Pt(p.maxDim, p.maxDim) + ) + // Go backwards to prioritize smaller spaces. + for i, space := range p.spaces { rightSpace := space.Dx() - s.X bottomSpace := space.Dy() - s.Y - if rightSpace >= 0 && bottomSpace >= 0 { - // Remove space. - p.spaces[i] = p.spaces[len(p.spaces)-1] - p.spaces = p.spaces[:len(p.spaces)-1] - // Put s in the top left corner and add the (at most) - // two smaller spaces. - pos := space.Min - if bottomSpace > 0 { - p.spaces = append(p.spaces, image.Rectangle{ - Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, - Max: image.Point{X: space.Max.X, Y: space.Max.Y}, - }) - } - if rightSpace > 0 { - p.spaces = append(p.spaces, image.Rectangle{ - Min: image.Point{X: pos.X + s.X, Y: pos.Y}, - Max: image.Point{X: space.Max.X, Y: pos.Y + s.Y}, - }) - } - idx := len(p.sizes) - 1 - size := &p.sizes[idx] - if x := pos.X + s.X; x > size.X { - size.X = x - } - if y := pos.Y + s.Y; y > size.Y { - size.Y = y - } - return placement{Idx: idx, Pos: pos}, true + if rightSpace < 0 || bottomSpace < 0 { + continue + } + idx := len(p.sizes) - 1 + size := p.sizes[idx] + if x := space.Min.X + s.X; x > size.X { + size.X = x + } + if y := space.Min.Y + s.Y; y > size.Y { + size.Y = y + } + if size.X*size.Y < bestSize.X*bestSize.Y { + bestIdx = i + bestSpace = space + bestSize = size } } - return placement{}, false + if bestIdx == -1 { + return placement{}, false + } + // Remove space. + p.spaces[bestIdx] = p.spaces[len(p.spaces)-1] + p.spaces = p.spaces[:len(p.spaces)-1] + // Put s in the top left corner and add the (at most) + // two smaller spaces. + pos := bestSpace.Min + if rem := bestSpace.Dy() - s.Y; rem > 0 { + p.spaces = append(p.spaces, image.Rectangle{ + Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, + Max: image.Point{X: bestSpace.Max.X, Y: bestSpace.Max.Y}, + }) + } + if rem := bestSpace.Dx() - s.X; rem > 0 { + p.spaces = append(p.spaces, image.Rectangle{ + Min: image.Point{X: pos.X + s.X, Y: pos.Y}, + Max: image.Point{X: bestSpace.Max.X, Y: pos.Y + s.Y}, + }) + } + idx := len(p.sizes) - 1 + p.sizes[idx] = bestSize + return placement{Idx: idx, Pos: pos}, true }