graphics: Speed up DrawTriangles (#723)

DrawTriangles is expensive and slow because of massive memory
allocation and garbage collection costs. This patch moves from ~47TPS
on my laptop (with ~24k triangles) to 60TPS. The first part
is just allocating the right size of vertex buffer up front; that
got to about 55TPS. The second part replaces the frequent
allocations of []float32 in Vertex() calls with writing the
desired values into a provided destination slice.

Time spent in drawing triangles for 1,000 frames:
	13.07s	baseline
	11.09s	preallocate whole buffer to avoid resizing
	6.13s	use new PutVertex function

This might need some cleanup, but I think it's good evidence that
the design change is viable.
This commit is contained in:
seebs 2018-10-30 20:53:17 -05:00 committed by Hajime Hoshi
parent 45017213a7
commit 74e204d952
3 changed files with 6 additions and 9 deletions

View File

@ -519,10 +519,10 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
filter = graphics.Filter(img.filter) filter = graphics.Filter(img.filter)
} }
vs := []float32{} vs := make([]float32, len(vertices)*10)
src := img.mipmap.original() src := img.mipmap.original()
for _, v := range vertices { for idx, v := range vertices {
vs = append(vs, src.Vertex(float32(v.DstX), float32(v.DstY), v.SrcX, v.SrcY, v.ColorR, v.ColorG, v.ColorB, v.ColorA)...) src.PutVertex(vs[idx*10:idx*10+10], float32(v.DstX), float32(v.DstY), v.SrcX, v.SrcY, v.ColorR, v.ColorG, v.ColorB, v.ColorA)
} }
i.mipmap.original().DrawImage(img.mipmap.original(), vs, indices, options.ColorM.impl, mode, filter) i.mipmap.original().DrawImage(img.mipmap.original(), vs, indices, options.ColorM.impl, mode, filter)
i.disposeMipmaps() i.disposeMipmaps()

View File

@ -140,7 +140,7 @@ func QuadIndices() []uint16 {
return quadIndices return quadIndices
} }
func Vertex(width, height int, dx, dy, sx, sy float32, cr, cg, cb, ca float32) []float32 { func PutVertex(vs []float32, width, height int, dx, dy, sx, sy float32, cr, cg, cb, ca float32) {
if !isPowerOf2(width) { if !isPowerOf2(width) {
panic("not reached") panic("not reached")
} }
@ -155,7 +155,6 @@ func Vertex(width, height int, dx, dy, sx, sy float32, cr, cg, cb, ca float32) [
// //
// NaN would make more sense to represent an invalid state, but vertices including NaN values doesn't work on // NaN would make more sense to represent an invalid state, but vertices including NaN values doesn't work on
// some machines (#696). Let's use negative numbers to represent such state. // some machines (#696). Let's use negative numbers to represent such state.
vs := theVerticesBackend.slice(1)[0:10]
vs[0] = dx vs[0] = dx
vs[1] = dy vs[1] = dy
vs[2] = sx / wf vs[2] = sx / wf
@ -166,6 +165,4 @@ func Vertex(width, height int, dx, dy, sx, sy float32, cr, cg, cb, ca float32) [
vs[7] = cg vs[7] = cg
vs[8] = cb vs[8] = cb
vs[9] = ca vs[9] = ca
return vs
} }

View File

@ -191,13 +191,13 @@ func (i *Image) QuadVertices(sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32,
return graphics.QuadVertices(w, h, sx0+ox, sy0+oy, sx1+ox, sy1+oy, a, b, c, d, tx, ty, cr, cg, cb, ca) return graphics.QuadVertices(w, h, sx0+ox, sy0+oy, sx1+ox, sy1+oy, a, b, c, d, tx, ty, cr, cg, cb, ca)
} }
func (i *Image) Vertex(dx, dy, sx, sy float32, cr, cg, cb, ca float32) []float32 { func (i *Image) PutVertex(dest []float32, dx, dy, sx, sy float32, cr, cg, cb, ca float32) {
if i.backend == nil { if i.backend == nil {
i.allocate(true) i.allocate(true)
} }
ox, oy, _, _ := i.region() ox, oy, _, _ := i.region()
w, h := i.backend.restorable.SizePowerOf2() w, h := i.backend.restorable.SizePowerOf2()
return graphics.Vertex(w, h, dx, dy, sx+float32(ox), sy+float32(oy), cr, cg, cb, ca) graphics.PutVertex(dest, w, h, dx, dy, sx+float32(ox), sy+float32(oy), cr, cg, cb, ca)
} }
const MaxCountForShare = 10 const MaxCountForShare = 10