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 <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-08-22 15:15:29 +02:00
parent baa98e7737
commit 589c94e64b
+46 -32
View File
@@ -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
}