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.
This commit is contained in:
parent
279a980106
commit
97394f6cdb
26
balance/debug.go
Normal file
26
balance/debug.go
Normal file
|
@ -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
|
CanvasScrollSpeed int32 = 8
|
||||||
|
|
||||||
// Default chunk size for canvases.
|
// Default chunk size for canvases.
|
||||||
ChunkSize = 100
|
ChunkSize = 128
|
||||||
|
|
||||||
// Default size for a new Doodad.
|
// Default size for a new Doodad.
|
||||||
DoodadSize = 100
|
DoodadSize = 100
|
||||||
|
|
8
level/base_test.go
Normal file
8
level/base_test.go
Normal file
|
@ -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"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"git.kirsle.net/apps/doodle/balance"
|
||||||
"git.kirsle.net/apps/doodle/render"
|
"git.kirsle.net/apps/doodle/render"
|
||||||
"golang.org/x/image/bmp"
|
"golang.org/x/image/bmp"
|
||||||
)
|
)
|
||||||
|
@ -103,15 +104,15 @@ func (c *Chunk) ToBitmap(filename string) error {
|
||||||
// Blank out the pixels.
|
// Blank out the pixels.
|
||||||
for x := 0; x < img.Bounds().Max.X; x++ {
|
for x := 0; x < img.Bounds().Max.X; x++ {
|
||||||
for y := 0; y < img.Bounds().Max.Y; y++ {
|
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
|
// Pixel coordinate offset to map the Chunk World Position to the
|
||||||
// smaller image boundaries.
|
// smaller image boundaries.
|
||||||
pointOffset := render.Point{
|
pointOffset := render.Point{
|
||||||
X: int32(math.Abs(float64(c.Point.X * int32(c.Size)))),
|
X: int32(c.Point.X * int32(c.Size)),
|
||||||
Y: int32(math.Abs(float64(c.Point.Y * int32(c.Size)))),
|
Y: int32(c.Point.Y * int32(c.Size)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blot all the pixels onto it.
|
// Blot all the pixels onto it.
|
||||||
|
@ -181,8 +182,6 @@ func (c *Chunk) Rect() render.Rect {
|
||||||
func (c *Chunk) SizePositive() render.Rect {
|
func (c *Chunk) SizePositive() render.Rect {
|
||||||
S := c.Rect()
|
S := c.Rect()
|
||||||
return render.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,
|
W: int32(math.Abs(float64(S.X))) + S.W,
|
||||||
H: int32(math.Abs(float64(S.Y))) + S.H,
|
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)
|
pipe := make(chan render.Point)
|
||||||
go func() {
|
go func() {
|
||||||
sent := make(map[render.Point]interface{})
|
sent := make(map[render.Point]interface{})
|
||||||
|
|
||||||
for x := viewport.X; x < viewport.W; x += int32(c.Size / 4) {
|
for x := viewport.X; x < viewport.W; x += int32(c.Size / 4) {
|
||||||
for y := viewport.Y; y < viewport.H; y += 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.
|
// Translate to a chunk coordinate, dedupe and send it.
|
||||||
coord := c.ChunkCoordinate(render.NewPoint(x, y))
|
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 {
|
if _, ok := sent[coord]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sent[coord] = nil
|
sent[coord] = nil
|
||||||
|
|
||||||
if _, ok := c.GetChunk(coord); ok {
|
if _, ok := c.GetChunk(coord); ok {
|
||||||
fmt.Printf("Iter: send chunk %s for point %s\n", coord, point)
|
|
||||||
pipe <- coord
|
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)
|
close(pipe)
|
||||||
}()
|
}()
|
||||||
return pipe
|
return pipe
|
||||||
|
|
|
@ -32,8 +32,6 @@ func (t *Texture) Size() render.Rect {
|
||||||
|
|
||||||
// NewBitmap initializes a texture from a bitmap image.
|
// NewBitmap initializes a texture from a bitmap image.
|
||||||
func (r *Renderer) NewBitmap(filename string) (render.Texturer, error) {
|
func (r *Renderer) NewBitmap(filename string) (render.Texturer, error) {
|
||||||
log.Debug("NewBitmap: open from file %s", filename)
|
|
||||||
|
|
||||||
surface, err := sdl.LoadBMP(filename)
|
surface, err := sdl.LoadBMP(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("NewBitmap: LoadBMP: %s", err)
|
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)
|
return nil, fmt.Errorf("NewBitmap: create texture: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("Created texture")
|
|
||||||
return &Texture{
|
return &Texture{
|
||||||
width: surface.W,
|
width: surface.W,
|
||||||
height: surface.H,
|
height: surface.H,
|
||||||
|
|
|
@ -107,14 +107,14 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
// Arrow keys to scroll the view.
|
// Arrow keys to scroll the view.
|
||||||
scrollBy := render.Point{}
|
scrollBy := render.Point{}
|
||||||
if ev.Right.Now {
|
if ev.Right.Now {
|
||||||
scrollBy.X += balance.CanvasScrollSpeed
|
|
||||||
} else if ev.Left.Now {
|
|
||||||
scrollBy.X -= balance.CanvasScrollSpeed
|
scrollBy.X -= balance.CanvasScrollSpeed
|
||||||
|
} else if ev.Left.Now {
|
||||||
|
scrollBy.X += balance.CanvasScrollSpeed
|
||||||
}
|
}
|
||||||
if ev.Down.Now {
|
if ev.Down.Now {
|
||||||
scrollBy.Y += balance.CanvasScrollSpeed
|
|
||||||
} else if ev.Up.Now {
|
|
||||||
scrollBy.Y -= balance.CanvasScrollSpeed
|
scrollBy.Y -= balance.CanvasScrollSpeed
|
||||||
|
} else if ev.Up.Now {
|
||||||
|
scrollBy.Y += balance.CanvasScrollSpeed
|
||||||
}
|
}
|
||||||
if !scrollBy.IsZero() {
|
if !scrollBy.IsZero() {
|
||||||
w.ScrollBy(scrollBy)
|
w.ScrollBy(scrollBy)
|
||||||
|
@ -136,8 +136,8 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
if ev.Button1.Now {
|
if ev.Button1.Now {
|
||||||
lastPixel := w.lastPixel
|
lastPixel := w.lastPixel
|
||||||
cursor := render.Point{
|
cursor := render.Point{
|
||||||
X: ev.CursorX.Now - P.X + w.Scroll.X,
|
X: ev.CursorX.Now - P.X - w.Scroll.X,
|
||||||
Y: ev.CursorY.Now - P.Y + w.Scroll.Y,
|
Y: ev.CursorY.Now - P.Y - w.Scroll.Y,
|
||||||
}
|
}
|
||||||
pixel := &level.Pixel{
|
pixel := &level.Pixel{
|
||||||
X: cursor.X,
|
X: cursor.X,
|
||||||
|
@ -184,10 +184,10 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
func (w *Canvas) Viewport() render.Rect {
|
func (w *Canvas) Viewport() render.Rect {
|
||||||
var S = w.Size()
|
var S = w.Size()
|
||||||
return render.Rect{
|
return render.Rect{
|
||||||
X: w.Scroll.X,
|
X: -w.Scroll.X,
|
||||||
Y: w.Scroll.Y,
|
Y: -w.Scroll.Y,
|
||||||
W: S.W - w.BoxThickness(2) + w.Scroll.X,
|
W: S.W - w.Scroll.X,
|
||||||
H: S.H - w.BoxThickness(2) + w.Scroll.Y,
|
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,
|
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
|
// If the destination width will cause it to overflow the widget
|
||||||
// box, trim off the right edge of the destination rect.
|
// 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
|
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)
|
e.Copy(tex, src, dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user