shareable: Replace At with Pixels

This change replaces the API At with Pixels to reduce mutex locks.

Updates #1137
This commit is contained in:
Hajime Hoshi 2020-04-18 20:01:40 +09:00
parent bbeb0d14e6
commit 3550abef7a
4 changed files with 109 additions and 67 deletions

View File

@ -142,17 +142,27 @@ func (i *Image) At(x, y int) (r, g, b, a byte, err error) {
panic("buffered: the command queue is not available yet at At")
}
if x < 0 || y < 0 || x >= i.width || y >= i.height {
return 0, 0, 0, 0, nil
}
// If there are pixels or pending fillling that needs to be resolved, use this rather than resolving.
// Resolving them needs to access GPU and is expensive (#1137).
if i.hasFill {
return i.fillColor.R, i.fillColor.G, i.fillColor.B, i.fillColor.A, nil
}
if i.pixels != nil {
if i.pixels == nil {
pix, err := i.img.Pixels(0, 0, i.width, i.height)
if err != nil {
return 0, 0, 0, 0, err
}
i.pixels = pix
}
idx := i.width*y + x
return i.pixels[4*idx], i.pixels[4*idx+1], i.pixels[4*idx+2], i.pixels[4*idx+3], nil
}
return i.img.At(x, y)
}
func (i *Image) Dump(name string, blackbg bool) error {
delayedCommandsM.Lock()
@ -209,23 +219,10 @@ func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error {
// TODO: Can we use (*restorable.Image).ReplacePixels?
if i.pixels == nil {
pix := make([]byte, 4*i.width*i.height)
idx := 0
img := i.img
sw, sh := i.width, i.height
for j := 0; j < sh; j++ {
for i := 0; i < sw; i++ {
r, g, b, a, err := img.At(i, j)
pix, err := i.img.Pixels(0, 0, i.width, i.height)
if err != nil {
return err
}
pix[4*idx] = r
pix[4*idx+1] = g
pix[4*idx+2] = b
pix[4*idx+3] = a
idx++
}
}
i.pixels = pix
}
for j := 0; j < height; j++ {

View File

@ -92,8 +92,8 @@ func (m *Mipmap) ReplacePixels(pix []byte) {
m.disposeMipmaps()
}
func (m *Mipmap) At(x, y int) (r, g, b, a byte, err error) {
return m.orig.At(x, y)
func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
return m.orig.Pixels(x, y, width, height)
}
func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom GeoM, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter) {

View File

@ -376,11 +376,26 @@ func (i *Image) replacePixels(p []byte) {
i.backend.restorable.ReplacePixels(p, x, y, w, h)
}
func (i *Image) At(x, y int) (byte, byte, byte, byte, error) {
func (img *Image) Pixels(x, y, width, height int) ([]byte, error) {
backendsM.Lock()
r, g, b, a, err := i.at(x, y)
backendsM.Unlock()
return r, g, b, a, err
defer backendsM.Unlock()
bs := make([]byte, 4*width*height)
idx := 0
for j := y; j < y+height; j++ {
for i := x; i < x+width; i++ {
r, g, b, a, err := img.at(i, j)
if err != nil {
return nil, err
}
bs[4*idx] = r
bs[4*idx+1] = g
bs[4*idx+2] = b
bs[4*idx+3] = a
idx++
}
}
return bs, nil
}
func (i *Image) at(x, y int) (byte, byte, byte, byte, error) {

View File

@ -95,12 +95,16 @@ func TestEnsureNotShared(t *testing.T) {
t.Errorf("got: %v, want: %v", got, want)
}
for j := 0; j < size; j++ {
for i := 0; i < size; i++ {
r, g, b, a, err := img4.At(i, j)
pix, err := img4.Pixels(0, 0, size, size)
if err != nil {
t.Fatal(err)
}
for j := 0; j < size; j++ {
for i := 0; i < size; i++ {
r := pix[4*(size*j+i)]
g := pix[4*(size*j+i)+1]
b := pix[4*(size*j+i)+2]
a := pix[4*(size*j+i)+3]
got := color.RGBA{r, g, b, a}
var want color.RGBA
if i < dx0 || dx1 <= i || j < dy0 || dy1 <= j {
@ -108,7 +112,7 @@ func TestEnsureNotShared(t *testing.T) {
want = color.RGBA{c, c, c, c}
}
if got != want {
t.Errorf("img4.At(%d, %d): got: %v, want: %v", i, j, got, want)
t.Errorf("at(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
@ -174,13 +178,17 @@ func TestReshared(t *testing.T) {
t.Fatal(err)
}
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)}
r, g, b, a, err := img1.At(i, j)
pix, err := img1.Pixels(0, 0, size, size)
if err != nil {
t.Fatal(err)
}
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)}
r := pix[4*(size*j+i)]
g := pix[4*(size*j+i)+1]
b := pix[4*(size*j+i)+2]
a := pix[4*(size*j+i)+3]
got := color.RGBA{r, g, b, a}
if got != want {
t.Errorf("got: %v, want: %v", got, want)
@ -193,13 +201,17 @@ func TestReshared(t *testing.T) {
t.Errorf("got: %v, want: %v", got, want)
}
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)}
r, g, b, a, err := img1.At(i, j)
pix, err = img1.Pixels(0, 0, size, size)
if err != nil {
t.Fatal(err)
}
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)}
r := pix[4*(size*j+i)]
g := pix[4*(size*j+i)+1]
b := pix[4*(size*j+i)+2]
a := pix[4*(size*j+i)+3]
got := color.RGBA{r, g, b, a}
if got != want {
t.Errorf("got: %v, want: %v", got, want)
@ -247,32 +259,40 @@ func TestExtend(t *testing.T) {
// Ensure to allocate
img1.ReplacePixels(p1)
for j := 0; j < h0; j++ {
for i := 0; i < w0; i++ {
r, g, b, a, err := img0.At(i, j)
pix0, err := img0.Pixels(0, 0, w0, h0)
if err != nil {
t.Fatal(err)
}
for j := 0; j < h0; j++ {
for i := 0; i < w0; i++ {
r := pix0[4*(w0*j+i)]
g := pix0[4*(w0*j+i)+1]
b := pix0[4*(w0*j+i)+2]
a := pix0[4*(w0*j+i)+3]
got := color.RGBA{r, g, b, a}
c := byte(i + w0*j)
want := color.RGBA{c, c, c, c}
if got != want {
t.Errorf("img0.At(%d, %d): got: %v, want: %v", i, j, got, want)
t.Errorf("at(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
for j := 0; j < h1; j++ {
for i := 0; i < w1; i++ {
r, g, b, a, err := img1.At(i, j)
pix1, err := img1.Pixels(0, 0, w1, h1)
if err != nil {
t.Fatal(err)
}
for j := 0; j < h1; j++ {
for i := 0; i < w1; i++ {
r := pix1[4*(w1*j+i)]
g := pix1[4*(w1*j+i)+1]
b := pix1[4*(w1*j+i)+2]
a := pix1[4*(w1*j+i)+3]
got := color.RGBA{r, g, b, a}
c := byte(i + w1*j)
want := color.RGBA{c, c, c, c}
if got != want {
t.Errorf("img1.At(%d, %d): got: %v, want: %v", i, j, got, want)
t.Errorf("at(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
@ -302,17 +322,21 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
dst.ReplacePixels(pix)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
r, g, b, a, err := dst.At(i, j)
pix, err := dst.Pixels(0, 0, w, h)
if err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
r := pix[4*(w*j+i)]
g := pix[4*(w*j+i)+1]
b := pix[4*(w*j+i)+2]
a := pix[4*(w*j+i)+3]
got := color.RGBA{r, g, b, a}
c := byte(i + w*j)
want := color.RGBA{c, c, c, c}
if got != want {
t.Errorf("dst.At(%d, %d): got %v, want: %v", i, j, got, want)
t.Errorf("at(%d, %d): got %v, want: %v", i, j, got, want)
}
}
}
@ -339,17 +363,19 @@ func TestSmallImages(t *testing.T) {
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
r, _, _, a, err := dst.At(i, j)
pix, err := dst.Pixels(0, 0, w, h)
if err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
r := pix[4*(w*j+i)]
a := pix[4*(w*j+i)+3]
if got, want := r, byte(0xff); got != want {
t.Errorf("At(%d, %d) red: got: %d, want: %d", i, j, got, want)
t.Errorf("at(%d, %d) red: got: %d, want: %d", i, j, got, want)
}
if got, want := a, byte(0xff); got != want {
t.Errorf("At(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
t.Errorf("at(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
}
}
}
@ -360,7 +386,9 @@ func TestLongImages(t *testing.T) {
const w, h = 1, 6
src := NewImage(w, h, false)
defer src.MarkDisposed()
dst := NewImage(256, 256, false)
const dstW, dstH = 256, 256
dst := NewImage(dstW, dstH, false)
defer dst.MarkDisposed()
pix := make([]byte, 4*w*h)
@ -377,17 +405,19 @@ func TestLongImages(t *testing.T) {
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
for j := 0; j < h; j++ {
for i := 0; i < w*scale; i++ {
r, _, _, a, err := dst.At(i, j)
pix, err := dst.Pixels(0, 0, dstW, dstH)
if err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
for i := 0; i < w*scale; i++ {
r := pix[4*(dstW*j+i)]
a := pix[4*(dstW*j+i)+3]
if got, want := r, byte(0xff); got != want {
t.Errorf("At(%d, %d) red: got: %d, want: %d", i, j, got, want)
t.Errorf("at(%d, %d) red: got: %d, want: %d", i, j, got, want)
}
if got, want := a, byte(0xff); got != want {
t.Errorf("At(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
t.Errorf("at(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
}
}
}