mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-26 02:42:02 +01:00
internal/restorable: use a special shader to clear an image
This reduces one extra internal texture.
This commit is contained in:
parent
c76201090a
commit
e5bb89aa35
@ -125,38 +125,6 @@ type Image struct {
|
|||||||
staleRegions []image.Rectangle
|
staleRegions []image.Rectangle
|
||||||
|
|
||||||
imageType ImageType
|
imageType ImageType
|
||||||
|
|
||||||
// priority indicates whether the image is restored in high priority when context-lost happens.
|
|
||||||
priority bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var whiteImage *Image
|
|
||||||
|
|
||||||
func ensureWhiteImage() *Image {
|
|
||||||
if whiteImage != nil {
|
|
||||||
return whiteImage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the white image lazily. Some functions like needsRestoring might not work at the initial phase.
|
|
||||||
|
|
||||||
// w and h are the white image's size. They indicate the 1x1 image with 1px padding around.
|
|
||||||
const w, h = 3, 3
|
|
||||||
whiteImage = &Image{
|
|
||||||
image: graphicscommand.NewImage(w, h, false),
|
|
||||||
width: w,
|
|
||||||
height: h,
|
|
||||||
priority: true,
|
|
||||||
}
|
|
||||||
pix := make([]byte, 4*w*h)
|
|
||||||
for i := range pix {
|
|
||||||
pix[i] = 0xff
|
|
||||||
}
|
|
||||||
|
|
||||||
// As whiteImage is the source at clearImage, initialize this with WritePixels, not clearImage.
|
|
||||||
// This operation is also important when restoring whiteImage.
|
|
||||||
whiteImage.WritePixels(pix, 0, 0, w, h)
|
|
||||||
theImages.add(whiteImage)
|
|
||||||
return whiteImage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage creates a white image with the given size.
|
// NewImage creates a white image with the given size.
|
||||||
@ -211,6 +179,14 @@ func (i *Image) Extend(width, height int) *Image {
|
|||||||
|
|
||||||
// quadVertices returns vertices to render a quad. These values are passed to graphicscommand.Image.
|
// quadVertices returns vertices to render a quad. These values are passed to graphicscommand.Image.
|
||||||
func quadVertices(src *Image, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) []float32 {
|
func quadVertices(src *Image, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) []float32 {
|
||||||
|
if src == nil {
|
||||||
|
return []float32{
|
||||||
|
dx0, dy0, 0, 0, cr, cg, cb, ca,
|
||||||
|
dx1, dy0, 0, 0, cr, cg, cb, ca,
|
||||||
|
dx0, dy1, 0, 0, cr, cg, cb, ca,
|
||||||
|
dx1, dy1, 0, 0, cr, cg, cb, ca,
|
||||||
|
}
|
||||||
|
}
|
||||||
sw, sh := src.InternalSize()
|
sw, sh := src.InternalSize()
|
||||||
swf, shf := float32(sw), float32(sh)
|
swf, shf := float32(sw), float32(sh)
|
||||||
return []float32{
|
return []float32{
|
||||||
@ -222,19 +198,11 @@ func quadVertices(src *Image, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb
|
|||||||
}
|
}
|
||||||
|
|
||||||
func clearImage(i *graphicscommand.Image) {
|
func clearImage(i *graphicscommand.Image) {
|
||||||
whiteImage := ensureWhiteImage()
|
|
||||||
|
|
||||||
if i == whiteImage.image {
|
|
||||||
panic("restorable: fillImage cannot be called on whiteImage")
|
|
||||||
}
|
|
||||||
|
|
||||||
// This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some
|
// This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some
|
||||||
// devices.
|
// devices.
|
||||||
dw, dh := i.InternalSize()
|
dw, dh := i.InternalSize()
|
||||||
sw, sh := whiteImage.width, whiteImage.height
|
vs := quadVertices(nil, 0, 0, float32(dw), float32(dh), 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
vs := quadVertices(whiteImage, 0, 0, float32(dw), float32(dh), 1, 1, float32(sw-1), float32(sh-1), 0, 0, 0, 0)
|
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
srcs := [graphics.ShaderImageCount]*graphicscommand.Image{whiteImage.image}
|
|
||||||
var offsets [graphics.ShaderImageCount - 1][2]float32
|
var offsets [graphics.ShaderImageCount - 1][2]float32
|
||||||
dstRegion := graphicsdriver.Region{
|
dstRegion := graphicsdriver.Region{
|
||||||
X: 0,
|
X: 0,
|
||||||
@ -242,7 +210,7 @@ func clearImage(i *graphicscommand.Image) {
|
|||||||
Width: float32(dw),
|
Width: float32(dw),
|
||||||
Height: float32(dh),
|
Height: float32(dh),
|
||||||
}
|
}
|
||||||
i.DrawTriangles(srcs, offsets, vs, is, graphicsdriver.BlendClear, dstRegion, graphicsdriver.Region{}, NearestFilterShader.shader, nil, false)
|
i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, offsets, vs, is, graphicsdriver.BlendClear, dstRegion, graphicsdriver.Region{}, clearShader.shader, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasePixelsForTesting returns the image's basePixels for testing.
|
// BasePixelsForTesting returns the image's basePixels for testing.
|
||||||
@ -364,9 +332,6 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
|
|||||||
// 6: Color B
|
// 6: Color B
|
||||||
// 7: Color Y
|
// 7: Color Y
|
||||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms []uint32, evenOdd bool) {
|
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms []uint32, evenOdd bool) {
|
||||||
if i.priority {
|
|
||||||
panic("restorable: DrawTriangles cannot be called on a priority image")
|
|
||||||
}
|
|
||||||
if len(vertices) == 0 {
|
if len(vertices) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -606,11 +571,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.
|
||||||
if i != ensureWhiteImage() {
|
clearImage(gimg)
|
||||||
// As clearImage uses whiteImage, clearImage cannot be called on whiteImage.
|
|
||||||
// It is OK to skip this since whiteImage has its entire pixel information.
|
|
||||||
clearImage(gimg)
|
|
||||||
}
|
|
||||||
i.basePixels.Apply(gimg)
|
i.basePixels.Apply(gimg)
|
||||||
|
|
||||||
for _, c := range i.drawTrianglesHistory {
|
for _, c := range i.drawTrianglesHistory {
|
||||||
|
@ -222,9 +222,7 @@ func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
}
|
}
|
||||||
images := map[*Image]struct{}{}
|
images := map[*Image]struct{}{}
|
||||||
for i := range i.images {
|
for i := range i.images {
|
||||||
if !i.priority {
|
images[i] = struct{}{}
|
||||||
images[i] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
edges := map[edge]struct{}{}
|
edges := map[edge]struct{}{}
|
||||||
for t := range images {
|
for t := range images {
|
||||||
@ -233,12 +231,7 @@ func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sorted := []*Image{}
|
var sorted []*Image
|
||||||
for i := range i.images {
|
|
||||||
if i.priority {
|
|
||||||
sorted = append(sorted, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for len(images) > 0 {
|
for len(images) > 0 {
|
||||||
// current represents images that have no incoming edges.
|
// current represents images that have no incoming edges.
|
||||||
current := map[*Image]struct{}{}
|
current := map[*Image]struct{}{}
|
||||||
|
@ -53,11 +53,12 @@ func (s *Shader) restore() {
|
|||||||
var (
|
var (
|
||||||
NearestFilterShader *Shader
|
NearestFilterShader *Shader
|
||||||
LinearFilterShader *Shader
|
LinearFilterShader *Shader
|
||||||
|
clearShader *Shader
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var wg errgroup.Group
|
var wg errgroup.Group
|
||||||
var nearestIR, linearIR *shaderir.Program
|
var nearestIR, linearIR, clearIR *shaderir.Program
|
||||||
wg.Go(func() error {
|
wg.Go(func() error {
|
||||||
ir, err := graphics.CompileShader([]byte(builtinshader.Shader(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false)))
|
ir, err := graphics.CompileShader([]byte(builtinshader.Shader(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -74,9 +75,22 @@ func init() {
|
|||||||
linearIR = ir
|
linearIR = ir
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
wg.Go(func() error {
|
||||||
|
ir, err := graphics.CompileShader([]byte(`package main
|
||||||
|
|
||||||
|
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||||
|
return vec4(0)
|
||||||
|
}`))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("restorable: compiling the clear shader failed: %w", err)
|
||||||
|
}
|
||||||
|
clearIR = ir
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err := wg.Wait(); err != nil {
|
if err := wg.Wait(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
NearestFilterShader = NewShader(nearestIR)
|
NearestFilterShader = NewShader(nearestIR)
|
||||||
LinearFilterShader = NewShader(linearIR)
|
LinearFilterShader = NewShader(linearIR)
|
||||||
|
clearShader = NewShader(clearIR)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user