diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 960890009..9e3b3cd2e 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -470,6 +470,16 @@ func (i *Image) makeStaleIfDependingOn(target *Image) { } } +// makeStaleIfDependingOnShader makes the image stale if the image depends on shader. +func (i *Image) makeStaleIfDependingOnShader(shader *Shader) { + if i.stale { + return + } + if i.dependsOnShader(shader) { + i.makeStale() + } +} + // readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state. func (i *Image) readPixelsFromGPU() error { pix, err := i.image.Pixels() @@ -516,6 +526,16 @@ func (i *Image) dependsOn(target *Image) bool { return false } +// dependsOnShader reports whether the image depends on shader. +func (i *Image) dependsOnShader(shader *Shader) bool { + for _, c := range i.drawTrianglesHistory { + if c.shader == shader { + return true + } + } + return false +} + // dependingImages returns all images that is depended by the image. func (i *Image) dependingImages() map[*Image]struct{} { r := map[*Image]struct{}{} diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 26e770d0b..b7d45b69c 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -126,14 +126,12 @@ func (i *images) addShader(shader *Shader) { // remove removes img from the images. func (i *images) remove(img *Image) { - i.makeStaleIfDependingOnImpl(img) + i.makeStaleIfDependingOn(img) delete(i.images, img) } func (i *images) removeShader(shader *Shader) { - // TODO: Do we have to make images stale? - // However, dependencies are determiend by uniform variables... - // ?? + i.makeStaleIfDependingOnShader(shader) delete(i.shaders, shader) } @@ -153,12 +151,8 @@ func (i *images) resolveStaleImages() error { // When target is modified, all images depending on target can't be restored with target. // makeStaleIfDependingOn is called in such situation. func (i *images) makeStaleIfDependingOn(target *Image) { - i.makeStaleIfDependingOnImpl(target) -} - -func (i *images) makeStaleIfDependingOnImpl(target *Image) { if target == nil { - panic("restorable: target must not be nil at makeStaleIfDependingOnImpl") + panic("restorable: target must not be nil at makeStaleIfDependingOn") } if i.lastTarget == target { return @@ -169,6 +163,16 @@ func (i *images) makeStaleIfDependingOnImpl(target *Image) { } } +// makeStaleIfDependingOn makes all the images stale that depend on shader. +func (i *images) makeStaleIfDependingOnShader(shader *Shader) { + if shader == nil { + panic("restorable: shader must not be nil at makeStaleIfDependingOnShader") + } + for img := range i.images { + img.makeStaleIfDependingOnShader(shader) + } +} + // restore restores the images. // // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index 24dd41a68..81d5344e6 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -136,3 +136,35 @@ func TestShaderMultipleSources(t *testing.T) { t.Errorf("got %v, want %v", got, want) } } + +func TestShaderDispose(t *testing.T) { + if !graphicscommand.IsShaderAvailable() { + t.Skip("shader is not available on this environment") + } + + img := NewImage(1, 1, false) + defer img.Dispose() + + ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) + s := NewShader(&ir) + us := map[int]interface{}{ + 0: []float32{1, 1}, + } + img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero, s, us) + + // Dispose the shader. This should invalidates (= being stale) all the images using this shader. + s.Dispose() + + if err := ResolveStaleImages(); err != nil { + t.Fatal(err) + } + if err := RestoreIfNeeded(); err != nil { + t.Fatal(err) + } + + want := color.RGBA{0xff, 0, 0, 0xff} + got := pixelsToColor(img.BasePixelsForTesting(), 0, 0) + if !sameColors(got, want, 1) { + t.Errorf("got %v, want %v", got, want) + } +}