From 1dd32066d2832c23761ac66fcb35811ca0c4a219 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 7 Mar 2018 02:18:08 +0900 Subject: [PATCH] packing: Add Extend --- internal/packing/packing.go | 117 +++++++++++++++++++- internal/packing/packing_test.go | 177 +++++++++++++++++++------------ shared.go | 39 +++++-- 3 files changed, 250 insertions(+), 83 deletions(-) diff --git a/internal/packing/packing.go b/internal/packing/packing.go index 77c1c1dce..0578de3e0 100644 --- a/internal/packing/packing.go +++ b/internal/packing/packing.go @@ -16,16 +16,20 @@ package packing import ( + "errors" + "github.com/hajimehoshi/ebiten/internal/sync" ) const ( - MaxSize = 2048 - minSize = 1 + initSize = 1024 + MaxSize = 4096 + minSize = 1 ) type Page struct { root *Node + size int m sync.Mutex } @@ -138,15 +142,30 @@ func (p *Page) alloc(n *Node, width, height int) *Node { return nil } +func (p *Page) ensureSize() { + if p.size == 0 { + p.size = initSize + } +} + +func (p *Page) Size() int { + p.m.Lock() + p.ensureSize() + s := p.size + p.m.Unlock() + return s +} + func (p *Page) Alloc(width, height int) *Node { p.m.Lock() if width <= 0 || height <= 0 { panic("bsp: width and height must > 0") } + p.ensureSize() if p.root == nil { p.root = &Node{ - width: MaxSize, - height: MaxSize, + width: p.size, + height: p.size, } } if width < minSize { @@ -183,3 +202,93 @@ func (p *Page) free(node *Node) { p.free(node.parent) } } + +func walk(n *Node, f func(n *Node) error) error { + if err := f(n); err != nil { + return err + } + if n.child0 != nil { + if err := walk(n.child0, f); err != nil { + return err + } + } + if n.child1 != nil { + if err := walk(n.child1, f); err != nil { + return err + } + } + return nil +} + +func (p *Page) Extend() bool { + p.m.Lock() + defer p.m.Unlock() + + p.ensureSize() + if p.size >= MaxSize { + return false + } + newSize := p.size * 2 + edgeNodes := []*Node{} + abort := errors.New("abort") + aborted := false + _ = walk(p.root, func(n *Node) error { + if n.x+n.width < p.size && n.y+n.height < p.size { + return nil + } + if n.used { + aborted = true + return abort + } + edgeNodes = append(edgeNodes, n) + return nil + }) + if aborted { + leftUpper := p.root + leftLower := &Node{ + x: 0, + y: p.size, + width: p.size, + height: newSize - p.size, + } + left := &Node{ + x: 0, + y: 0, + width: p.size, + height: p.size, + child0: leftUpper, + child1: leftLower, + } + leftUpper.parent = left + leftLower.parent = left + + right := &Node{ + x: p.size, + y: 0, + width: newSize - p.size, + height: newSize, + } + p.root = &Node{ + x: 0, + y: 0, + width: newSize, + height: newSize, + child0: left, + child1: right, + } + left.parent = p.root + right.parent = p.root + } else { + for _, n := range edgeNodes { + if n.x+n.width == p.size { + n.width += newSize - p.size + } + if n.y+n.height == p.size { + n.height += newSize - p.size + } + } + } + + p.size = newSize + return true +} diff --git a/internal/packing/packing_test.go b/internal/packing/packing_test.go index 91cf0a709..cf2037f81 100644 --- a/internal/packing/packing_test.go +++ b/internal/packing/packing_test.go @@ -52,7 +52,7 @@ func TestPage(t *testing.T) { {0, 0, 0}, {0, 0, 2}, {0, 0, 4}, - {MaxSize, MaxSize, -1}, + {1024, 1024, -1}, }, Out: []*Rect{ {0, 0, 100, 100}, @@ -67,108 +67,108 @@ func TestPage(t *testing.T) { nil, nil, nil, - {0, 0, MaxSize, MaxSize}, + {0, 0, 1024, 1024}, }, }, { In: []Op{ - {200, 400, -1}, - {MaxSize, MaxSize, -1}, - {200, 400, -1}, - {100, 100, -1}, - {400, 400, -1}, - {MaxSize, MaxSize, -1}, - {1000, 1000, -1}, - {1200, 1200, -1}, + {100, 200, -1}, + {1024, 1024, -1}, + {100, 200, -1}, + {50, 50, -1}, {200, 200, -1}, + {1024, 1024, -1}, + {500, 500, -1}, + {600, 600, -1}, + {100, 100, -1}, {0, 0, 2}, - {200, 400, -1}, + {100, 200, -1}, }, Out: []*Rect{ - {0, 0, 200, 400}, + {0, 0, 100, 200}, nil, - {0, 400, 200, 400}, - {0, 800, 100, 100}, - {200, 0, 400, 400}, + {0, 200, 100, 200}, + {0, 400, 50, 50}, + {100, 0, 200, 200}, nil, - {200, 400, 1000, 1000}, + {100, 200, 500, 500}, nil, - {0, 900, 200, 200}, + {0, 450, 100, 100}, nil, - {0, 400, 200, 400}, + {0, 200, 100, 200}, }, }, { In: []Op{ - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, - {512, 512, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, + {256, 256, -1}, - {512, 512, -1}, + {256, 256, -1}, }, Out: []*Rect{ - {0, 0, 512, 512}, - {0, 512, 512, 512}, - {0, 1024, 512, 512}, - {0, 1536, 512, 512}, + {0, 0, 256, 256}, + {0, 256, 256, 256}, + {0, 512, 256, 256}, + {0, 768, 256, 256}, - {512, 0, 512, 512}, - {1024, 0, 512, 512}, - {1536, 0, 512, 512}, - {512, 512, 512, 512}, + {256, 0, 256, 256}, + {512, 0, 256, 256}, + {768, 0, 256, 256}, + {256, 256, 256, 256}, - {512, 1024, 512, 512}, - {512, 1536, 512, 512}, - {1024, 512, 512, 512}, - {1536, 512, 512, 512}, + {256, 512, 256, 256}, + {256, 768, 256, 256}, + {512, 256, 256, 256}, + {768, 256, 256, 256}, - {1024, 1024, 512, 512}, - {1024, 1536, 512, 512}, - {1536, 1024, 512, 512}, - {1536, 1536, 512, 512}, + {512, 512, 256, 256}, + {512, 768, 256, 256}, + {768, 512, 256, 256}, + {768, 768, 256, 256}, nil, }, }, { In: []Op{ - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, - {600, 600, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, + {300, 300, -1}, }, Out: []*Rect{ - {0, 0, 600, 600}, - {0, 600, 600, 600}, - {0, 1200, 600, 600}, - {600, 0, 600, 600}, - {1200, 0, 600, 600}, - {600, 600, 600, 600}, - {600, 1200, 600, 600}, - {1200, 600, 600, 600}, - {1200, 1200, 600, 600}, + {0, 0, 300, 300}, + {0, 300, 300, 300}, + {0, 600, 300, 300}, + {300, 0, 300, 300}, + {600, 0, 300, 300}, + {300, 300, 300, 300}, + {300, 600, 300, 300}, + {600, 300, 300, 300}, + {600, 600, 300, 300}, nil, }, }, @@ -206,3 +206,40 @@ func TestPage(t *testing.T) { } } } + +func TestExtend(t *testing.T) { + p := &Page{} + s := p.Size() + p.Alloc(s/2, s/2) + p.Extend() + if p.Alloc(s*3/2, s*2) == nil { + t.Fail() + } + if p.Alloc(s/2, s*3/2) == nil { + t.Fail() + } + if p.Alloc(1, 1) != nil { + t.Fail() + } +} + +func TestExtend2(t *testing.T) { + p := &Page{} + s := p.Size() + p.Alloc(s/2, s/2) + n1 := p.Alloc(s/2, s/2) + n2 := p.Alloc(s/2, s/2) + p.Alloc(s/2, s/2) + p.Free(n1) + p.Free(n2) + p.Extend() + if p.Alloc(s, s*2) == nil { + t.Fail() + } + if p.Alloc(s, s) == nil { + t.Fail() + } + if p.Alloc(s, s) != nil { + t.Fail() + } +} diff --git a/shared.go b/shared.go index 5b0e30222..883cc6e39 100644 --- a/shared.go +++ b/shared.go @@ -15,6 +15,8 @@ package ebiten import ( + "github.com/hajimehoshi/ebiten/internal/graphics" + "github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/packing" "github.com/hajimehoshi/ebiten/internal/restorable" "github.com/hajimehoshi/ebiten/internal/sync" @@ -71,22 +73,41 @@ func newSharedImagePart(width, height int) *sharedImagePart { return nil } for _, s := range theSharedImages { - if n := s.page.Alloc(width, height); n != nil { - return &sharedImagePart{ - sharedImage: s, - node: n, + for { + if n := s.page.Alloc(width, height); n != nil { + return &sharedImagePart{ + sharedImage: s, + node: n, + } } + if !s.page.Extend() { + break + } + newSharedImage := restorable.NewImage(s.page.Size(), s.page.Size(), false) + newSharedImage.DrawImage(s.restorable, 0, 0, s.page.Size(), s.page.Size(), nil, nil, opengl.CompositeModeCopy, graphics.FilterNearest) + s.restorable.Dispose() + + s.restorable = newSharedImage } } - s := &sharedImage{ - restorable: restorable.NewImage(packing.MaxSize, packing.MaxSize, false), - } - theSharedImages = append(theSharedImages, s) - n := s.page.Alloc(width, height) + s := &sharedImage{} + var n *packing.Node + for { + n = s.page.Alloc(width, height) + if n != nil { + break + } + if !s.page.Extend() { + break + } + } if n == nil { panic("not reached") } + s.restorable = restorable.NewImage(s.page.Size(), s.page.Size(), false) + theSharedImages = append(theSharedImages, s) + return &sharedImagePart{ sharedImage: s, node: n,