From 7b9cc8deb41f69deeff92534cf533be87bfd3162 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 26 May 2020 00:20:08 +0900 Subject: [PATCH] restorable: Add tests to dispose shaders When a shader is disposed, all the images depending on it should become stale, i.e., discard its all the rendering history items, because they cannot be restored due to the lack of data on the GPU. --- internal/restorable/image.go | 20 +++++++++++++++++++ internal/restorable/images.go | 22 +++++++++++--------- internal/restorable/shader_test.go | 32 ++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 9 deletions(-) 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) + } +}