shareable: Use the shared backend again in some conditions

Fixes #633
This commit is contained in:
Hajime Hoshi 2018-07-12 01:40:24 +09:00
parent 2bba43d6db
commit a6edb90898
2 changed files with 137 additions and 2 deletions

View File

@ -91,13 +91,20 @@ type Image struct {
backend *backend
node *packing.Node
node *packing.Node
sharedCount int
}
func (i *Image) isShared() bool {
return i.node != nil
}
func (i *Image) IsSharedForTesting() bool {
backendsM.Lock()
defer backendsM.Unlock()
return i.isShared()
}
func (i *Image) ensureNotShared() {
if i.backend == nil {
i.allocate(false)
@ -121,6 +128,36 @@ func (i *Image) ensureNotShared() {
}
}
func (i *Image) forceShared() {
if i.backend == nil {
i.allocate(false)
return
}
if i.isShared() {
return
}
if !i.shareable() {
panic("not reached")
}
newI := NewImage(i.width, i.height)
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).(color.RGBA)
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
}
}
newI.replacePixels(pixels)
i.dispose(false)
*i = *newI
}
func (i *Image) region() (x, y, width, height int) {
if i.backend == nil {
panic("not reached")
@ -145,6 +182,8 @@ func (i *Image) QuadVertices(sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32)
return graphicsutil.QuadVertices(w, h, sx0+dx, sy0+dy, sx1+dx, sy1+dy, a, b, c, d, tx, ty)
}
const ReshareCount = 10
func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
backendsM.Lock()
defer backendsM.Unlock()
@ -168,12 +207,24 @@ func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colo
}
i.backend.restorable.DrawImage(img.backend.restorable, vertices, indices, colorm, mode, filter)
i.sharedCount = 0
if !img.isShared() && img.shareable() {
img.sharedCount++
if img.sharedCount >= ReshareCount {
img.forceShared()
img.sharedCount = 0
}
}
}
func (i *Image) ReplacePixels(p []byte) {
backendsM.Lock()
defer backendsM.Unlock()
i.replacePixels(p)
}
func (i *Image) replacePixels(p []byte) {
if i.disposed {
panic("shareable: the image must not be disposed")
}
@ -191,7 +242,10 @@ func (i *Image) ReplacePixels(p []byte) {
func (i *Image) At(x, y int) color.Color {
backendsM.Lock()
defer backendsM.Unlock()
return i.at(x, y)
}
func (i *Image) at(x, y int) color.Color {
if i.backend == nil {
return color.RGBA{}
}
@ -271,12 +325,16 @@ func NewImage(width, height int) *Image {
}
}
func (i *Image) shareable() bool {
return i.width <= maxSize && i.height <= maxSize
}
func (i *Image) allocate(shareable bool) {
if i.backend != nil {
panic("not reached")
}
if !shareable || i.width > maxSize || i.height > maxSize {
if !shareable || !i.shareable() {
i.backend = &backend{
restorable: restorable.NewImage(i.width, i.height, false),
}

View File

@ -88,6 +88,10 @@ func TestEnsureNotShared(t *testing.T) {
vs := img3.QuadVertices(0, 0, size/2, size/2, 1, 0, 0, 1, size/4, size/4)
is := graphicsutil.QuadIndices()
img4.DrawImage(img3, vs, is, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
want := false
if got := img4.IsSharedForTesting(); got != want {
t.Errorf("got: %v, want: %v", got, want)
}
for j := 0; j < size; j++ {
for i := 0; i < size; i++ {
@ -107,3 +111,76 @@ func TestEnsureNotShared(t *testing.T) {
// This bug was fixed by 03dcd948.
img4.DrawImage(img3, vs, is, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
}
func TestReshared(t *testing.T) {
const size = 16
img0 := NewImage(size, size)
defer img0.Dispose()
img0.ReplacePixels(make([]byte, 4*size*size))
img1 := NewImage(size, size)
defer img1.Dispose()
img1.ReplacePixels(make([]byte, 4*size*size))
want := true
if got := img1.IsSharedForTesting(); got != want {
t.Errorf("got: %v, want: %v", got, want)
}
img2 := NewImage(size, size)
defer img2.Dispose()
pix := make([]byte, 4*size*size)
for j := 0; j < size; j++ {
for i := 0; i < size; i++ {
pix[4*(i+j*size)] = byte(i + j)
pix[4*(i+j*size)+1] = byte(i + j)
pix[4*(i+j*size)+2] = byte(i + j)
pix[4*(i+j*size)+3] = byte(i + j)
}
}
img2.ReplacePixels(pix)
// Use img1 as a render target.
vs := img2.QuadVertices(0, 0, size, size, 1, 0, 0, 1, 0, 0)
is := graphicsutil.QuadIndices()
img1.DrawImage(img2, vs, is, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
want = false
if got := img1.IsSharedForTesting(); got != want {
t.Errorf("got: %v, want: %v", got, want)
}
// Use img1 as a render source.
for i := 0; i < ReshareCount-1; i++ {
img0.DrawImage(img1, vs, is, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
want := false
if got := img1.IsSharedForTesting(); got != want {
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)}
got := img1.At(i, j).(color.RGBA)
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
}
}
img0.DrawImage(img1, vs, is, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
want = true
if got := img1.IsSharedForTesting(); got != want {
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)}
got := img1.At(i, j).(color.RGBA)
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
}
}
}