internal/restorable: use clearImage to clear a region

This is an improvement for https://github.com/hajimehoshi/ebiten/issues/2676#issuecomment-1650325027

Updates #2676
This commit is contained in:
Hajime Hoshi 2023-08-31 23:31:42 +09:00
parent e082ea73dc
commit d9757138a7
2 changed files with 18 additions and 55 deletions

View File

@ -102,37 +102,6 @@ const (
ImageTypeVolatile ImageTypeVolatile
) )
// emptyPixels holds a byte slice used for ClearPixels.
// All the elements of the slice must be 0 and must not be modified.
//
// emptyPixels resets its slice if this is not used for a while.
type emptyPixels struct {
s []byte
last int
count int
}
var theEmptyPixels emptyPixels
func (e *emptyPixels) alloc(size int) []byte {
if cap(e.s) < size {
n := 16
for n < size {
n <<= 1
}
e.s = make([]byte, n)
}
e.last = e.count
return e.s[:size]
}
func (e *emptyPixels) endFrame() {
e.count++
if e.count-e.last >= 60 {
e.s = nil
}
}
// Image represents an image that can be restored when GL context is lost. // Image represents an image that can be restored when GL context is lost.
type Image struct { type Image struct {
image *graphicscommand.Image image *graphicscommand.Image
@ -170,7 +139,7 @@ type Image struct {
imageType ImageType imageType ImageType
} }
// NewImage creates a white image with the given size. // NewImage creates an emtpy image with the given size.
// //
// The returned image is cleared. // The returned image is cleared.
// //
@ -186,7 +155,11 @@ func NewImage(width, height int, imageType ImageType) *Image {
height: height, height: height,
imageType: imageType, imageType: imageType,
} }
clearImage(i.image)
// This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some
// devices.
iw, ih := i.image.InternalSize()
clearImage(i.image, image.Rect(0, 0, iw, ih))
theImages.add(i) theImages.add(i)
return i return i
} }
@ -229,17 +202,14 @@ func quadVertices(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32
} }
} }
func clearImage(i *graphicscommand.Image) { func clearImage(i *graphicscommand.Image, region image.Rectangle) {
// This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some vs := quadVertices(float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0)
// devices.
dw, dh := i.InternalSize()
vs := quadVertices(0, 0, float32(dw), float32(dh), 0, 0, 0, 0, 0, 0, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
dstRegion := graphicsdriver.Region{ dstRegion := graphicsdriver.Region{
X: 0, X: float32(region.Min.X),
Y: 0, Y: float32(region.Min.Y),
Width: float32(dw), Width: float32(region.Dx()),
Height: float32(dh), Height: float32(region.Dy()),
} }
i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, dstRegion, [graphics.ShaderImageCount]graphicsdriver.Region{}, clearShader.shader, nil, false) i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, dstRegion, [graphics.ShaderImageCount]graphicsdriver.Region{}, clearShader.shader, nil, false)
} }
@ -309,10 +279,7 @@ func (i *Image) WritePixels(pixels []byte, region image.Rectangle) {
if pixels != nil { if pixels != nil {
i.image.WritePixels(pixels, region) i.image.WritePixels(pixels, region)
} else { } else {
// TODO: When pixels == nil, we don't have to care the pixel state there. In such cases, the image clearImage(i.image, region)
// accepts only WritePixels and not Fill or DrawTriangles.
// TODO: Separate Image struct into two: images for WritePixels-only, and the others.
i.image.WritePixels(theEmptyPixels.alloc(4*region.Dx()*region.Dy()), region)
} }
// Even if the image is already stale, call makeStale to extend the stale region. // Even if the image is already stale, call makeStale to extend the stale region.
@ -623,7 +590,8 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
return nil return nil
case ImageTypeVolatile: case ImageTypeVolatile:
i.image = graphicscommand.NewImage(w, h, false) i.image = graphicscommand.NewImage(w, h, false)
clearImage(i.image) iw, ih := i.image.InternalSize()
clearImage(i.image, image.Rect(0, 0, iw, ih))
return nil return nil
} }
@ -633,7 +601,8 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
gimg := graphicscommand.NewImage(w, h, false) gimg := graphicscommand.NewImage(w, h, false)
// Clear the image explicitly. // Clear the image explicitly.
clearImage(gimg) iw, ih := gimg.InternalSize()
clearImage(gimg, image.Rect(0, 0, iw, ih))
i.basePixels.Apply(gimg) i.basePixels.Apply(gimg)

View File

@ -65,13 +65,7 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) e
} }
graphicscommand.LogImagesInfo(imgs) graphicscommand.LogImagesInfo(imgs)
} }
return resolveStaleImages(graphicsDriver, true, swapBuffersForGL)
if err := resolveStaleImages(graphicsDriver, true, swapBuffersForGL); err != nil {
return err
}
theEmptyPixels.endFrame()
return nil
} }
// resolveStaleImages flushes the queued draw commands and resolves all stale images. // resolveStaleImages flushes the queued draw commands and resolves all stale images.