mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-09-19 21:02:18 +02:00
graphics: Remove MakeVolatile/IsVolatile
This commit is contained in:
parent
06d198cc0e
commit
1c2c932c6c
36
image.go
36
image.go
@ -663,38 +663,16 @@ type DrawImageOptions struct {
|
|||||||
//
|
//
|
||||||
// Error returned by NewImage is always nil as of 1.5.0-alpha.
|
// Error returned by NewImage is always nil as of 1.5.0-alpha.
|
||||||
func NewImage(width, height int, filter Filter) (*Image, error) {
|
func NewImage(width, height int, filter Filter) (*Image, error) {
|
||||||
|
return newImage(width, height, filter, false), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newImage(width, height int, filter Filter, volatile bool) *Image {
|
||||||
i := &Image{
|
i := &Image{
|
||||||
mipmap: newMipmap(width, height),
|
mipmap: newMipmap(width, height, volatile),
|
||||||
filter: filter,
|
filter: filter,
|
||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
return i, nil
|
return i
|
||||||
}
|
|
||||||
|
|
||||||
// makeVolatile makes the image 'volatile'.
|
|
||||||
// A volatile image is always cleared at the start of a frame.
|
|
||||||
//
|
|
||||||
// This is suitable for offscreen images that pixels are changed often.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// When the image is disposed, makeVolatile does nothing.
|
|
||||||
func (i *Image) makeVolatile() {
|
|
||||||
if enqueueImageOpIfNeeded(func() func() {
|
|
||||||
return func() {
|
|
||||||
i.makeVolatile()
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.isDisposed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i.mipmap.makeVolatile()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageFromImage creates a new image with the given image (source).
|
// NewImageFromImage creates a new image with the given image (source).
|
||||||
@ -711,7 +689,7 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
|||||||
width, height := size.X, size.Y
|
width, height := size.X, size.Y
|
||||||
|
|
||||||
i := &Image{
|
i := &Image{
|
||||||
mipmap: newMipmap(width, height),
|
mipmap: newMipmap(width, height, false),
|
||||||
filter: filter,
|
filter: filter,
|
||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
|
@ -129,14 +129,20 @@ func init() {
|
|||||||
|
|
||||||
// NewImage creates an empty image with the given size.
|
// 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.
|
// The returned image is cleared.
|
||||||
//
|
//
|
||||||
// Note that Dispose is not called automatically.
|
// Note that Dispose is not called automatically.
|
||||||
func NewImage(width, height int) *Image {
|
func NewImage(width, height int, volatile bool) *Image {
|
||||||
i := &Image{
|
i := &Image{
|
||||||
image: graphicscommand.NewImage(width, height),
|
image: graphicscommand.NewImage(width, height),
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
volatile: volatile,
|
||||||
}
|
}
|
||||||
i.clear()
|
i.clear()
|
||||||
theImages.add(i)
|
theImages.add(i)
|
||||||
@ -165,7 +171,7 @@ func (i *Image) Extend(width, height int) *Image {
|
|||||||
panic("restorable: Extend after DrawTriangles is forbidden")
|
panic("restorable: Extend after DrawTriangles is forbidden")
|
||||||
}
|
}
|
||||||
|
|
||||||
newImg := NewImage(width, height)
|
newImg := NewImage(width, height, i.volatile)
|
||||||
i.basePixels.Apply(newImg.image)
|
i.basePixels.Apply(newImg.image)
|
||||||
|
|
||||||
if i.basePixels.baseColor != (color.RGBA{}) {
|
if i.basePixels.baseColor != (color.RGBA{}) {
|
||||||
@ -178,10 +184,6 @@ func (i *Image) Extend(width, height int) *Image {
|
|||||||
return newImg
|
return newImg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) MakeVolatile() {
|
|
||||||
i.volatile = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
|
// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
|
||||||
//
|
//
|
||||||
// The returned image is cleared.
|
// The returned image is cleared.
|
||||||
@ -288,10 +290,6 @@ func fillImage(i *graphicscommand.Image, clr color.RGBA) {
|
|||||||
i.DrawTriangles(emptyImage.image, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressClampToZero)
|
i.DrawTriangles(emptyImage.image, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressClampToZero)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) IsVolatile() bool {
|
|
||||||
return i.volatile
|
|
||||||
}
|
|
||||||
|
|
||||||
// BasePixelsForTesting returns the image's basePixels for testing.
|
// BasePixelsForTesting returns the image's basePixels for testing.
|
||||||
func (i *Image) BasePixelsForTesting() *Pixels {
|
func (i *Image) BasePixelsForTesting() *Pixels {
|
||||||
return &i.basePixels
|
return &i.basePixels
|
||||||
|
@ -70,7 +70,7 @@ func sameColors(c1, c2 color.RGBA, delta int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRestore(t *testing.T) {
|
func TestRestore(t *testing.T) {
|
||||||
img0 := NewImage(1, 1)
|
img0 := NewImage(1, 1, false)
|
||||||
defer img0.Dispose()
|
defer img0.Dispose()
|
||||||
|
|
||||||
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
|
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
|
||||||
@ -87,7 +87,7 @@ func TestRestore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRestoreWithoutDraw(t *testing.T) {
|
func TestRestoreWithoutDraw(t *testing.T) {
|
||||||
img0 := NewImage(1024, 1024)
|
img0 := NewImage(1024, 1024, false)
|
||||||
defer img0.Dispose()
|
defer img0.Dispose()
|
||||||
|
|
||||||
// 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.
|
||||||
@ -129,7 +129,7 @@ func TestRestoreChain(t *testing.T) {
|
|||||||
const num = 10
|
const num = 10
|
||||||
imgs := []*Image{}
|
imgs := []*Image{}
|
||||||
for i := 0; i < num; i++ {
|
for i := 0; i < num; i++ {
|
||||||
img := NewImage(1, 1)
|
img := NewImage(1, 1, false)
|
||||||
imgs = append(imgs, img)
|
imgs = append(imgs, img)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -165,7 +165,7 @@ func TestRestoreChain2(t *testing.T) {
|
|||||||
)
|
)
|
||||||
imgs := []*Image{}
|
imgs := []*Image{}
|
||||||
for i := 0; i < num; i++ {
|
for i := 0; i < num; i++ {
|
||||||
img := NewImage(w, h)
|
img := NewImage(w, h, false)
|
||||||
imgs = append(imgs, img)
|
imgs = append(imgs, img)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -209,10 +209,10 @@ func TestRestoreOverrideSource(t *testing.T) {
|
|||||||
w = 1
|
w = 1
|
||||||
h = 1
|
h = 1
|
||||||
)
|
)
|
||||||
img0 := NewImage(w, h)
|
img0 := NewImage(w, h, false)
|
||||||
img1 := NewImage(w, h)
|
img1 := NewImage(w, h, false)
|
||||||
img2 := NewImage(w, h)
|
img2 := NewImage(w, h, false)
|
||||||
img3 := NewImage(w, h)
|
img3 := NewImage(w, h, false)
|
||||||
defer func() {
|
defer func() {
|
||||||
img3.Dispose()
|
img3.Dispose()
|
||||||
img2.Dispose()
|
img2.Dispose()
|
||||||
@ -286,11 +286,11 @@ func TestRestoreComplexGraph(t *testing.T) {
|
|||||||
img0 := newImageFromImage(base)
|
img0 := newImageFromImage(base)
|
||||||
img1 := newImageFromImage(base)
|
img1 := newImageFromImage(base)
|
||||||
img2 := newImageFromImage(base)
|
img2 := newImageFromImage(base)
|
||||||
img3 := NewImage(w, h)
|
img3 := NewImage(w, h, false)
|
||||||
img4 := NewImage(w, h)
|
img4 := NewImage(w, h, false)
|
||||||
img5 := NewImage(w, h)
|
img5 := NewImage(w, h, false)
|
||||||
img6 := NewImage(w, h)
|
img6 := NewImage(w, h, false)
|
||||||
img7 := NewImage(w, h)
|
img7 := NewImage(w, h, false)
|
||||||
defer func() {
|
defer func() {
|
||||||
img7.Dispose()
|
img7.Dispose()
|
||||||
img6.Dispose()
|
img6.Dispose()
|
||||||
@ -386,7 +386,7 @@ func TestRestoreComplexGraph(t *testing.T) {
|
|||||||
|
|
||||||
func newImageFromImage(rgba *image.RGBA) *Image {
|
func newImageFromImage(rgba *image.RGBA) *Image {
|
||||||
s := rgba.Bounds().Size()
|
s := rgba.Bounds().Size()
|
||||||
img := NewImage(s.X, s.Y)
|
img := NewImage(s.X, s.Y, false)
|
||||||
img.ReplacePixels(rgba.Pix, 0, 0, s.X, s.Y)
|
img.ReplacePixels(rgba.Pix, 0, 0, s.X, s.Y)
|
||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
@ -403,7 +403,7 @@ func TestRestoreRecursive(t *testing.T) {
|
|||||||
base.Pix[3] = 0xff
|
base.Pix[3] = 0xff
|
||||||
|
|
||||||
img0 := newImageFromImage(base)
|
img0 := newImageFromImage(base)
|
||||||
img1 := NewImage(w, h)
|
img1 := NewImage(w, h, false)
|
||||||
defer func() {
|
defer func() {
|
||||||
img1.Dispose()
|
img1.Dispose()
|
||||||
img0.Dispose()
|
img0.Dispose()
|
||||||
@ -446,7 +446,7 @@ func TestRestoreRecursive(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestReplacePixels(t *testing.T) {
|
func TestReplacePixels(t *testing.T) {
|
||||||
img := NewImage(17, 31)
|
img := NewImage(17, 31, false)
|
||||||
defer img.Dispose()
|
defer img.Dispose()
|
||||||
|
|
||||||
pix := make([]byte, 4*4*4)
|
pix := make([]byte, 4*4*4)
|
||||||
@ -489,7 +489,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
|
|||||||
base.Pix[3] = 0xff
|
base.Pix[3] = 0xff
|
||||||
img0 := newImageFromImage(base)
|
img0 := newImageFromImage(base)
|
||||||
defer img0.Dispose()
|
defer img0.Dispose()
|
||||||
img1 := NewImage(2, 1)
|
img1 := NewImage(2, 1, false)
|
||||||
defer img1.Dispose()
|
defer img1.Dispose()
|
||||||
|
|
||||||
vs := quadVertices(1, 1, 0, 0)
|
vs := quadVertices(1, 1, 0, 0)
|
||||||
@ -548,7 +548,7 @@ func TestReplacePixelsPart(t *testing.T) {
|
|||||||
pix[i] = 0xff
|
pix[i] = 0xff
|
||||||
}
|
}
|
||||||
|
|
||||||
img := NewImage(4, 4)
|
img := NewImage(4, 4, false)
|
||||||
// This doesn't make the image stale. Its base pixels are available.
|
// This doesn't make the image stale. Its base pixels are available.
|
||||||
img.ReplacePixels(pix, 1, 1, 2, 2)
|
img.ReplacePixels(pix, 1, 1, 2, 2)
|
||||||
|
|
||||||
@ -619,9 +619,9 @@ func TestReplacePixelsPart(t *testing.T) {
|
|||||||
|
|
||||||
func TestReplacePixelsOnly(t *testing.T) {
|
func TestReplacePixelsOnly(t *testing.T) {
|
||||||
const w, h = 128, 128
|
const w, h = 128, 128
|
||||||
img0 := NewImage(w, h)
|
img0 := NewImage(w, h, false)
|
||||||
defer img0.Dispose()
|
defer img0.Dispose()
|
||||||
img1 := NewImage(1, 1)
|
img1 := NewImage(1, 1, false)
|
||||||
defer img1.Dispose()
|
defer img1.Dispose()
|
||||||
|
|
||||||
for i := 0; i < w*h; i += 5 {
|
for i := 0; i < w*h; i += 5 {
|
||||||
@ -667,9 +667,8 @@ func TestReplacePixelsOnly(t *testing.T) {
|
|||||||
// Issue #793
|
// Issue #793
|
||||||
func TestReadPixelsFromVolatileImage(t *testing.T) {
|
func TestReadPixelsFromVolatileImage(t *testing.T) {
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
dst := NewImage(w, h)
|
dst := NewImage(w, h, true /* volatile */)
|
||||||
dst.MakeVolatile()
|
src := NewImage(w, h, false)
|
||||||
src := NewImage(w, h)
|
|
||||||
|
|
||||||
// First, make sure that dst has pixels
|
// First, make sure that dst has pixels
|
||||||
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
|
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
|
||||||
@ -695,8 +694,8 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
|
|||||||
|
|
||||||
func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
|
func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
src := NewImage(w, h)
|
src := NewImage(w, h, false)
|
||||||
dst := NewImage(w, h)
|
dst := NewImage(w, h, false)
|
||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0)
|
vs := quadVertices(w, h, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
@ -713,8 +712,8 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
src := NewImage(w, h)
|
src := NewImage(w, h, false)
|
||||||
dst := NewImage(w, h)
|
dst := NewImage(w, h, false)
|
||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0)
|
vs := quadVertices(w, h, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
@ -728,7 +727,7 @@ func TestExtend(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
orig := NewImage(w, h)
|
orig := NewImage(w, h, false)
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
for j := 0; j < h; j++ {
|
for j := 0; j < h; j++ {
|
||||||
for i := 0; i < w; i++ {
|
for i := 0; i < w; i++ {
|
||||||
@ -760,7 +759,7 @@ func TestExtend(t *testing.T) {
|
|||||||
|
|
||||||
func TestClearPixels(t *testing.T) {
|
func TestClearPixels(t *testing.T) {
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
img := NewImage(w, h)
|
img := NewImage(w, h, false)
|
||||||
img.ReplacePixels(make([]byte, 4*4*4), 0, 0, 4, 4)
|
img.ReplacePixels(make([]byte, 4*4*4), 0, 0, 4, 4)
|
||||||
img.ReplacePixels(make([]byte, 4*4*4), 4, 0, 4, 4)
|
img.ReplacePixels(make([]byte, 4*4*4), 4, 0, 4, 4)
|
||||||
img.ClearPixels(0, 0, 4, 4)
|
img.ClearPixels(0, 0, 4, 4)
|
||||||
@ -772,7 +771,7 @@ func TestClearPixels(t *testing.T) {
|
|||||||
|
|
||||||
func TestFill(t *testing.T) {
|
func TestFill(t *testing.T) {
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
img := NewImage(w, h)
|
img := NewImage(w, h, false)
|
||||||
img.Fill(color.RGBA{0xff, 0, 0, 0xff})
|
img.Fill(color.RGBA{0xff, 0, 0, 0xff})
|
||||||
ResolveStaleImages()
|
ResolveStaleImages()
|
||||||
if err := RestoreIfNeeded(); err != nil {
|
if err := RestoreIfNeeded(); err != nil {
|
||||||
|
@ -150,6 +150,7 @@ type Image struct {
|
|||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
disposed bool
|
disposed bool
|
||||||
|
volatile bool
|
||||||
screen bool
|
screen bool
|
||||||
|
|
||||||
backend *backend
|
backend *backend
|
||||||
@ -165,8 +166,6 @@ type Image struct {
|
|||||||
//
|
//
|
||||||
// ReplacePixels doesn't affect this value since ReplacePixels can be done on shared images.
|
// ReplacePixels doesn't affect this value since ReplacePixels can be done on shared images.
|
||||||
nonUpdatedCount int
|
nonUpdatedCount int
|
||||||
|
|
||||||
neverShared bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) moveTo(dst *Image) {
|
func (i *Image) moveTo(dst *Image) {
|
||||||
@ -201,7 +200,7 @@ func (i *Image) ensureNotShared() {
|
|||||||
sy0 := float32(oy)
|
sy0 := float32(oy)
|
||||||
sx1 := float32(ox + w)
|
sx1 := float32(ox + w)
|
||||||
sy1 := float32(oy + h)
|
sy1 := float32(oy + h)
|
||||||
newImg := restorable.NewImage(w, h)
|
newImg := restorable.NewImage(w, h, i.volatile)
|
||||||
vs := []float32{
|
vs := []float32{
|
||||||
dx0, dy0, sx0, sy0, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
|
dx0, dy0, sx0, sy0, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
|
||||||
dx1, dy0, sx1, sy0, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
|
dx1, dy0, sx1, sy0, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
|
||||||
@ -231,7 +230,7 @@ func (i *Image) makeShared() {
|
|||||||
panic("shareable: makeShared cannot be called on a non-shareable image")
|
panic("shareable: makeShared cannot be called on a non-shareable image")
|
||||||
}
|
}
|
||||||
|
|
||||||
newI := NewImage(i.width, i.height)
|
newI := NewImage(i.width, i.height, i.volatile)
|
||||||
pixels := make([]byte, 4*i.width*i.height)
|
pixels := make([]byte, 4*i.width*i.height)
|
||||||
for y := 0; y < i.height; y++ {
|
for y := 0; y < i.height; y++ {
|
||||||
for x := 0; x < i.width; x++ {
|
for x := 0; x < i.width; x++ {
|
||||||
@ -462,21 +461,12 @@ func (i *Image) dispose(markDisposed bool) {
|
|||||||
theBackends = append(theBackends[:index], theBackends[index+1:]...)
|
theBackends = append(theBackends[:index], theBackends[index+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) IsVolatile() bool {
|
func NewImage(width, height int, volatile bool) *Image {
|
||||||
backendsM.Lock()
|
|
||||||
defer backendsM.Unlock()
|
|
||||||
if i.backend == nil {
|
|
||||||
// Not allocated yet. Only non-volatile images can do lazy allocation so far.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return i.backend.restorable.IsVolatile()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewImage(width, height int) *Image {
|
|
||||||
// Actual allocation is done lazily, and the lock is not needed.
|
// Actual allocation is done lazily, and the lock is not needed.
|
||||||
return &Image{
|
return &Image{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
volatile: volatile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +474,10 @@ func (i *Image) shareable() bool {
|
|||||||
if minSize == 0 || maxSize == 0 {
|
if minSize == 0 || maxSize == 0 {
|
||||||
panic("shareable: minSize or maxSize must be initialized")
|
panic("shareable: minSize or maxSize must be initialized")
|
||||||
}
|
}
|
||||||
if i.neverShared {
|
if i.volatile {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if i.screen {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return i.width <= maxSize && i.height <= maxSize
|
return i.width <= maxSize && i.height <= maxSize
|
||||||
@ -505,7 +498,7 @@ func (i *Image) allocate(shareable bool) {
|
|||||||
|
|
||||||
if !shareable || !i.shareable() {
|
if !shareable || !i.shareable() {
|
||||||
i.backend = &backend{
|
i.backend = &backend{
|
||||||
restorable: restorable.NewImage(i.width, i.height),
|
restorable: restorable.NewImage(i.width, i.height, i.volatile),
|
||||||
}
|
}
|
||||||
runtime.SetFinalizer(i, (*Image).disposeFromFinalizer)
|
runtime.SetFinalizer(i, (*Image).disposeFromFinalizer)
|
||||||
return
|
return
|
||||||
@ -528,7 +521,7 @@ func (i *Image) allocate(shareable bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b := &backend{
|
b := &backend{
|
||||||
restorable: restorable.NewImage(size, size),
|
restorable: restorable.NewImage(size, size, i.volatile),
|
||||||
page: packing.NewPage(size, maxSize),
|
page: packing.NewPage(size, maxSize),
|
||||||
}
|
}
|
||||||
theBackends = append(theBackends, b)
|
theBackends = append(theBackends, b)
|
||||||
@ -542,15 +535,6 @@ func (i *Image) allocate(shareable bool) {
|
|||||||
runtime.SetFinalizer(i, (*Image).disposeFromFinalizer)
|
runtime.SetFinalizer(i, (*Image).disposeFromFinalizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) MakeVolatile() {
|
|
||||||
backendsM.Lock()
|
|
||||||
defer backendsM.Unlock()
|
|
||||||
|
|
||||||
i.ensureNotShared()
|
|
||||||
i.backend.restorable.MakeVolatile()
|
|
||||||
i.neverShared = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) Dump(path string) error {
|
func (i *Image) Dump(path string) error {
|
||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
defer backendsM.Unlock()
|
||||||
@ -561,10 +545,9 @@ func (i *Image) Dump(path string) error {
|
|||||||
func NewScreenFramebufferImage(width, height int) *Image {
|
func NewScreenFramebufferImage(width, height int) *Image {
|
||||||
// Actual allocation is done lazily.
|
// Actual allocation is done lazily.
|
||||||
i := &Image{
|
i := &Image{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
screen: true,
|
screen: true,
|
||||||
neverShared: true,
|
|
||||||
}
|
}
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
@ -66,22 +66,22 @@ const bigSize = 2049
|
|||||||
func TestEnsureNotShared(t *testing.T) {
|
func TestEnsureNotShared(t *testing.T) {
|
||||||
// Create img1 and img2 with this size so that the next images are allocated
|
// Create img1 and img2 with this size so that the next images are allocated
|
||||||
// with non-upper-left location.
|
// with non-upper-left location.
|
||||||
img1 := NewImage(bigSize, 100)
|
img1 := NewImage(bigSize, 100, false)
|
||||||
defer img1.Dispose()
|
defer img1.Dispose()
|
||||||
// Ensure img1's region is allocated.
|
// Ensure img1's region is allocated.
|
||||||
img1.ReplacePixels(make([]byte, 4*bigSize*100))
|
img1.ReplacePixels(make([]byte, 4*bigSize*100))
|
||||||
|
|
||||||
img2 := NewImage(100, bigSize)
|
img2 := NewImage(100, bigSize, false)
|
||||||
defer img2.Dispose()
|
defer img2.Dispose()
|
||||||
img2.ReplacePixels(make([]byte, 4*100*bigSize))
|
img2.ReplacePixels(make([]byte, 4*100*bigSize))
|
||||||
|
|
||||||
const size = 32
|
const size = 32
|
||||||
|
|
||||||
img3 := NewImage(size/2, size/2)
|
img3 := NewImage(size/2, size/2, false)
|
||||||
defer img3.Dispose()
|
defer img3.Dispose()
|
||||||
img3.ReplacePixels(make([]byte, (size/2)*(size/2)*4))
|
img3.ReplacePixels(make([]byte, (size/2)*(size/2)*4))
|
||||||
|
|
||||||
img4 := NewImage(size, size)
|
img4 := NewImage(size, size, false)
|
||||||
defer img4.Dispose()
|
defer img4.Dispose()
|
||||||
|
|
||||||
pix := make([]byte, size*size*4)
|
pix := make([]byte, size*size*4)
|
||||||
@ -133,18 +133,18 @@ func TestEnsureNotShared(t *testing.T) {
|
|||||||
func TestReshared(t *testing.T) {
|
func TestReshared(t *testing.T) {
|
||||||
const size = 16
|
const size = 16
|
||||||
|
|
||||||
img0 := NewImage(size, size)
|
img0 := NewImage(size, size, false)
|
||||||
defer img0.Dispose()
|
defer img0.Dispose()
|
||||||
img0.ReplacePixels(make([]byte, 4*size*size))
|
img0.ReplacePixels(make([]byte, 4*size*size))
|
||||||
|
|
||||||
img1 := NewImage(size, size)
|
img1 := NewImage(size, size, false)
|
||||||
defer img1.Dispose()
|
defer img1.Dispose()
|
||||||
img1.ReplacePixels(make([]byte, 4*size*size))
|
img1.ReplacePixels(make([]byte, 4*size*size))
|
||||||
if got, want := img1.IsSharedForTesting(), true; got != want {
|
if got, want := img1.IsSharedForTesting(), true; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
img2 := NewImage(size, size)
|
img2 := NewImage(size, size, false)
|
||||||
defer img2.Dispose()
|
defer img2.Dispose()
|
||||||
pix := make([]byte, 4*size*size)
|
pix := make([]byte, 4*size*size)
|
||||||
for j := 0; j < size; j++ {
|
for j := 0; j < size; j++ {
|
||||||
@ -157,8 +157,7 @@ func TestReshared(t *testing.T) {
|
|||||||
}
|
}
|
||||||
img2.ReplacePixels(pix)
|
img2.ReplacePixels(pix)
|
||||||
|
|
||||||
img3 := NewImage(size, size)
|
img3 := NewImage(size, size, true /* volatile */)
|
||||||
img3.MakeVolatile()
|
|
||||||
defer img3.Dispose()
|
defer img3.Dispose()
|
||||||
img1.ReplacePixels(make([]byte, 4*size*size))
|
img1.ReplacePixels(make([]byte, 4*size*size))
|
||||||
if got, want := img3.IsSharedForTesting(), false; got != want {
|
if got, want := img3.IsSharedForTesting(), false; got != want {
|
||||||
@ -224,7 +223,7 @@ func TestReshared(t *testing.T) {
|
|||||||
|
|
||||||
func TestExtend(t *testing.T) {
|
func TestExtend(t *testing.T) {
|
||||||
const w0, h0 = 100, 100
|
const w0, h0 = 100, 100
|
||||||
img0 := NewImage(w0, h0)
|
img0 := NewImage(w0, h0, false)
|
||||||
defer img0.Dispose()
|
defer img0.Dispose()
|
||||||
p0 := make([]byte, 4*w0*h0)
|
p0 := make([]byte, 4*w0*h0)
|
||||||
for i := 0; i < w0*h0; i++ {
|
for i := 0; i < w0*h0; i++ {
|
||||||
@ -236,7 +235,7 @@ func TestExtend(t *testing.T) {
|
|||||||
img0.ReplacePixels(p0)
|
img0.ReplacePixels(p0)
|
||||||
|
|
||||||
const w1, h1 = 1025, 100
|
const w1, h1 = 1025, 100
|
||||||
img1 := NewImage(w1, h1)
|
img1 := NewImage(w1, h1, false)
|
||||||
defer img1.Dispose()
|
defer img1.Dispose()
|
||||||
p1 := make([]byte, 4*w1*h1)
|
p1 := make([]byte, 4*w1*h1)
|
||||||
for i := 0; i < w1*h1; i++ {
|
for i := 0; i < w1*h1; i++ {
|
||||||
@ -278,9 +277,9 @@ func TestExtend(t *testing.T) {
|
|||||||
|
|
||||||
func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
||||||
const w, h = 256, 256
|
const w, h = 256, 256
|
||||||
src := NewImage(w, h)
|
src := NewImage(w, h, false)
|
||||||
defer src.Dispose()
|
defer src.Dispose()
|
||||||
dst := NewImage(w, h)
|
dst := NewImage(w, h, false)
|
||||||
defer dst.Dispose()
|
defer dst.Dispose()
|
||||||
|
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
@ -313,9 +312,9 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
|||||||
// Issue #887
|
// Issue #887
|
||||||
func TestSmallImages(t *testing.T) {
|
func TestSmallImages(t *testing.T) {
|
||||||
const w, h = 4, 8
|
const w, h = 4, 8
|
||||||
src := NewImage(w, h)
|
src := NewImage(w, h, false)
|
||||||
defer src.Dispose()
|
defer src.Dispose()
|
||||||
dst := NewImage(w, h)
|
dst := NewImage(w, h, false)
|
||||||
defer dst.Dispose()
|
defer dst.Dispose()
|
||||||
|
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
@ -347,9 +346,9 @@ func TestSmallImages(t *testing.T) {
|
|||||||
// Issue #887
|
// Issue #887
|
||||||
func TestLongImages(t *testing.T) {
|
func TestLongImages(t *testing.T) {
|
||||||
const w, h = 1, 6
|
const w, h = 1, 6
|
||||||
src := NewImage(w, h)
|
src := NewImage(w, h, false)
|
||||||
defer src.Dispose()
|
defer src.Dispose()
|
||||||
dst := NewImage(256, 256)
|
dst := NewImage(256, 256, false)
|
||||||
defer dst.Dispose()
|
defer dst.Dispose()
|
||||||
|
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
@ -382,10 +381,10 @@ func TestLongImages(t *testing.T) {
|
|||||||
func TestDisposeImmediately(t *testing.T) {
|
func TestDisposeImmediately(t *testing.T) {
|
||||||
// This tests restorable.Image.ClearPixels is called but ReplacePixels is not called.
|
// This tests restorable.Image.ClearPixels is called but ReplacePixels is not called.
|
||||||
|
|
||||||
img0 := NewImage(16, 16)
|
img0 := NewImage(16, 16, false)
|
||||||
img0.EnsureNotSharedForTesting()
|
img0.EnsureNotSharedForTesting()
|
||||||
|
|
||||||
img1 := NewImage(16, 16)
|
img1 := NewImage(16, 16, false)
|
||||||
img1.EnsureNotSharedForTesting()
|
img1.EnsureNotSharedForTesting()
|
||||||
|
|
||||||
// img0 and img1 should share the same backend in 99.9999% possibility.
|
// img0 and img1 should share the same backend in 99.9999% possibility.
|
||||||
|
31
mipmap.go
31
mipmap.go
@ -29,18 +29,20 @@ import (
|
|||||||
type levelToImage map[int]*shareable.Image
|
type levelToImage map[int]*shareable.Image
|
||||||
|
|
||||||
type mipmap struct {
|
type mipmap struct {
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
orig *shareable.Image
|
volatile bool
|
||||||
imgs map[image.Rectangle]levelToImage
|
orig *shareable.Image
|
||||||
|
imgs map[image.Rectangle]levelToImage
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMipmap(width, height int) *mipmap {
|
func newMipmap(width, height int, volatile bool) *mipmap {
|
||||||
return &mipmap{
|
return &mipmap{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
orig: shareable.NewImage(width, height),
|
volatile: volatile,
|
||||||
imgs: map[image.Rectangle]levelToImage{},
|
orig: shareable.NewImage(width, height, volatile),
|
||||||
|
imgs: map[image.Rectangle]levelToImage{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,11 +55,6 @@ func newScreenFramebufferMipmap(width, height int) *mipmap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) makeVolatile() {
|
|
||||||
m.orig.MakeVolatile()
|
|
||||||
m.disposeMipmaps()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mipmap) dump(name string) error {
|
func (m *mipmap) dump(name string) error {
|
||||||
return m.orig.Dump(name)
|
return m.orig.Dump(name)
|
||||||
}
|
}
|
||||||
@ -176,7 +173,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
|||||||
panic("ebiten: level must be non-zero at level")
|
panic("ebiten: level must be non-zero at level")
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.orig.IsVolatile() {
|
if m.volatile {
|
||||||
panic("ebiten: mipmap images for a volatile image is not implemented yet")
|
panic("ebiten: mipmap images for a volatile image is not implemented yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +226,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
|||||||
imgs[level] = nil
|
imgs[level] = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s := shareable.NewImage(w2, h2)
|
s := shareable.NewImage(w2, h2, m.volatile)
|
||||||
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressClampToZero)
|
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressClampToZero)
|
||||||
imgs[level] = s
|
imgs[level] = s
|
||||||
|
|
||||||
@ -325,7 +322,7 @@ func (m *mipmap) mipmapLevel(geom *GeoM, width, height int, filter driver.Filter
|
|||||||
if filter != driver.FilterLinear {
|
if filter != driver.FilterLinear {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if m.orig.IsVolatile() {
|
if m.volatile {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ func (c *uiContext) SetSize(screenWidth, screenHeight int, screenScale float64)
|
|||||||
if c.offscreen != nil {
|
if c.offscreen != nil {
|
||||||
_ = c.offscreen.Dispose()
|
_ = c.offscreen.Dispose()
|
||||||
}
|
}
|
||||||
c.offscreen, _ = NewImage(screenWidth, screenHeight, FilterDefault)
|
|
||||||
c.offscreen.makeVolatile()
|
c.offscreen = newImage(screenWidth, screenHeight, FilterDefault, true)
|
||||||
|
|
||||||
// Round up the screensize not to cause glitches e.g. on Xperia (#622)
|
// Round up the screensize not to cause glitches e.g. on Xperia (#622)
|
||||||
w := int(math.Ceil(float64(screenWidth) * screenScale))
|
w := int(math.Ceil(float64(screenWidth) * screenScale))
|
||||||
|
Loading…
Reference in New Issue
Block a user