restorable: Bug fix: Dispose all the image before start restoring

A current texture ID and a new texture ID can be duplicated.
Disposing part of textures and creating textures at the same time
can make contradicted situation.
This commit is contained in:
Hajime Hoshi 2019-08-16 00:36:54 +09:00
parent 2cb1ea6fb4
commit 6ba6cdc721
2 changed files with 19 additions and 11 deletions

View File

@ -472,24 +472,20 @@ func (i *Image) hasDependency() bool {
} }
// Restore restores *graphicscommand.Image from the pixels using its state. // Restore restores *graphicscommand.Image from the pixels using its state.
func (i *Image) restore() { func (i *Image) restore(width, height int) {
w, h := i.Size() // Do not dispose the image here. The image should be already disposed.
// Dispose the internal image after getting its size for safety.
i.image.Dispose()
i.image = nil
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
// be changed. // be changed.
i.image = graphicscommand.NewScreenFramebufferImage(w, h) i.image = graphicscommand.NewScreenFramebufferImage(width, height)
i.basePixels = Pixels{} i.basePixels = Pixels{}
i.drawTrianglesHistory = nil i.drawTrianglesHistory = nil
i.stale = false i.stale = false
return return
} }
if i.volatile { if i.volatile {
i.image = graphicscommand.NewImage(w, h) i.image = graphicscommand.NewImage(width, height)
i.clear() i.clear()
return return
} }
@ -497,7 +493,7 @@ func (i *Image) restore() {
panic("restorable: pixels must not be stale when restoring") panic("restorable: pixels must not be stale when restoring")
} }
gimg := graphicscommand.NewImage(w, h) gimg := graphicscommand.NewImage(width, height)
// Clear the image explicitly. // Clear the image explicitly.
if i != emptyImage { if i != emptyImage {
// As clearImage uses emptyImage, clearImage cannot be called on emptyImage. // As clearImage uses emptyImage, clearImage cannot be called on emptyImage.
@ -515,7 +511,7 @@ func (i *Image) restore() {
if len(i.drawTrianglesHistory) > 0 { if len(i.drawTrianglesHistory) > 0 {
i.basePixels = Pixels{} i.basePixels = Pixels{}
i.basePixels.AddOrReplace(gimg.Pixels(), 0, 0, w, h) i.basePixels.AddOrReplace(gimg.Pixels(), 0, 0, width, height)
} }
i.image = gimg i.image = gimg

View File

@ -152,6 +152,17 @@ func (i *images) restore() {
panic("restorable: restore cannot be called when restoring is disabled") panic("restorable: restore cannot be called when restoring is disabled")
} }
// Dispose all the images ahead of restoring. A current texture ID and a new texture ID can be duplicated.
// TODO: Write a test to confirm that ID duplication never happens.
sizes := map[*Image]struct{ w, h int }{}
for i := range i.images {
// Keep the size before disposing i.image.
w, h := i.Size()
sizes[i] = struct{ w, h int }{w, h}
i.image.Dispose()
i.image = nil
}
// Let's do topological sort based on dependencies of drawing history. // Let's do topological sort based on dependencies of drawing history.
// It is assured that there are not loops since cyclic drawing makes images stale. // It is assured that there are not loops since cyclic drawing makes images stale.
type edge struct { type edge struct {
@ -204,7 +215,8 @@ func (i *images) restore() {
} }
for _, img := range sorted { for _, img := range sorted {
img.restore() s := sizes[img]
img.restore(s.w, s.h)
} }
} }