From 352ff81b7266bdb99e8e7f42f62dd9d115151890 Mon Sep 17 00:00:00 2001 From: Tim Sarbin Date: Sun, 10 Nov 2019 20:13:10 -0500 Subject: [PATCH] Fixed texture atlas. Added GC debugging. (#140) --- d2core/engine.go | 7 +++++ d2render/sprite.go | 66 ++++++++++++++++++++++++++++++---------------- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/d2core/engine.go b/d2core/engine.go index 79a5b8ae..80cf8be9 100644 --- a/d2core/engine.go +++ b/d2core/engine.go @@ -228,7 +228,14 @@ func (v Engine) Draw(screen *ebiten.Image) { } if v.showFPS == true { ebitenutil.DebugPrintAt(screen, "vsync:"+strconv.FormatBool(ebiten.IsVsyncEnabled())+"\nFPS:"+strconv.Itoa(int(ebiten.CurrentFPS())), 5, 565) + var m runtime.MemStats + runtime.ReadMemStats(&m) + ebitenutil.DebugPrintAt(screen, "Alloc "+strconv.FormatInt(int64(m.Alloc)/1024/1024, 10), 700, 0) + ebitenutil.DebugPrintAt(screen, "Pause "+strconv.FormatInt(int64(m.PauseTotalNs/1024/1024), 10), 700, 10) + ebitenutil.DebugPrintAt(screen, "HeapSys "+strconv.FormatInt(int64(m.HeapSys/1024/1024), 10), 700, 20) + ebitenutil.DebugPrintAt(screen, "NumGC "+strconv.FormatInt(int64(m.NumGC), 10), 700, 30) } + } // SetNextScene tells the engine what scene to load on the next update cycle diff --git a/d2render/sprite.go b/d2render/sprite.go index 4f66287c..a3b4e9bf 100644 --- a/d2render/sprite.go +++ b/d2render/sprite.go @@ -47,6 +47,8 @@ type SpriteFrame struct { FrameData []byte Image *ebiten.Image cached bool + atlasX int + atlasY int } // CreateSprite creates an instance of a sprite @@ -143,36 +145,56 @@ func CreateSprite(data []byte, palette d2datadict.PaletteRec) Sprite { }(i) } wg.Wait() - totalWidth := 0 - totalHeight := 0 frame := 0 - for d := 0; d < int(result.Directions); d++ { - curMaxWidth := 0 - for f := 0; f < int(result.FramesPerDirection); f++ { - curMaxWidth = int(d2helper.Max(uint32(curMaxWidth), result.Frames[frame].Width)) - totalHeight += int(result.Frames[frame].Height) - frame++ - } - totalWidth += curMaxWidth - } - result.atlas, _ = ebiten.NewImage(totalWidth, totalHeight, ebiten.FilterNearest) - result.atlasBytes = make([]byte, totalWidth*totalHeight*4) - frame = 0 + curMaxWidth := 0 + atlasWidth := 0 + atlasHeight := 0 curX := 0 curY := 0 + const maxHeight = 8192 for d := 0; d < int(result.Directions); d++ { - curMaxWidth := 0 for f := 0; f < int(result.FramesPerDirection); f++ { + if curY+int(result.Frames[frame].Height) >= maxHeight { + curX += curMaxWidth + curY = 0 + curMaxWidth = 0 + if curX >= maxHeight { + log.Fatal("Ran out of texture atlas space!") + } + } + result.Frames[frame].atlasX = curX + result.Frames[frame].atlasY = curY curMaxWidth = int(d2helper.Max(uint32(curMaxWidth), result.Frames[frame].Width)) - result.Frames[frame].Image = result.atlas.SubImage(image.Rect(curX, curY, curX+int(result.Frames[frame].Width), curY+int(result.Frames[frame].Height))).(*ebiten.Image) + atlasWidth = int(d2helper.Max(uint32(atlasWidth), uint32(curX)+result.Frames[frame].Width)) + atlasHeight = int(d2helper.Max(uint32(atlasHeight), uint32(curY)+result.Frames[frame].Height)) + curY += int(result.Frames[frame].Height) + frame++ + } + } + p2 := 1 + for p2 < atlasWidth { + p2 <<= 1 + } + atlasWidth = p2 + p2 = 1 + for p2 < atlasHeight { + p2 <<= 1 + } + atlasHeight = p2 + result.atlas, _ = ebiten.NewImage(atlasWidth, atlasHeight, ebiten.FilterNearest) + result.atlasBytes = make([]byte, atlasWidth*atlasHeight*4) + frame = 0 + for d := 0; d < int(result.Directions); d++ { + for f := 0; f < int(result.FramesPerDirection); f++ { + f := &result.Frames[frame] + f.Image = result.atlas.SubImage(image.Rect(f.atlasX, f.atlasY, f.atlasX+int(f.Width), f.atlasY+int(f.Height))).(*ebiten.Image) curY += int(result.Frames[frame].Height) frame++ } - curX += curMaxWidth - curY = 0 } result.valid = true return result + } func (v Sprite) IsValid() bool { @@ -183,14 +205,13 @@ func (v *Sprite) cacheFrame(frame int) { if v.Frames[frame].cached { return } - r := v.Frames[frame].Image.Bounds().Min - curX := r.X - curY := r.Y totalWidth := v.atlas.Bounds().Max.X + ox := v.Frames[frame].atlasX + oy := v.Frames[frame].atlasY for y := 0; y < int(v.Frames[frame].Height); y++ { for x := 0; x < int(v.Frames[frame].Width); x++ { pix := (x + (y * int(v.Frames[frame].Width))) * 4 - idx := (curX + x + ((curY + y) * totalWidth)) * 4 + idx := (ox + x + ((oy + y) * totalWidth)) * 4 v.atlasBytes[idx] = v.Frames[frame].FrameData[pix] v.atlasBytes[idx+1] = v.Frames[frame].FrameData[pix+1] v.atlasBytes[idx+2] = v.Frames[frame].FrameData[pix+2] @@ -201,6 +222,7 @@ func (v *Sprite) cacheFrame(frame int) { log.Panic(err.Error()) } v.Frames[frame].cached = true + v.Frames[frame].FrameData = []byte{} } // GetSize returns the size of the sprite