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
+34 -20
View File
@@ -46,40 +46,54 @@ func (p *packer) newPage() {
} }
func (p *packer) tryAdd(s image.Point) (placement, bool) { func (p *packer) tryAdd(s image.Point) (placement, bool) {
// Go backwards to prioritize smaller spaces first. var (
for i := len(p.spaces) - 1; i >= 0; i-- { bestIdx = -1
space := p.spaces[i] 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 rightSpace := space.Dx() - s.X
bottomSpace := space.Dy() - s.Y bottomSpace := space.Dy() - s.Y
if rightSpace >= 0 && bottomSpace >= 0 { 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
}
}
if bestIdx == -1 {
return placement{}, false
}
// Remove space. // Remove space.
p.spaces[i] = p.spaces[len(p.spaces)-1] p.spaces[bestIdx] = p.spaces[len(p.spaces)-1]
p.spaces = 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) // Put s in the top left corner and add the (at most)
// two smaller spaces. // two smaller spaces.
pos := space.Min pos := bestSpace.Min
if bottomSpace > 0 { if rem := bestSpace.Dy() - s.Y; rem > 0 {
p.spaces = append(p.spaces, image.Rectangle{ p.spaces = append(p.spaces, image.Rectangle{
Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, Min: image.Point{X: pos.X, Y: pos.Y + s.Y},
Max: image.Point{X: space.Max.X, Y: space.Max.Y}, Max: image.Point{X: bestSpace.Max.X, Y: bestSpace.Max.Y},
}) })
} }
if rightSpace > 0 { if rem := bestSpace.Dx() - s.X; rem > 0 {
p.spaces = append(p.spaces, image.Rectangle{ p.spaces = append(p.spaces, image.Rectangle{
Min: image.Point{X: pos.X + s.X, Y: pos.Y}, Min: image.Point{X: pos.X + s.X, Y: pos.Y},
Max: image.Point{X: space.Max.X, Y: pos.Y + s.Y}, Max: image.Point{X: bestSpace.Max.X, Y: pos.Y + s.Y},
}) })
} }
idx := len(p.sizes) - 1 idx := len(p.sizes) - 1
size := &p.sizes[idx] p.sizes[idx] = bestSize
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 return placement{Idx: idx, Pos: pos}, true
} }
}
return placement{}, false
}