restorable: Bug fix: Image must be protected with locks (#567)

This commit is contained in:
Hajime Hoshi 2018-03-25 22:31:51 +09:00
parent e2632e358e
commit 125e5c4f32
2 changed files with 41 additions and 4 deletions

View File

@ -23,6 +23,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/affine" "github.com/hajimehoshi/ebiten/internal/affine"
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/sync"
) )
// drawImageHistoryItem is an item for history of draw-image commands. // drawImageHistoryItem is an item for history of draw-image commands.
@ -70,6 +71,8 @@ type Image struct {
// screen indicates whether the image is used as an actual screen. // screen indicates whether the image is used as an actual screen.
screen bool screen bool
m sync.Mutex
} }
// NewImage creates an empty image with the given size. // NewImage creates an empty image with the given size.
@ -131,6 +134,9 @@ 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)
i.m.Lock()
defer i.m.Unlock()
i.image.ReplacePixels(pixels, x, y, width, height) i.image.ReplacePixels(pixels, x, y, width, height)
if x == 0 && y == 0 && width == w && height == h { if x == 0 && y == 0 && width == w && height == h {
@ -160,11 +166,16 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
// DrawImage draws a given image img to the image. // DrawImage draws a given image img to the image.
func (i *Image) DrawImage(img *Image, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) { func (i *Image) DrawImage(img *Image, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
vs := img.vertices(sx0, sy0, sx1, sy1, geom) w, h := img.Size()
vs := vertices(w, h, sx0, sy0, sx1, sy1, geom)
if vs == nil { if vs == nil {
return return
} }
theImages.makeStaleIfDependingOn(i) theImages.makeStaleIfDependingOn(i)
i.m.Lock()
defer i.m.Unlock()
if img.stale || img.volatile || i.screen || !IsRestoringEnabled() { if img.stale || img.volatile || i.screen || !IsRestoringEnabled() {
i.makeStale() i.makeStale()
} else { } else {
@ -210,6 +221,10 @@ func (i *Image) At(x, y int) (color.RGBA, error) {
if x < 0 || y < 0 || w <= x || h <= y { if x < 0 || y < 0 || w <= x || h <= y {
return color.RGBA{}, nil return color.RGBA{}, nil
} }
i.m.Lock()
defer i.m.Unlock()
if err := graphics.FlushCommands(); err != nil { if err := graphics.FlushCommands(); err != nil {
return color.RGBA{}, err return color.RGBA{}, err
} }
@ -225,6 +240,9 @@ func (i *Image) At(x, y int) (color.RGBA, error) {
// makeStaleIfDependingOn makes the image stale if the image depends on target. // makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) { func (i *Image) makeStaleIfDependingOn(target *Image) {
i.m.Lock()
defer i.m.Unlock()
if i.stale { if i.stale {
return return
} }
@ -250,6 +268,10 @@ func (i *Image) resolveStale() error {
if !IsRestoringEnabled() { if !IsRestoringEnabled() {
return nil return nil
} }
i.m.Lock()
defer i.m.Unlock()
if i.volatile { if i.volatile {
return nil return nil
} }
@ -274,6 +296,9 @@ func (i *Image) dependsOn(target *Image) bool {
// dependingImages returns all images that is depended by the image. // dependingImages returns all images that is depended by the image.
func (i *Image) dependingImages() map[*Image]struct{} { func (i *Image) dependingImages() map[*Image]struct{} {
i.m.Lock()
defer i.m.Unlock()
r := map[*Image]struct{}{} r := map[*Image]struct{}{}
for _, c := range i.drawImageHistory { for _, c := range i.drawImageHistory {
r[c.image] = struct{}{} r[c.image] = struct{}{}
@ -283,6 +308,9 @@ func (i *Image) dependingImages() map[*Image]struct{} {
// hasDependency returns a boolean value indicating whether the image depends on another image. // hasDependency returns a boolean value indicating whether the image depends on another image.
func (i *Image) hasDependency() bool { func (i *Image) hasDependency() bool {
i.m.Lock()
defer i.m.Unlock()
if i.stale { if i.stale {
return false return false
} }
@ -291,6 +319,9 @@ func (i *Image) hasDependency() bool {
// Restore restores *graphics.Image from the pixels using its state. // Restore restores *graphics.Image from the pixels using its state.
func (i *Image) restore() error { func (i *Image) restore() error {
i.m.Lock()
defer i.m.Unlock()
w, h := i.image.Size() w, h := i.image.Size()
if i.screen { if i.screen {
// The screen image should also be recreated because framebuffer might // The screen image should also be recreated because framebuffer might
@ -348,6 +379,10 @@ func (i *Image) restore() error {
// After disposing, calling the function of the image causes unexpected results. // After disposing, calling the function of the image causes unexpected results.
func (i *Image) Dispose() { func (i *Image) Dispose() {
theImages.remove(i) theImages.remove(i)
i.m.Lock()
defer i.m.Unlock()
i.image.Dispose() i.image.Dispose()
i.image = nil i.image = nil
i.basePixels = nil i.basePixels = nil
@ -364,9 +399,12 @@ func (i *Image) IsInvalidated() (bool, error) {
if err := graphics.FlushCommands(); err != nil { if err := graphics.FlushCommands(); err != nil {
return false, err return false, err
} }
if !IsRestoringEnabled() { if !IsRestoringEnabled() {
return false, nil return false, nil
} }
i.m.Lock()
defer i.m.Unlock()
return i.image.IsInvalidated(), nil return i.image.IsInvalidated(), nil
} }

View File

@ -43,7 +43,7 @@ func (v *verticesBackend) get() []float32 {
return s return s
} }
func (i *Image) vertices(sx0, sy0, sx1, sy1 int, geo *affine.GeoM) []float32 { func vertices(width, height int, sx0, sy0, sx1, sy1 int, geo *affine.GeoM) []float32 {
if sx0 >= sx1 || sy0 >= sy1 { if sx0 >= sx1 || sy0 >= sy1 {
return nil return nil
} }
@ -58,7 +58,6 @@ func (i *Image) vertices(sx0, sy0, sx1, sy1 int, geo *affine.GeoM) []float32 {
// it really feels like we should be able to cache this computation // it really feels like we should be able to cache this computation
// but it may not matter. // but it may not matter.
width, height := i.Size()
w := 1 w := 1
h := 1 h := 1
for w < width { for w < width {