From 8aef1f90804c50eacac910dad50fea659bab04a7 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 19 Aug 2020 00:54:21 +0900 Subject: [PATCH] restorable: Enable to set the 'volatile' state later Updates #1309 --- image.go | 8 ++-- internal/buffered/image.go | 22 +++++++--- internal/mipmap/mipmap.go | 22 ++++++---- internal/restorable/image.go | 27 +++++++----- internal/restorable/images_test.go | 67 +++++++++++++++--------------- internal/restorable/shader_test.go | 14 +++---- internal/shareable/image.go | 30 +++++++++---- internal/shareable/image_test.go | 45 ++++++++++---------- uicontext.go | 3 +- 9 files changed, 140 insertions(+), 98 deletions(-) diff --git a/image.go b/image.go index 69c4ba44a..6691dcfca 100644 --- a/image.go +++ b/image.go @@ -814,12 +814,12 @@ func (i *Image) ReplacePixels(pixels []byte) error { // // Error returned by NewImage is always nil as of 1.5.0. func NewImage(width, height int, filter Filter) (*Image, error) { - return newImage(width, height, filter, false), nil + return newImage(width, height, filter), nil } -func newImage(width, height int, filter Filter, volatile bool) *Image { +func newImage(width, height int, filter Filter) *Image { i := &Image{ - mipmap: mipmap.New(width, height, volatile), + mipmap: mipmap.New(width, height), filter: filter, bounds: image.Rect(0, 0, width, height), } @@ -841,7 +841,7 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) { width, height := size.X, size.Y i := &Image{ - mipmap: mipmap.New(width, height, false), + mipmap: mipmap.New(width, height), filter: filter, bounds: image.Rect(0, 0, width, height), } diff --git a/internal/buffered/image.go b/internal/buffered/image.go index c92087238..cdc8c141c 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -49,26 +49,38 @@ func EndFrame() error { return shareable.EndFrame() } -func NewImage(width, height int, volatile bool) *Image { +func NewImage(width, height int) *Image { i := &Image{} - i.initialize(width, height, volatile) + i.initialize(width, height) return i } -func (i *Image) initialize(width, height int, volatile bool) { +func (i *Image) initialize(width, height int) { if maybeCanAddDelayedCommand() { if tryAddDelayedCommand(func() error { - i.initialize(width, height, volatile) + i.initialize(width, height) return nil }) { return } } - i.img = shareable.NewImage(width, height, volatile) + i.img = shareable.NewImage(width, height) i.width = width i.height = height } +func (i *Image) SetVolatile(volatile bool) { + if maybeCanAddDelayedCommand() { + if tryAddDelayedCommand(func() error { + i.SetVolatile(volatile) + return nil + }) { + return + } + } + i.img.SetVolatile(volatile) +} + func NewScreenFramebufferImage(width, height int) *Image { i := &Image{} i.initializeAsScreenFramebuffer(width, height) diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index f02e37ca6..ec0eb413d 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -50,13 +50,12 @@ type Mipmap struct { imgs map[int]*buffered.Image } -func New(width, height int, volatile bool) *Mipmap { +func New(width, height int) *Mipmap { return &Mipmap{ - width: width, - height: height, - volatile: volatile, - orig: buffered.NewImage(width, height, volatile), - imgs: map[int]*buffered.Image{}, + width: width, + height: height, + orig: buffered.NewImage(width, height), + imgs: map[int]*buffered.Image{}, } } @@ -69,6 +68,14 @@ func NewScreenFramebufferMipmap(width, height int) *Mipmap { } } +func (m *Mipmap) SetVolatile(volatile bool) { + m.volatile = volatile + if m.volatile { + m.disposeMipmaps() + } + m.orig.SetVolatile(volatile) +} + func (m *Mipmap) Dump(name string, blackbg bool) error { return m.orig.Dump(name, blackbg) } @@ -229,7 +236,8 @@ func (m *Mipmap) level(level int) *buffered.Image { m.imgs[level] = nil return nil } - s := buffered.NewImage(w2, h2, m.volatile) + s := buffered.NewImage(w2, h2) + s.SetVolatile(m.volatile) s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, nil, nil) m.imgs[level] = s diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 0febc1f45..2a072e5c2 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -133,26 +133,30 @@ func init() { // NewImage creates an empty image with the given size. // -// volatile indicates whether the image is volatile. Regular non-volatile images need to record drawing history or -// read its pixels from GPU if necessary so that all the images can be restored automatically from the context lost. -// However, such recording the drawing history or reading pixels from GPU are expensive operations. Volatile images -// can skip such oprations, but the image content is cleared every frame instead. -// // The returned image is cleared. // // Note that Dispose is not called automatically. -func NewImage(width, height int, volatile bool) *Image { +func NewImage(width, height int) *Image { i := &Image{ - image: graphicscommand.NewImage(width, height), - width: width, - height: height, - volatile: volatile, + image: graphicscommand.NewImage(width, height), + width: width, + height: height, } fillImage(i.image, color.RGBA{}) theImages.add(i) return i } +// SetVolatile sets the volatile state of the image. +// +// Regular non-volatile images need to record drawing history or read its pixels from GPU if necessary so that all +// the images can be restored automatically from the context lost. However, such recording the drawing history or +// reading pixels from GPU are expensive operations. Volatile images can skip such oprations, but the image content +// is cleared every frame instead. +func (i *Image) SetVolatile(volatile bool) { + i.volatile = volatile +} + // Extend extends the image by the given size. // Extend creates a new image with the given size and copies the pixels of the given source image. // Extend disposes itself after its call. @@ -175,7 +179,8 @@ func (i *Image) Extend(width, height int) *Image { panic("restorable: Extend after DrawTriangles is forbidden") } - newImg := NewImage(width, height, i.volatile) + newImg := NewImage(width, height) + newImg.SetVolatile(i.volatile) i.basePixels.Apply(newImg.image) if i.basePixels.baseColor != (color.RGBA{}) { diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index 6a3b973d1..9366d5db4 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -55,7 +55,7 @@ func sameColors(c1, c2 color.RGBA, delta int) bool { } func TestRestore(t *testing.T) { - img0 := NewImage(1, 1, false) + img0 := NewImage(1, 1) defer img0.Dispose() clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff} @@ -74,7 +74,7 @@ func TestRestore(t *testing.T) { } func TestRestoreWithoutDraw(t *testing.T) { - img0 := NewImage(1024, 1024, false) + img0 := NewImage(1024, 1024) defer img0.Dispose() // If there is no drawing command on img0, img0 is cleared when restored. @@ -118,7 +118,7 @@ func TestRestoreChain(t *testing.T) { const num = 10 imgs := []*Image{} for i := 0; i < num; i++ { - img := NewImage(1, 1, false) + img := NewImage(1, 1) imgs = append(imgs, img) } defer func() { @@ -156,7 +156,7 @@ func TestRestoreChain2(t *testing.T) { ) imgs := []*Image{} for i := 0; i < num; i++ { - img := NewImage(w, h, false) + img := NewImage(w, h) imgs = append(imgs, img) } defer func() { @@ -202,10 +202,10 @@ func TestRestoreOverrideSource(t *testing.T) { w = 1 h = 1 ) - img0 := NewImage(w, h, false) - img1 := NewImage(w, h, false) - img2 := NewImage(w, h, false) - img3 := NewImage(w, h, false) + img0 := NewImage(w, h) + img1 := NewImage(w, h) + img2 := NewImage(w, h) + img3 := NewImage(w, h) defer func() { img3.Dispose() img2.Dispose() @@ -281,11 +281,11 @@ func TestRestoreComplexGraph(t *testing.T) { img0 := newImageFromImage(base) img1 := newImageFromImage(base) img2 := newImageFromImage(base) - img3 := NewImage(w, h, false) - img4 := NewImage(w, h, false) - img5 := NewImage(w, h, false) - img6 := NewImage(w, h, false) - img7 := NewImage(w, h, false) + img3 := NewImage(w, h) + img4 := NewImage(w, h) + img5 := NewImage(w, h) + img6 := NewImage(w, h) + img7 := NewImage(w, h) defer func() { img7.Dispose() img6.Dispose() @@ -384,7 +384,7 @@ func TestRestoreComplexGraph(t *testing.T) { func newImageFromImage(rgba *image.RGBA) *Image { s := rgba.Bounds().Size() - img := NewImage(s.X, s.Y, false) + img := NewImage(s.X, s.Y) img.ReplacePixels(rgba.Pix, 0, 0, s.X, s.Y) return img } @@ -401,7 +401,7 @@ func TestRestoreRecursive(t *testing.T) { base.Pix[3] = 0xff img0 := newImageFromImage(base) - img1 := NewImage(w, h, false) + img1 := NewImage(w, h) defer func() { img1.Dispose() img0.Dispose() @@ -446,7 +446,7 @@ func TestRestoreRecursive(t *testing.T) { } func TestReplacePixels(t *testing.T) { - img := NewImage(17, 31, false) + img := NewImage(17, 31) defer img.Dispose() pix := make([]byte, 4*4*4) @@ -497,7 +497,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) { base.Pix[3] = 0xff img0 := newImageFromImage(base) defer img0.Dispose() - img1 := NewImage(2, 1, false) + img1 := NewImage(2, 1) defer img1.Dispose() vs := quadVertices(1, 1, 0, 0) @@ -566,7 +566,7 @@ func TestReplacePixelsPart(t *testing.T) { pix[i] = 0xff } - img := NewImage(4, 4, false) + img := NewImage(4, 4) // This doesn't make the image stale. Its base pixels are available. img.ReplacePixels(pix, 1, 1, 2, 2) @@ -637,9 +637,9 @@ func TestReplacePixelsPart(t *testing.T) { func TestReplacePixelsOnly(t *testing.T) { const w, h = 128, 128 - img0 := NewImage(w, h, false) + img0 := NewImage(w, h) defer img0.Dispose() - img1 := NewImage(1, 1, false) + img1 := NewImage(1, 1) defer img1.Dispose() for i := 0; i < w*h; i += 5 { @@ -687,8 +687,9 @@ func TestReplacePixelsOnly(t *testing.T) { // Issue #793 func TestReadPixelsFromVolatileImage(t *testing.T) { const w, h = 16, 16 - dst := NewImage(w, h, true /* volatile */) - src := NewImage(w, h, false) + dst := NewImage(w, h) + dst.SetVolatile(true) + src := NewImage(w, h) // First, make sure that dst has pixels dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h) @@ -717,8 +718,8 @@ func TestReadPixelsFromVolatileImage(t *testing.T) { func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) { const w, h = 16, 16 - src := NewImage(w, h, false) - dst := NewImage(w, h, false) + src := NewImage(w, h) + dst := NewImage(w, h) vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() @@ -735,8 +736,8 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) { }() const w, h = 16, 16 - src := NewImage(w, h, false) - dst := NewImage(w, h, false) + src := NewImage(w, h) + dst := NewImage(w, h) vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() @@ -750,7 +751,7 @@ func TestExtend(t *testing.T) { } const w, h = 16, 16 - orig := NewImage(w, h, false) + orig := NewImage(w, h) pix := make([]byte, 4*w*h) for j := 0; j < h; j++ { for i := 0; i < w; i++ { @@ -785,7 +786,7 @@ func TestExtend(t *testing.T) { func TestClearPixels(t *testing.T) { const w, h = 16, 16 - img := NewImage(w, h, false) + img := NewImage(w, h) img.ReplacePixels(make([]byte, 4*4*4), 0, 0, 4, 4) img.ReplacePixels(make([]byte, 4*4*4), 4, 0, 4, 4) img.ClearPixels(0, 0, 4, 4) @@ -797,7 +798,7 @@ func TestClearPixels(t *testing.T) { func TestFill(t *testing.T) { const w, h = 16, 16 - img := NewImage(w, h, false) + img := NewImage(w, h) img.Fill(color.RGBA{0xff, 0, 0, 0xff}) if err := ResolveStaleImages(); err != nil { t.Fatal(err) @@ -823,10 +824,10 @@ func TestFill(t *testing.T) { // Issue #1170 func TestFill2(t *testing.T) { const w, h = 16, 16 - src := NewImage(w, h, false) + src := NewImage(w, h) src.Fill(color.RGBA{0xff, 0, 0, 0xff}) - dst := NewImage(w, h, false) + dst := NewImage(w, h) vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) @@ -854,8 +855,8 @@ func TestFill2(t *testing.T) { func TestMutateSlices(t *testing.T) { const w, h = 16, 16 - dst := NewImage(w, h, false) - src := NewImage(w, h, false) + dst := NewImage(w, h) + src := NewImage(w, h) pix := make([]byte, 4*w*h) for i := 0; i < w*h; i++ { pix[4*i] = byte(i) diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index 0e36bd5c8..4451bf39a 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -25,7 +25,7 @@ import ( ) func TestShader(t *testing.T) { - img := NewImage(1, 1, false) + img := NewImage(1, 1) defer img.Dispose() ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) @@ -50,7 +50,7 @@ func TestShaderChain(t *testing.T) { const num = 10 imgs := []*Image{} for i := 0; i < num; i++ { - img := NewImage(1, 1, false) + img := NewImage(1, 1) defer img.Dispose() imgs = append(imgs, img) } @@ -82,13 +82,13 @@ func TestShaderChain(t *testing.T) { func TestShaderMultipleSources(t *testing.T) { var srcs [graphics.ShaderImageNum]*Image for i := range srcs { - srcs[i] = NewImage(1, 1, false) + srcs[i] = NewImage(1, 1) } srcs[0].ReplacePixels([]byte{0x40, 0, 0, 0xff}, 0, 0, 1, 1) srcs[1].ReplacePixels([]byte{0, 0x80, 0, 0xff}, 0, 0, 1, 1) srcs[2].ReplacePixels([]byte{0, 0, 0xc0, 0xff}, 0, 0, 1, 1) - dst := NewImage(1, 1, false) + dst := NewImage(1, 1) ir := etesting.ShaderProgramImages(3) s := NewShader(&ir) @@ -113,7 +113,7 @@ func TestShaderMultipleSources(t *testing.T) { } func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { - src := NewImage(3, 1, false) + src := NewImage(3, 1) src.ReplacePixels([]byte{ 0x40, 0, 0, 0xff, 0, 0x80, 0, 0xff, @@ -121,7 +121,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { }, 0, 0, 3, 1) srcs := [graphics.ShaderImageNum]*Image{src, src, src} - dst := NewImage(1, 1, false) + dst := NewImage(1, 1) ir := etesting.ShaderProgramImages(3) s := NewShader(&ir) @@ -149,7 +149,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { } func TestShaderDispose(t *testing.T) { - img := NewImage(1, 1, false) + img := NewImage(1, 1) defer img.Dispose() ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) diff --git a/internal/shareable/image.go b/internal/shareable/image.go index 0ae46ee7b..7978f9487 100644 --- a/internal/shareable/image.go +++ b/internal/shareable/image.go @@ -212,7 +212,8 @@ func (i *Image) ensureNotShared() { sy0 := float32(oy) sx1 := float32(ox + w) sy1 := float32(oy + h) - newImg := restorable.NewImage(w, h, i.volatile) + newImg := restorable.NewImage(w, h) + newImg.SetVolatile(i.volatile) vs := []float32{ dx0, dy0, sx0, sy0, 1, 1, 1, 1, dx1, dy0, sx1, sy0, 1, 1, 1, 1, @@ -244,7 +245,8 @@ func (i *Image) makeShared() error { panic("shareable: makeShared cannot be called on a non-shareable image") } - newI := NewImage(i.width, i.height, i.volatile) + newI := NewImage(i.width, i.height) + newI.SetVolatile(i.volatile) pixels := make([]byte, 4*i.width*i.height) for y := 0; y < i.height; y++ { for x := 0; x < i.width; x++ { @@ -545,15 +547,25 @@ func (i *Image) dispose(markDisposed bool) { theBackends = append(theBackends[:index], theBackends[index+1:]...) } -func NewImage(width, height int, volatile bool) *Image { +func NewImage(width, height int) *Image { // Actual allocation is done lazily, and the lock is not needed. return &Image{ - width: width, - height: height, - volatile: volatile, + width: width, + height: height, } } +func (i *Image) SetVolatile(volatile bool) { + i.volatile = volatile + if i.backend == nil { + return + } + if i.volatile { + i.ensureNotShared() + } + i.backend.restorable.SetVolatile(i.volatile) +} + func (i *Image) shareable() bool { if minSize == 0 || maxSize == 0 { panic("shareable: minSize or maxSize must be initialized") @@ -584,8 +596,9 @@ func (i *Image) allocate(shareable bool) { if !shareable || !i.shareable() { i.backend = &backend{ - restorable: restorable.NewImage(i.width+2*paddingSize, i.height+2*paddingSize, i.volatile), + restorable: restorable.NewImage(i.width+2*paddingSize, i.height+2*paddingSize), } + i.backend.restorable.SetVolatile(i.volatile) return } @@ -605,9 +618,10 @@ func (i *Image) allocate(shareable bool) { } b := &backend{ - restorable: restorable.NewImage(size, size, i.volatile), + restorable: restorable.NewImage(size, size), page: packing.NewPage(size, maxSize), } + b.restorable.SetVolatile(i.volatile) theBackends = append(theBackends, b) n := b.page.Alloc(i.width+2*paddingSize, i.height+2*paddingSize) diff --git a/internal/shareable/image_test.go b/internal/shareable/image_test.go index 650fbaba7..040beb287 100644 --- a/internal/shareable/image_test.go +++ b/internal/shareable/image_test.go @@ -58,22 +58,22 @@ const bigSize = 2049 func TestEnsureNotShared(t *testing.T) { // Create img1 and img2 with this size so that the next images are allocated // with non-upper-left location. - img1 := NewImage(bigSize, 100, false) + img1 := NewImage(bigSize, 100) defer img1.MarkDisposed() // Ensure img1's region is allocated. img1.ReplacePixels(make([]byte, 4*bigSize*100)) - img2 := NewImage(100, bigSize, false) + img2 := NewImage(100, bigSize) defer img2.MarkDisposed() img2.ReplacePixels(make([]byte, 4*100*bigSize)) const size = 32 - img3 := NewImage(size/2, size/2, false) + img3 := NewImage(size/2, size/2) defer img3.MarkDisposed() img3.ReplacePixels(make([]byte, (size/2)*(size/2)*4)) - img4 := NewImage(size, size, false) + img4 := NewImage(size, size) defer img4.MarkDisposed() pix := make([]byte, size*size*4) @@ -132,18 +132,18 @@ func TestEnsureNotShared(t *testing.T) { func TestReshared(t *testing.T) { const size = 16 - img0 := NewImage(size, size, false) + img0 := NewImage(size, size) defer img0.MarkDisposed() img0.ReplacePixels(make([]byte, 4*size*size)) - img1 := NewImage(size, size, false) + img1 := NewImage(size, size) defer img1.MarkDisposed() img1.ReplacePixels(make([]byte, 4*size*size)) if got, want := img1.IsSharedForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } - img2 := NewImage(size, size, false) + img2 := NewImage(size, size) defer img2.MarkDisposed() pix := make([]byte, 4*size*size) for j := 0; j < size; j++ { @@ -156,7 +156,8 @@ func TestReshared(t *testing.T) { } img2.ReplacePixels(pix) - img3 := NewImage(size, size, true /* volatile */) + img3 := NewImage(size, size) + img3.SetVolatile(true) defer img3.MarkDisposed() img1.ReplacePixels(make([]byte, 4*size*size)) if got, want := img3.IsSharedForTesting(), false; got != want { @@ -242,7 +243,7 @@ func TestReshared(t *testing.T) { func TestExtend(t *testing.T) { const w0, h0 = 100, 100 - img0 := NewImage(w0, h0, false) + img0 := NewImage(w0, h0) defer img0.MarkDisposed() p0 := make([]byte, 4*w0*h0) for i := 0; i < w0*h0; i++ { @@ -254,7 +255,7 @@ func TestExtend(t *testing.T) { img0.ReplacePixels(p0) const w1, h1 = 1025, 100 - img1 := NewImage(w1, h1, false) + img1 := NewImage(w1, h1) defer img1.MarkDisposed() p1 := make([]byte, 4*w1*h1) for i := 0; i < w1*h1; i++ { @@ -310,9 +311,9 @@ func TestExtend(t *testing.T) { func TestReplacePixelsAfterDrawTriangles(t *testing.T) { const w, h = 256, 256 - src := NewImage(w, h, false) + src := NewImage(w, h) defer src.MarkDisposed() - dst := NewImage(w, h, false) + dst := NewImage(w, h) defer dst.MarkDisposed() pix := make([]byte, 4*w*h) @@ -352,9 +353,9 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) { // Issue #887 func TestSmallImages(t *testing.T) { const w, h = 4, 8 - src := NewImage(w, h, false) + src := NewImage(w, h) defer src.MarkDisposed() - dst := NewImage(w, h, false) + dst := NewImage(w, h) defer dst.MarkDisposed() pix := make([]byte, 4*w*h) @@ -391,11 +392,11 @@ func TestSmallImages(t *testing.T) { // Issue #887 func TestLongImages(t *testing.T) { const w, h = 1, 6 - src := NewImage(w, h, false) + src := NewImage(w, h) defer src.MarkDisposed() const dstW, dstH = 256, 256 - dst := NewImage(dstW, dstH, false) + dst := NewImage(dstW, dstH) defer dst.MarkDisposed() pix := make([]byte, 4*w*h) @@ -433,10 +434,10 @@ func TestLongImages(t *testing.T) { func TestDisposeImmediately(t *testing.T) { // This tests restorable.Image.ClearPixels is called but ReplacePixels is not called. - img0 := NewImage(16, 16, false) + img0 := NewImage(16, 16) img0.EnsureNotSharedForTesting() - img1 := NewImage(16, 16, false) + img1 := NewImage(16, 16) img1.EnsureNotSharedForTesting() // img0 and img1 should share the same backend in 99.9999% possibility. @@ -447,12 +448,12 @@ func TestDisposeImmediately(t *testing.T) { // Issue #1028 func TestExtendWithBigImage(t *testing.T) { - img0 := NewImage(1, 1, false) + img0 := NewImage(1, 1) defer img0.MarkDisposed() img0.ReplacePixels(make([]byte, 4*1*1)) - img1 := NewImage(1025, 1025, false) + img1 := NewImage(1025, 1025) defer img1.MarkDisposed() img1.ReplacePixels(make([]byte, 4*1025*1025)) @@ -462,7 +463,7 @@ func TestExtendWithBigImage(t *testing.T) { func TestMaxImageSize(t *testing.T) { // This tests that a too-big image is allocated correctly. s := maxImageSizeForTesting - img := NewImage(s, s, false) + img := NewImage(s, s) defer img.MarkDisposed() img.ReplacePixels(make([]byte, 4*s*s)) } @@ -474,7 +475,7 @@ func TestMinImageSize(t *testing.T) { // This tests that extending a backend works correctly. // Though the image size is minimum size of the backend, extending the backend happens due to the paddings. s := minImageSizeForTesting - img := NewImage(s, s, false) + img := NewImage(s, s) defer img.MarkDisposed() img.ReplacePixels(make([]byte, 4*s*s)) } diff --git a/uicontext.go b/uicontext.go index bc2355ee6..57986100b 100644 --- a/uicontext.go +++ b/uicontext.go @@ -175,7 +175,8 @@ func (c *uiContext) updateOffscreen() { } } if c.offscreen == nil { - c.offscreen = newImage(sw, sh, FilterDefault, true) + c.offscreen = newImage(sw, sh, FilterDefault) + c.offscreen.mipmap.SetVolatile(true) } // The window size is automatically adjusted when Run is used.