From 107dfe50743db9da8c7d1e032afdae7042b2d80d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 14 Jan 2019 04:25:39 +0900 Subject: [PATCH] graphics: Improve Set speed On GopherJS, copying a struct is very heavy. This change avoids copying (color) structs when possible. --- image.go | 13 +++++++------ internal/restorable/image.go | 10 ++++------ internal/restorable/images.go | 10 +++++----- internal/restorable/images_test.go | 12 ++++++++---- internal/shareable/shareable.go | 19 +++++++++---------- internal/shareable/shareable_test.go | 18 ++++++++++++------ 6 files changed, 45 insertions(+), 37 deletions(-) diff --git a/image.go b/image.go index 0948200d5..4260b7fde 100644 --- a/image.go +++ b/image.go @@ -542,7 +542,8 @@ func (i *Image) At(x, y int) color.Color { return color.RGBA{} } i.resolvePixelsToSet(true) - return i.mipmap.original().At(x, y) + r, g, b, a := i.mipmap.original().At(x, y) + return color.RGBA{r, g, b, a} } // Set sets the color at (x, y). @@ -570,11 +571,11 @@ func (img *Image) Set(x, y int, clr color.Color) { idx := 0 for j := 0; j < h; j++ { for i := 0; i < w; i++ { - c := img.mipmap.original().At(i, j) - pix[4*idx] = c.R - pix[4*idx+1] = c.G - pix[4*idx+2] = c.B - pix[4*idx+3] = c.A + r, g, b, a := img.mipmap.original().At(i, j) + pix[4*idx] = r + pix[4*idx+1] = g + pix[4*idx+2] = b + pix[4*idx+3] = a idx++ } } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 9c64a6faf..b0da2a449 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -17,7 +17,6 @@ package restorable import ( "errors" "fmt" - "image/color" "github.com/hajimehoshi/ebiten/internal/affine" "github.com/hajimehoshi/ebiten/internal/graphics" @@ -278,22 +277,21 @@ func (i *Image) readPixelsFromGPUIfNeeded() { // At returns a color value at (x, y). // // Note that this must not be called until context is available. -func (i *Image) At(x, y int) color.RGBA { +func (i *Image) At(x, y int) (byte, byte, byte, byte) { w, h := i.image.Size() if x < 0 || y < 0 || w <= x || h <= y { - return color.RGBA{} + return 0, 0, 0, 0 } i.readPixelsFromGPUIfNeeded() // Even after readPixelsFromGPU, basePixels might be nil when OpenGL error happens. if i.basePixels == nil { - return color.RGBA{} + return 0, 0, 0, 0 } idx := 4*x + 4*y*w - r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3] - return color.RGBA{r, g, b, a} + return i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3] } // makeStaleIfDependingOn makes the image stale if the image depends on target. diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 84d46b1cd..113a3df36 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -85,11 +85,11 @@ func Images() []image.Image { pix := make([]byte, 4*w*h) for j := 0; j < h; j++ { for i := 0; i < w; i++ { - c := img.At(i, j) - pix[4*(i+j*w)] = byte(c.R) - pix[4*(i+j*w)+1] = byte(c.G) - pix[4*(i+j*w)+2] = byte(c.B) - pix[4*(i+j*w)+3] = byte(c.A) + r, g, b, a := img.At(i, j) + pix[4*(i+j*w)] = r + pix[4*(i+j*w)+1] = g + pix[4*(i+j*w)+2] = b + pix[4*(i+j*w)+3] = a } } imgs = append(imgs, &image.RGBA{ diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index 985c1a04b..3133d897a 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -458,7 +458,8 @@ func TestReplacePixels(t *testing.T) { // Check the region (5, 7)-(9, 11). Outside state is indeterministic. for j := 7; j < 11; j++ { for i := 5; i < 9; i++ { - got := img.At(i, j) + r, g, b, a := img.At(i, j) + got := color.RGBA{r, g, b, a} want := color.RGBA{0xff, 0xff, 0xff, 0xff} if got != want { t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want) @@ -471,7 +472,8 @@ func TestReplacePixels(t *testing.T) { } for j := 7; j < 11; j++ { for i := 5; i < 9; i++ { - got := img.At(i, j) + r, g, b, a := img.At(i, j) + got := color.RGBA{r, g, b, a} want := color.RGBA{0xff, 0xff, 0xff, 0xff} if got != want { t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want) @@ -500,7 +502,8 @@ func TestDrawImageAndReplacePixels(t *testing.T) { if err := Restore(); err != nil { t.Fatal(err) } - got := img1.At(0, 0) + r, g, b, a := img1.At(0, 0) + got := color.RGBA{r, g, b, a} want := color.RGBA{0xff, 0xff, 0xff, 0xff} if !sameColors(got, want, 1) { t.Errorf("got: %v, want: %v", got, want) @@ -533,7 +536,8 @@ func TestDispose(t *testing.T) { if err := Restore(); err != nil { t.Fatal(err) } - got := img0.At(0, 0) + r, g, b, a := img0.At(0, 0) + got := color.RGBA{r, g, b, a} want := color.RGBA{0xff, 0xff, 0xff, 0xff} if !sameColors(got, want, 1) { t.Errorf("got: %v, want: %v", got, want) diff --git a/internal/shareable/shareable.go b/internal/shareable/shareable.go index eb66a3c32..a441f2265 100644 --- a/internal/shareable/shareable.go +++ b/internal/shareable/shareable.go @@ -16,7 +16,6 @@ package shareable import ( "image" - "image/color" "runtime" "sync" @@ -162,11 +161,11 @@ func (i *Image) forceShared() { pixels := make([]byte, 4*i.width*i.height) for y := 0; y < i.height; y++ { for x := 0; x < i.width; x++ { - c := i.at(x, y) - pixels[4*(x+i.width*y)] = c.R - pixels[4*(x+i.width*y)+1] = c.G - pixels[4*(x+i.width*y)+2] = c.B - pixels[4*(x+i.width*y)+3] = c.A + r, g, b, a := i.at(x, y) + pixels[4*(x+i.width*y)] = r + pixels[4*(x+i.width*y)+1] = g + pixels[4*(x+i.width*y)+2] = b + pixels[4*(x+i.width*y)+3] = a } } newI.replacePixels(pixels) @@ -315,20 +314,20 @@ func (i *Image) replacePixels(p []byte) { i.backend.restorable.ReplacePixels(p, x, y, w, h) } -func (i *Image) At(x, y int) color.RGBA { +func (i *Image) At(x, y int) (byte, byte, byte, byte) { backendsM.Lock() defer backendsM.Unlock() return i.at(x, y) } -func (i *Image) at(x, y int) color.RGBA { +func (i *Image) at(x, y int) (byte, byte, byte, byte) { if i.backend == nil { - return color.RGBA{} + return 0, 0, 0, 0 } ox, oy, w, h := i.region() if x < 0 || y < 0 || x >= w || y >= h { - return color.RGBA{} + return 0, 0, 0, 0 } return i.backend.restorable.At(x+ox, y+oy) diff --git a/internal/shareable/shareable_test.go b/internal/shareable/shareable_test.go index 5060a9150..d9ed4a7bb 100644 --- a/internal/shareable/shareable_test.go +++ b/internal/shareable/shareable_test.go @@ -94,7 +94,8 @@ func TestEnsureNotShared(t *testing.T) { for j := 0; j < size; j++ { for i := 0; i < size; i++ { - got := img4.At(i, j) + r, g, b, a := img4.At(i, j) + got := color.RGBA{r, g, b, a} var want color.RGBA if i < dx0 || dx1 <= i || j < dy0 || dy1 <= j { c := byte(i + j) @@ -168,7 +169,8 @@ func Disabled_TestReshared(t *testing.T) { for j := 0; j < size; j++ { for i := 0; i < size; i++ { want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)} - got := img1.At(i, j) + r, g, b, a := img1.At(i, j) + got := color.RGBA{r, g, b, a} if got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -184,7 +186,8 @@ func Disabled_TestReshared(t *testing.T) { for j := 0; j < size; j++ { for i := 0; i < size; i++ { want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)} - got := img1.At(i, j) + r, g, b, a := img1.At(i, j) + got := color.RGBA{r, g, b, a} if got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -231,7 +234,8 @@ func TestExtend(t *testing.T) { for j := 0; j < h0; j++ { for i := 0; i < w0; i++ { - got := img0.At(i, j) + r, g, b, a := img0.At(i, j) + got := color.RGBA{r, g, b, a} c := byte(i + w0*j) want := color.RGBA{c, c, c, c} if got != want { @@ -242,7 +246,8 @@ func TestExtend(t *testing.T) { for j := 0; j < h1; j++ { for i := 0; i < w1; i++ { - got := img1.At(i, j) + r, g, b, a := img1.At(i, j) + got := color.RGBA{r, g, b, a} c := byte(i + w1*j) want := color.RGBA{c, c, c, c} if got != want { @@ -278,7 +283,8 @@ func TestReplacePixelsAfterDrawImage(t *testing.T) { for j := 0; j < h; j++ { for i := 0; i < w; i++ { - got := dst.At(i, j) + r, g, b, a := dst.At(i, j) + got := color.RGBA{r, g, b, a} c := byte(i + w*j) want := color.RGBA{c, c, c, c} if got != want {