From 71a4465c6fc8063cdf60e60ac2e6b695297fd40e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 19 Jan 2017 12:07:31 +0900 Subject: [PATCH] graphics: Improve matrices speed --- colorm.go | 2 +- geom.go | 2 +- internal/affine/affine.go | 10 ++++---- internal/affine/affine_js.go | 20 ++++++++++++---- internal/affine/affine_notjs.go | 41 ++++++++++++++++++++++----------- internal/affine/colorm.go | 29 ++++++++++++++--------- internal/affine/geom.go | 21 +++++++++++------ internal/graphics/program.go | 3 ++- 8 files changed, 84 insertions(+), 44 deletions(-) diff --git a/colorm.go b/colorm.go index 48c25180e..5c70f461f 100644 --- a/colorm.go +++ b/colorm.go @@ -72,7 +72,7 @@ func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float64, valueScale // Element returns a value of a matrix at (i, j). func (c *ColorM) Element(i, j int) float64 { - return c.impl.Element(i, j) + return c.impl.Elements()[i*affine.ColorMDim+j] } // SetElement sets an element at (i, j). diff --git a/geom.go b/geom.go index 63b7e59dc..f02329230 100644 --- a/geom.go +++ b/geom.go @@ -30,7 +30,7 @@ type GeoM struct { // Element returns a value of a matrix at (i, j). func (g *GeoM) Element(i, j int) float64 { - return g.impl.Element(i, j) + return g.impl.Elements()[i*affine.GeoMDim+j] } // Concat multiplies a geometry matrix with the other geometry matrix. diff --git a/internal/affine/affine.go b/internal/affine/affine.go index d9235dfbf..5881278a9 100644 --- a/internal/affine/affine.go +++ b/internal/affine/affine.go @@ -49,7 +49,7 @@ func uint64ToBytes(value uint64) []uint8 { type affine interface { dim() int - Element(i, j int) float64 + element(i, j int) float64 SetElement(i, j int, element float64) } @@ -62,7 +62,7 @@ func add(lhs, rhs, result affine) { for i := 0; i < dim-1; i++ { for j := 0; j < dim; j++ { - v := lhs.Element(i, j) + rhs.Element(i, j) + v := lhs.element(i, j) + rhs.element(i, j) result.SetElement(i, j, v) } } @@ -78,11 +78,11 @@ func mul(lhs, rhs, result affine) { for j := 0; j < dim; j++ { element := float64(0) for k := 0; k < dim-1; k++ { - element += lhs.Element(i, k) * - rhs.Element(k, j) + element += lhs.element(i, k) * + rhs.element(k, j) } if j == dim-1 { - element += lhs.Element(i, j) + element += lhs.element(i, j) } result.SetElement(i, j, element) } diff --git a/internal/affine/affine_js.go b/internal/affine/affine_js.go index f454b6e90..018938198 100644 --- a/internal/affine/affine_js.go +++ b/internal/affine/affine_js.go @@ -20,16 +20,26 @@ import ( "github.com/gopherjs/gopherjs/js" ) -func element(values string, dim int, i, j int) float64 { +func elements(values string, dim int) []float64 { if values == "" { - if i == j { - return 1 + result := make([]float64, dim*(dim-1)) + for i := 0; i < dim-1; i++ { + result[i*dim+i] = 1 } - return 0 + return result } a := js.NewArrayBuffer([]uint8(values)) + return js.Global.Get("Float64Array").New(a).Interface().([]float64) +} + +func setElements(values []float64, dim int) string { + a := js.NewArrayBuffer(make([]uint8, len(values)*8)) + a8 := js.Global.Get("Uint8Array").New(a) af64 := js.Global.Get("Float64Array").New(a) - return af64.Index(i*dim + j).Float() + for i, v := range values { + af64.SetIndex(i, v) + } + return string(a8.Interface().([]uint8)) } func setElement(values string, dim int, i, j int, value float64) string { diff --git a/internal/affine/affine_notjs.go b/internal/affine/affine_notjs.go index c958c1c9c..b41a5305b 100644 --- a/internal/affine/affine_notjs.go +++ b/internal/affine/affine_notjs.go @@ -22,27 +22,42 @@ import ( "github.com/hajimehoshi/ebiten/internal/endian" ) -func element(values string, dim int, i, j int) float64 { +func elements(values string, dim int) []float64 { + result := make([]float64, dim*(dim-1)) if values == "" { - if i == j { - return 1 + for i := 0; i < dim-1; i++ { + result[i*dim+i] = 1 } - return 0 + return result } - offset := 8 * (i*dim + j) - v := uint64(0) if endian.IsLittle() { - for k := 7; 0 <= k; k-- { - v <<= 8 - v += uint64(values[offset+k]) + for i := 0; i < len(values)/8; i++ { + v := uint64(0) + for k := 7; 0 <= k; k-- { + v <<= 8 + v += uint64(values[i*8+k]) + } + result[i] = math.Float64frombits(v) } } else { - for k := 0; k < 8; k++ { - v <<= 8 - v += uint64(values[offset+k]) + for i := 0; i < len(values)/8; i++ { + v := uint64(0) + for k := 0; k < 8; k++ { + v <<= 8 + v += uint64(values[i*8+k]) + } + result[i] = math.Float64frombits(v) } } - return math.Float64frombits(v) + return result +} + +func setElements(values []float64, dim int) string { + result := make([]uint8, len(values)*8) + for i, v := range values { + copy(result[i*8:(i+1)*8], uint64ToBytes(math.Float64bits(v))) + } + return string(result) } func setElement(values string, dim int, i, j int, value float64) string { diff --git a/internal/affine/colorm.go b/internal/affine/colorm.go index e6c9cc8f7..3187c8863 100644 --- a/internal/affine/colorm.go +++ b/internal/affine/colorm.go @@ -48,9 +48,12 @@ func (c *ColorM) dim() int { return ColorMDim } -// Element returns a value of a matrix at (i, j). -func (c *ColorM) Element(i, j int) float64 { - return element(c.values, ColorMDim, i, j) +func (c *ColorM) Elements() []float64 { + return elements(c.values, ColorMDim) +} + +func (c *ColorM) element(i, j int) float64 { + return c.Elements()[i*ColorMDim+j] } // SetElement sets an element at (i, j). @@ -85,20 +88,24 @@ func (c *ColorM) Add(other ColorM) { // Scale scales the matrix by (r, g, b, a). func (c *ColorM) Scale(r, g, b, a float64) { + v := elements(c.values, ColorMDim) for i := 0; i < ColorMDim; i++ { - c.SetElement(0, i, c.Element(0, i)*r) - c.SetElement(1, i, c.Element(1, i)*g) - c.SetElement(2, i, c.Element(2, i)*b) - c.SetElement(3, i, c.Element(3, i)*a) + v[i] *= r + v[i+ColorMDim] *= g + v[i+ColorMDim*2] *= b + v[i+ColorMDim*3] *= a } + c.values = setElements(v, ColorMDim) } // Translate translates the matrix by (r, g, b, a). func (c *ColorM) Translate(r, g, b, a float64) { - c.SetElement(0, 4, c.Element(0, 4)+r) - c.SetElement(1, 4, c.Element(1, 4)+g) - c.SetElement(2, 4, c.Element(2, 4)+b) - c.SetElement(3, 4, c.Element(3, 4)+a) + v := elements(c.values, ColorMDim) + v[4] += r + v[4+ColorMDim] += g + v[4+ColorMDim*2] += b + v[4+ColorMDim*3] += a + c.values = setElements(v, ColorMDim) } // RotateHue rotates the hue. diff --git a/internal/affine/geom.go b/internal/affine/geom.go index dffc48bf3..7099b4439 100644 --- a/internal/affine/geom.go +++ b/internal/affine/geom.go @@ -43,9 +43,12 @@ func (g *GeoM) dim() int { return GeoMDim } -// Element returns a value of a matrix at (i, j). -func (g *GeoM) Element(i, j int) float64 { - return element(g.values, GeoMDim, i, j) +func (g *GeoM) Elements() []float64 { + return elements(g.values, GeoMDim) +} + +func (g *GeoM) element(i, j int) float64 { + return g.Elements()[i*GeoMDim+j] } // SetElement sets an element at (i, j). @@ -70,16 +73,20 @@ func (g *GeoM) Add(other GeoM) { // Scale scales the matrix by (x, y). func (g *GeoM) Scale(x, y float64) { + v := elements(g.values, GeoMDim) for i := 0; i < GeoMDim; i++ { - g.SetElement(0, i, g.Element(0, i)*x) - g.SetElement(1, i, g.Element(1, i)*y) + v[i] *= x + v[i+GeoMDim] *= y } + g.values = setElements(v, GeoMDim) } // Translate translates the matrix by (x, y). func (g *GeoM) Translate(tx, ty float64) { - g.SetElement(0, 2, g.Element(0, 2)+tx) - g.SetElement(1, 2, g.Element(1, 2)+ty) + v := elements(g.values, GeoMDim) + v[2] += tx + v[2+GeoMDim] += ty + g.values = setElements(v, GeoMDim) } // Rotate rotates the matrix by theta. diff --git a/internal/graphics/program.go b/internal/graphics/program.go index dc246633a..7232a0b6c 100644 --- a/internal/graphics/program.go +++ b/internal/graphics/program.go @@ -231,9 +231,10 @@ func (p *programContext) begin() error { } e := [4][5]float32{} + es := p.colorM.Elements() for i := 0; i < 4; i++ { for j := 0; j < 5; j++ { - e[i][j] = float32(p.colorM.Element(i, j)) + e[i][j] = float32(es[i*affine.ColorMDim+j]) } }