WIP Finishing Up Texture Caching System

Apart from putting the cached bitmaps in a better place, this about
finishes up the texture caching optimization and IT IS FAST!

When I spam drag a lot of pixels around the FPS may drop to the 40's but
once the caches are warmed up the FPS returns to 60 and stays there,
even if the screen is very busy with pixels.

An undocumented debug feature: set the environment variable
DEBUG_CHUNK_COLOR='#00FFFF' to set a bitmap background color besides
white to be used when caching the chunks. It helps to visualize where on
the screen the bitmaps are being used. May go away in the future.

Changes:

- Found that the old default chunk size of 1000 was slow to generate
  bitmap images to cache. The 100px test size was fast and 128 sounds
  like a good middle ground number to pick for now.
- Fixed all the problems with scroll behavior and offset by inverting
  the sign of the scroll behavior. Scrolling to the Right and Down
  actually subtracts X,Y values instead of adds them.
这个提交包含在:
Noah 2018-10-17 23:01:21 -07:00
父节点 279a980106
当前提交 97394f6cdb
共有 7 个文件被更改,包括 50 次插入42 次删除

26
balance/debug.go 普通文件
查看文件

@ -0,0 +1,26 @@
package balance
import (
"os"
"git.kirsle.net/apps/doodle/render"
)
// Debug related variables that can toggle on or off certain features and
// overlays within the game.
var (
/***************
* Visualizers *
***************/
// Background color to use when exporting a drawing Chunk as a bitmap image
// on disk. Default is white. Setting this to translucent yellow is a great
// way to visualize the chunks loaded from cache on your screen.
DebugChunkBitmapBackground = render.White // XXX: export $DEBUG_CHUNK_COLOR
)
func init() {
if color := os.Getenv("DEBUG_CHUNK_COLOR"); color != "" {
DebugChunkBitmapBackground = render.MustHexColor(color)
}
}

查看文件

@ -10,7 +10,7 @@ var (
CanvasScrollSpeed int32 = 8
// Default chunk size for canvases.
ChunkSize = 100
ChunkSize = 128
// Default size for a new Doodad.
DoodadSize = 100

8
level/base_test.go 普通文件
查看文件

@ -0,0 +1,8 @@
package level_test
import "github.com/kirsle/golog"
func init() {
log := golog.GetLogger("doodle")
log.Config.Level = golog.ErrorLevel
}

查看文件

@ -7,6 +7,7 @@ import (
"math"
"os"
"git.kirsle.net/apps/doodle/balance"
"git.kirsle.net/apps/doodle/render"
"golang.org/x/image/bmp"
)
@ -103,15 +104,15 @@ func (c *Chunk) ToBitmap(filename string) error {
// Blank out the pixels.
for x := 0; x < img.Bounds().Max.X; x++ {
for y := 0; y < img.Bounds().Max.Y; y++ {
img.Set(x, y, render.RGBA(255, 255, 0, 153).ToColor())
img.Set(x, y, balance.DebugChunkBitmapBackground.ToColor())
}
}
// Pixel coordinate offset to map the Chunk World Position to the
// smaller image boundaries.
pointOffset := render.Point{
X: int32(math.Abs(float64(c.Point.X * int32(c.Size)))),
Y: int32(math.Abs(float64(c.Point.Y * int32(c.Size)))),
X: int32(c.Point.X * int32(c.Size)),
Y: int32(c.Point.Y * int32(c.Size)),
}
// Blot all the pixels onto it.
@ -181,8 +182,6 @@ func (c *Chunk) Rect() render.Rect {
func (c *Chunk) SizePositive() render.Rect {
S := c.Rect()
return render.Rect{
X: c.Point.X * int32(c.Size),
Y: c.Point.Y * int32(c.Size),
W: int32(math.Abs(float64(S.X))) + S.W,
H: int32(math.Abs(float64(S.Y))) + S.H,
}

查看文件

@ -74,6 +74,7 @@ func (c *Chunker) IterViewportChunks(viewport render.Rect) <-chan render.Point {
pipe := make(chan render.Point)
go func() {
sent := make(map[render.Point]interface{})
for x := viewport.X; x < viewport.W; x += int32(c.Size / 4) {
for y := viewport.Y; y < viewport.H; y += int32(c.Size / 4) {
@ -94,27 +95,17 @@ func (c *Chunker) IterViewportChunks(viewport render.Rect) <-chan render.Point {
// Translate to a chunk coordinate, dedupe and send it.
coord := c.ChunkCoordinate(render.NewPoint(x, y))
// fmt.Printf("IterViewportChunks: x=%d y=%d chunk=%s\n", x, y, coord)
if _, ok := sent[coord]; ok {
continue
}
sent[coord] = nil
if _, ok := c.GetChunk(coord); ok {
fmt.Printf("Iter: send chunk %s for point %s\n", coord, point)
pipe <- coord
}
}
}
// for cx := topLeft.X; cx <= bottomRight.X; cx++ {
// for cy := topLeft.Y; cy <= bottomRight.Y; cy++ {
// pt := render.NewPoint(cx, cy)
// if _, ok := c.GetChunk(pt); ok {
// pipe <- pt
// }
// }
// }
close(pipe)
}()
return pipe

查看文件

@ -32,8 +32,6 @@ func (t *Texture) Size() render.Rect {
// NewBitmap initializes a texture from a bitmap image.
func (r *Renderer) NewBitmap(filename string) (render.Texturer, error) {
log.Debug("NewBitmap: open from file %s", filename)
surface, err := sdl.LoadBMP(filename)
if err != nil {
return nil, fmt.Errorf("NewBitmap: LoadBMP: %s", err)
@ -45,7 +43,6 @@ func (r *Renderer) NewBitmap(filename string) (render.Texturer, error) {
return nil, fmt.Errorf("NewBitmap: create texture: %s", err)
}
log.Debug("Created texture")
return &Texture{
width: surface.W,
height: surface.H,

查看文件

@ -107,14 +107,14 @@ func (w *Canvas) Loop(ev *events.State) error {
// Arrow keys to scroll the view.
scrollBy := render.Point{}
if ev.Right.Now {
scrollBy.X += balance.CanvasScrollSpeed
} else if ev.Left.Now {
scrollBy.X -= balance.CanvasScrollSpeed
} else if ev.Left.Now {
scrollBy.X += balance.CanvasScrollSpeed
}
if ev.Down.Now {
scrollBy.Y += balance.CanvasScrollSpeed
} else if ev.Up.Now {
scrollBy.Y -= balance.CanvasScrollSpeed
} else if ev.Up.Now {
scrollBy.Y += balance.CanvasScrollSpeed
}
if !scrollBy.IsZero() {
w.ScrollBy(scrollBy)
@ -136,8 +136,8 @@ func (w *Canvas) Loop(ev *events.State) error {
if ev.Button1.Now {
lastPixel := w.lastPixel
cursor := render.Point{
X: ev.CursorX.Now - P.X + w.Scroll.X,
Y: ev.CursorY.Now - P.Y + w.Scroll.Y,
X: ev.CursorX.Now - P.X - w.Scroll.X,
Y: ev.CursorY.Now - P.Y - w.Scroll.Y,
}
pixel := &level.Pixel{
X: cursor.X,
@ -184,10 +184,10 @@ func (w *Canvas) Loop(ev *events.State) error {
func (w *Canvas) Viewport() render.Rect {
var S = w.Size()
return render.Rect{
X: w.Scroll.X,
Y: w.Scroll.Y,
W: S.W - w.BoxThickness(2) + w.Scroll.X,
H: S.H - w.BoxThickness(2) + w.Scroll.Y,
X: -w.Scroll.X,
Y: -w.Scroll.Y,
W: S.W - w.Scroll.X,
H: S.H - w.Scroll.Y,
}
}
@ -258,13 +258,6 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
H: src.H, // - w.Scroll.Y,
}
// log.Warn(
// "chunk: %s src: %s dst: %s",
// coord,
// src,
// dst,
// )
// If the destination width will cause it to overflow the widget
// box, trim off the right edge of the destination rect.
//
@ -316,12 +309,6 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
src.Y += delta
}
// If the destination rect would overflow our widget bounds, trim
// it off.
// if w.Name == "edit-canvas" {
// log.Info("%s: copy %+v -> %+v", w.Name, src, dst)
// }
e.Copy(tex, src, dst)
}
}