restorable: Merge Clear to ReplacePixels

This can avoid unnecessary stale images that requires loading
pixels from GPU.
This commit is contained in:
Hajime Hoshi 2018-04-25 01:30:51 +09:00
parent 4900d74a4d
commit fb641d88cd
3 changed files with 92 additions and 15 deletions

View File

@ -89,18 +89,10 @@ func newImageWithoutInit(width, height int, volatile bool) *Image {
// The returned image is cleared. // The returned image is cleared.
func NewImage(width, height int, volatile bool) *Image { func NewImage(width, height int, volatile bool) *Image {
i := newImageWithoutInit(width, height, volatile) i := newImageWithoutInit(width, height, volatile)
i.Clear(0, 0, width, height) i.ReplacePixels(nil, 0, 0, width, height)
return i return i
} }
func (i *Image) Clear(x, y, width, height int) {
w, h := dummyImage.Size()
geom := (*affine.GeoM)(nil).Scale(float64(width)/float64(w), float64(height)/float64(h))
geom = geom.Translate(float64(x), float64(y))
colorm := (*affine.ColorM)(nil).Scale(0, 0, 0, 0)
i.DrawImage(dummyImage, 0, 0, w, h, geom, colorm, opengl.CompositeModeCopy, graphics.FilterNearest)
}
// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen. // NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
// //
// The returned image is cleared. // The returned image is cleared.
@ -112,7 +104,7 @@ func NewScreenFramebufferImage(width, height int) *Image {
} }
theImages.add(i) theImages.add(i)
runtime.SetFinalizer(i, (*Image).Dispose) runtime.SetFinalizer(i, (*Image).Dispose)
i.Clear(0, 0, width, height) i.ReplacePixels(nil, 0, 0, width, height)
return i return i
} }
@ -139,6 +131,8 @@ func (i *Image) makeStale() {
} }
// ReplacePixels replaces the image pixels with the given pixels slice. // ReplacePixels replaces the image pixels with the given pixels slice.
//
// If pixels is nil, ReplacePixels clears the specified reagion.
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
w, h := i.image.Size() w, h := i.image.Size()
if width <= 0 || height <= 0 { if width <= 0 || height <= 0 {
@ -152,7 +146,16 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
// For this purpuse, images should remember which part of that is used for DrawImage. // For this purpuse, images should remember which part of that is used for DrawImage.
theImages.makeStaleIfDependingOn(i) theImages.makeStaleIfDependingOn(i)
if pixels != nil {
i.image.ReplacePixels(pixels, x, y, width, height) i.image.ReplacePixels(pixels, x, y, width, height)
} else {
w, h := dummyImage.Size()
geom := (*affine.GeoM)(nil).Scale(float64(width)/float64(w), float64(height)/float64(h))
geom = geom.Translate(float64(x), float64(y))
colorm := (*affine.ColorM)(nil).Scale(0, 0, 0, 0)
vs := vertices(w, h, 0, 0, w, h, geom)
i.image.DrawImage(dummyImage.image, vs, colorm, opengl.CompositeModeCopy, graphics.FilterNearest)
}
if x == 0 && y == 0 && width == w && height == h { if x == 0 && y == 0 && width == w && height == h {
if i.basePixels == nil { if i.basePixels == nil {
@ -172,10 +175,18 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
return return
} }
idx := 4 * (y*w + x) idx := 4 * (y*w + x)
if pixels != nil {
for j := 0; j < height; j++ { for j := 0; j < height; j++ {
copy(i.basePixels[idx:idx+4*width], pixels[4*j*width:4*(j+1)*width]) copy(i.basePixels[idx:idx+4*width], pixels[4*j*width:4*(j+1)*width])
idx += 4 * w idx += 4 * w
} }
} else {
zeros := make([]byte, 4*width)
for j := 0; j < height; j++ {
copy(i.basePixels[idx:idx+4*width], zeros)
idx += 4 * w
}
}
i.stale = false i.stale = false
} }

View File

@ -569,4 +569,69 @@ func TestDoubleResolve(t *testing.T) {
} }
} }
func TestClear(t *testing.T) {
pix := make([]uint8, 4*4*4)
for i := range pix {
pix[i] = 0xff
}
img := NewImage(4, 4, false)
img.ReplacePixels(pix, 0, 0, 4, 4)
// This doesn't make the image stale. Its base pixels are available.
img.ReplacePixels(nil, 1, 1, 2, 2)
cases := []struct {
Index int
Want color.RGBA
}{
{
Index: 0,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Index: 3,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Index: 4,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Index: 5,
Want: color.RGBA{0, 0, 0, 0},
},
{
Index: 7,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Index: 8,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Index: 10,
Want: color.RGBA{0, 0, 0, 0},
},
{
Index: 11,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Index: 12,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
{
Index: 15,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff},
},
}
for _, c := range cases {
got := byteSliceToColor(img.BasePixelsForTesting(), c.Index)
want := c.Want
if got != want {
t.Errorf("base pixel [%d]: got %v, want %v", c.Index, got, want)
}
}
}
// TODO: How about volatile/screen images? // TODO: How about volatile/screen images?

View File

@ -189,7 +189,8 @@ func (i *Image) dispose() {
i.backend.page.Free(i.node) i.backend.page.Free(i.node)
if !i.backend.page.IsEmpty() { if !i.backend.page.IsEmpty() {
// As this part can be reused, this should be cleared explicitly. // As this part can be reused, this should be cleared explicitly.
i.backend.restorable.Clear(i.region()) x0, y0, x1, y1 := i.region()
i.backend.restorable.ReplacePixels(nil, x0, y0, x1, y1)
return return
} }