restorable: Refactoring

This commit is contained in:
Hajime Hoshi 2019-05-26 21:10:25 +09:00
parent 0f611b49ac
commit 3c0cd47b1f
5 changed files with 43 additions and 56 deletions

View File

@ -301,7 +301,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
} }
i.image.ReplacePixels(pixels, x, y, width, height) i.image.ReplacePixels(pixels, x, y, width, height)
if !NeedsRestoring() { if !needsRestoring() {
i.makeStale() i.makeStale()
return return
} }
@ -362,7 +362,7 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
} }
theImages.makeStaleIfDependingOn(i) theImages.makeStaleIfDependingOn(i)
if img.stale || img.volatile || i.screen || !NeedsRestoring() || i.volatile { if img.stale || img.volatile || i.screen || !needsRestoring() || i.volatile {
i.makeStale() i.makeStale()
} else { } else {
i.appendDrawTrianglesHistory(img, vertices, indices, colorm, mode, filter, address) i.appendDrawTrianglesHistory(img, vertices, indices, colorm, mode, filter, address)
@ -447,7 +447,7 @@ func (i *Image) readPixelsFromGPU() {
// resolveStale resolves the image's 'stale' state. // resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() { func (i *Image) resolveStale() {
if !NeedsRestoring() { if !needsRestoring() {
return return
} }
@ -551,15 +551,11 @@ func (i *Image) Dispose() {
i.stale = false i.stale = false
} }
// IsInvalidated returns a boolean value indicating whether the image is invalidated. // isInvalidated returns a boolean value indicating whether the image is invalidated.
// //
// If an image is invalidated, GL context is lost and all the images should be restored asap. // If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) IsInvalidated() (bool, error) { func (i *Image) isInvalidated() bool {
// FlushCommands is required because c.offscreen.impl might not have an actual texture. // FlushCommands is required because c.offscreen.impl might not have an actual texture.
graphicscommand.FlushCommands() graphicscommand.FlushCommands()
if !NeedsRestoring() { return i.image.IsInvalidated()
return false, nil
}
return i.image.IsInvalidated(), nil
} }

View File

@ -23,8 +23,8 @@ import (
// forceRestoring reports whether restoring forcely happens or not. // forceRestoring reports whether restoring forcely happens or not.
var forceRestoring = true var forceRestoring = true
// NeedsRestoring reports whether restoring process works or not. // needsRestoring reports whether restoring process works or not.
func NeedsRestoring() bool { func needsRestoring() bool {
if forceRestoring { if forceRestoring {
return true return true
} }
@ -53,16 +53,33 @@ var theImages = &images{
// ResolveStaleImages is intended to be called at the end of a frame. // ResolveStaleImages is intended to be called at the end of a frame.
func ResolveStaleImages() { func ResolveStaleImages() {
graphicscommand.FlushCommands() graphicscommand.FlushCommands()
if !NeedsRestoring() { if !needsRestoring() {
return return
} }
theImages.resolveStaleImages() theImages.resolveStaleImages()
} }
// Restore restores the images. // RestoreIfNeeded restores the images.
// //
// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers.
func Restore() error { func RestoreIfNeeded() error {
if !needsRestoring() {
return nil
}
if !forceRestoring {
r := false
// As isInvalidated() is expensive, call this only for one image.
// This assumes that if there is one image that is invalidated, all images are invalidated.
for img := range theImages.images {
r = img.isInvalidated()
break
}
if !r {
return nil
}
}
if err := graphicscommand.ResetGraphicsDriverState(); err != nil { if err := graphicscommand.ResetGraphicsDriverState(); err != nil {
return err return err
} }
@ -144,7 +161,7 @@ func (i *images) makeStaleIfDependingOnImpl(target *Image) {
// //
// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers.
func (i *images) restore() error { func (i *images) restore() error {
if !NeedsRestoring() { if !needsRestoring() {
panic("restorable: restore cannot be called when restoring is disabled") panic("restorable: restore cannot be called when restoring is disabled")
} }

View File

@ -75,7 +75,7 @@ func TestRestore(t *testing.T) {
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff} clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
img0.Fill(clr0.R, clr0.G, clr0.B, clr0.A) img0.Fill(clr0.R, clr0.G, clr0.B, clr0.A)
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
want := clr0 want := clr0
@ -92,7 +92,7 @@ func TestRestoreWithoutDraw(t *testing.T) {
// If there is no drawing command on img0, img0 is cleared when restored. // If there is no drawing command on img0, img0 is cleared when restored.
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -131,7 +131,7 @@ func TestRestoreChain(t *testing.T) {
imgs[i+1].DrawTriangles(imgs[i], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) imgs[i+1].DrawTriangles(imgs[i], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
} }
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
want := clr want := clr
@ -175,7 +175,7 @@ func TestRestoreChain2(t *testing.T) {
} }
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for i, img := range imgs { for i, img := range imgs {
@ -214,7 +214,7 @@ func TestRestoreOverrideSource(t *testing.T) {
img0.Fill(clr1.R, clr1.G, clr1.B, clr1.A) img0.Fill(clr1.R, clr1.G, clr1.B, clr1.A)
img1.DrawTriangles(img0, quadVertices(img0, w, h, 0, 0), is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img1.DrawTriangles(img0, quadVertices(img0, w, h, 0, 0), is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
testCases := []struct { testCases := []struct {
@ -307,7 +307,7 @@ func TestRestoreComplexGraph(t *testing.T) {
vs = quadVertices(img3, w, h, 2, 0) vs = quadVertices(img3, w, h, 2, 0)
img7.DrawTriangles(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img7.DrawTriangles(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
testCases := []struct { testCases := []struct {
@ -398,7 +398,7 @@ func TestRestoreRecursive(t *testing.T) {
img1.DrawTriangles(img0, quadVertices(img0, w, h, 1, 0), is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img1.DrawTriangles(img0, quadVertices(img0, w, h, 1, 0), is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
img0.DrawTriangles(img1, quadVertices(img1, w, h, 1, 0), is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img0.DrawTriangles(img1, quadVertices(img1, w, h, 1, 0), is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
testCases := []struct { testCases := []struct {
@ -452,7 +452,7 @@ func TestReplacePixels(t *testing.T) {
} }
} }
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 7; j < 11; j++ { for j := 7; j < 11; j++ {
@ -484,7 +484,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1) img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
r, g, b, a := img1.At(0, 0) r, g, b, a := img1.At(0, 0)
@ -517,7 +517,7 @@ func TestDispose(t *testing.T) {
img1.Dispose() img1.Dispose()
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
r, g, b, a := img0.At(0, 0) r, g, b, a := img0.At(0, 0)
@ -625,7 +625,7 @@ func TestReplacePixelsOnly(t *testing.T) {
} }
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := RestoreIfNeeded(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
want := color.RGBA{1, 2, 3, 4} want := color.RGBA{1, 2, 3, 4}

View File

@ -404,13 +404,6 @@ func (i *Image) IsVolatile() bool {
return i.backend.restorable.IsVolatile() return i.backend.restorable.IsVolatile()
} }
func (i *Image) IsInvalidated() (bool, error) {
backendsM.Lock()
defer backendsM.Unlock()
v, err := i.backend.restorable.IsInvalidated()
return v, err
}
func NewImage(width, height int) *Image { func NewImage(width, height int) *Image {
// Actual allocation is done lazily. // Actual allocation is done lazily.
return &Image{ return &Image{
@ -508,15 +501,10 @@ func ResolveStaleImages() {
restorable.ResolveStaleImages() restorable.ResolveStaleImages()
} }
func NeedsRestoring() bool { func RestoreIfNeeded() error {
// As NeedsRestoring is an immutable state, no need to lock here.
return restorable.NeedsRestoring()
}
func Restore() error {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
return restorable.Restore() return restorable.RestoreIfNeeded()
} }
func Images() []image.Image { func Images() []image.Image {

View File

@ -146,22 +146,8 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
return nil return nil
} }
func (c *uiContext) needsRestoring() (bool, error) {
return c.offscreen.mipmap.original().IsInvalidated()
}
func (c *uiContext) restoreIfNeeded() error { func (c *uiContext) restoreIfNeeded() error {
if !shareable.NeedsRestoring() { if err := shareable.RestoreIfNeeded(); err != nil {
return nil
}
r, err := c.needsRestoring()
if err != nil {
return err
}
if !r {
return nil
}
if err := shareable.Restore(); err != nil {
return err return err
} }
return nil return nil