diff --git a/internal/atlas/export_test.go b/internal/atlas/export_test.go index 4116248f3..b20e2e1f2 100644 --- a/internal/atlas/export_test.go +++ b/internal/atlas/export_test.go @@ -60,3 +60,5 @@ func (i *Image) EnsureIsolatedForTesting() { } var FlushDeferredForTesting = flushDeferred + +var ToPowerOf2 = toPowerOf2 diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 00f1034d7..66aebb43d 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -728,6 +728,17 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics) error { return nil } +func toPowerOf2(x int) int { + if x <= 0 { + return 0 + } + p2 := 1 + for p2*2 <= x { + p2 *= 2 + } + return p2 +} + func BeginFrame(graphicsDriver graphicsdriver.Graphics) error { defer backendsM.Unlock() @@ -746,7 +757,7 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error { minSize = 1024 } if maxSize == 0 { - maxSize = restorable.MaxImageSize(graphicsDriver) + maxSize = toPowerOf2(restorable.MaxImageSize(graphicsDriver)) } }) if err != nil { diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 19d375112..5aa0b5c1d 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -736,4 +736,52 @@ func TestImageWritePixelsModify(t *testing.T) { } } +func TestPowerOf2(t *testing.T) { + testCases := []struct { + In int + Out int + }{ + { + In: 1023, + Out: 512, + }, + { + In: 1024, + Out: 1024, + }, + { + In: 1025, + Out: 1024, + }, + { + In: 10000, + Out: 8192, + }, + { + In: 16384, + Out: 16384, + }, + { + In: 1, + Out: 1, + }, + { + In: 0, + Out: 0, + }, + { + In: -1, + Out: 0, + }, + } + + for _, tc := range testCases { + got := atlas.ToPowerOf2(tc.In) + want := tc.Out + if got != want { + t.Errorf("packing.ToPowerOf2(%d): got: %d, want: %d", tc.In, got, want) + } + } +} + // TODO: Add tests to extend image on an atlas out of the main loop diff --git a/internal/packing/packing.go b/internal/packing/packing.go index f67127174..baa95a9d8 100644 --- a/internal/packing/packing.go +++ b/internal/packing/packing.go @@ -31,7 +31,26 @@ type Page struct { maxSize int } +func isPositivePowerOf2(x int) bool { + if x <= 0 { + return false + } + for x > 1 { + if x/2*2 != x { + return false + } + x /= 2 + } + return true +} + func NewPage(initSize int, maxSize int) *Page { + if !isPositivePowerOf2(initSize) { + panic(fmt.Sprintf("packing: initSize must be a positive power of 2 but %d", initSize)) + } + if !isPositivePowerOf2(maxSize) { + panic(fmt.Sprintf("packing: maxSize must be a positive power of 2 but %d", maxSize)) + } return &Page{ width: initSize, height: initSize, @@ -263,7 +282,7 @@ func (p *Page) extendFor(width, height int) bool { if newWidth > p.maxSize || newHeight > p.maxSize { if newWidth > p.maxSize && newHeight > p.maxSize { - panic(fmt.Sprintf("packing: too big extension: (%d, %d)", newWidth, newHeight)) + panic(fmt.Sprintf("packing: too big extension: allocating size: (%d, %d), current size: (%d, %d), new size: (%d, %d), (i, j): (%d, %d), max size: %d", width, height, p.width, p.height, newWidth, newHeight, i, j, p.maxSize)) } continue }