internal/buffered: remove redundant pixel data if possible

This commit is contained in:
Hajime Hoshi 2023-02-26 01:31:11 +09:00
parent 48147cab05
commit 110ba5403d
6 changed files with 36 additions and 26 deletions

View File

@ -522,7 +522,7 @@ func (i *Image) writePixels(pix []byte, x, y, width, height int) {
i.backend.restorable.WritePixels(pixb, x, y, pw, ph)
}
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte) error {
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, x, y, width, height int) error {
backendsM.Lock()
defer backendsM.Unlock()
@ -538,8 +538,10 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
}
ps := i.paddingSize()
ox, oy, w, h := i.regionWithPadding()
return i.backend.restorable.ReadPixels(graphicsDriver, pixels, ox+ps, oy+ps, w-ps*2, h-ps*2)
ox, oy, _, _ := i.regionWithPadding()
x += ox + ps
y += oy + ps
return i.backend.restorable.ReadPixels(graphicsDriver, pixels, x, y, width, height)
}
// MarkDisposed marks the image as disposed. The actual operation is deferred.

View File

@ -125,7 +125,7 @@ func TestEnsureIsolated(t *testing.T) {
}
pix = make([]byte, 4*size*size)
if err := img4.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := img4.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil {
t.Fatal(err)
}
for j := 0; j < size; j++ {
@ -217,7 +217,7 @@ func TestReputOnAtlas(t *testing.T) {
}
pix = make([]byte, 4*size*size)
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil {
t.Fatal(err)
}
for j := 0; j < size; j++ {
@ -241,7 +241,7 @@ func TestReputOnAtlas(t *testing.T) {
}
pix = make([]byte, 4*size*size)
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil {
t.Fatal(err)
}
for j := 0; j < size; j++ {
@ -329,7 +329,7 @@ func TestExtend(t *testing.T) {
img1.WritePixels(p1, 0, 0, w1, h1)
pix0 := make([]byte, 4*w0*h0)
if err := img0.ReadPixels(ui.GraphicsDriverForTesting(), pix0); err != nil {
if err := img0.ReadPixels(ui.GraphicsDriverForTesting(), pix0, 0, 0, w0, h0); err != nil {
t.Fatal(err)
}
for j := 0; j < h0; j++ {
@ -348,7 +348,7 @@ func TestExtend(t *testing.T) {
}
pix1 := make([]byte, 4*w1*h1)
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix1); err != nil {
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix1, 0, 0, w1, h1); err != nil {
t.Fatal(err)
}
for j := 0; j < h1; j++ {
@ -395,7 +395,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) {
dst.WritePixels(pix, 0, 0, w, h)
pix = make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
@ -442,7 +442,7 @@ func TestSmallImages(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
pix = make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
@ -490,7 +490,7 @@ func TestLongImages(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
pix = make([]byte, 4*dstW*dstH)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, dstW, dstH); err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
@ -717,7 +717,7 @@ func TestImageWritePixelsModify(t *testing.T) {
// Check the pixels are the original ones.
pix = make([]byte, 4*size*size)
if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil {
t.Fatal(err)
}
for j := 0; j < size; j++ {

View File

@ -48,7 +48,7 @@ func TestShaderFillTwice(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, s1, nil, false)
pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(g, pix); err != nil {
if err := dst.ReadPixels(g, pix, 0, 0, w, h); err != nil {
t.Error(err)
}
if got, want := (color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}); got != want {
@ -80,7 +80,7 @@ func TestImageDrawTwice(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil {
t.Error(err)
}
if got, want := (color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}); got != want {

View File

@ -21,6 +21,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/restorable"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
@ -29,6 +30,7 @@ type Image struct {
width int
height int
// pixels is valid only when restorable.AlwaysReadPixelsFromGPU() returns true.
pixels []byte
}
@ -99,9 +101,17 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
return nil
}
// If restorable.AlwaysReadPixelsFromGPU() returns false, the pixel data is cached in the restorable package.
if !restorable.AlwaysReadPixelsFromGPU() {
if err := i.img.ReadPixels(graphicsDriver, pixels, x, y, width, height); err != nil {
return err
}
return nil
}
if i.pixels == nil {
pix := make([]byte, 4*i.width*i.height)
if err := i.img.ReadPixels(graphicsDriver, pix); err != nil {
if err := i.img.ReadPixels(graphicsDriver, pix, 0, 0, i.width, i.height); err != nil {
return err
}
i.pixels = pix

View File

@ -121,7 +121,7 @@ type Image struct {
// staleRegions indicates the regions to restore.
// staleRegions is valid only when stale is true.
// staleRegions is not used when alwaysReadPixelsFromGPU() returns true.
// staleRegions is not used when AlwaysReadPixelsFromGPU() returns true.
staleRegions []image.Rectangle
imageType ImageType
@ -262,8 +262,8 @@ func (i *Image) BasePixelsForTesting() *Pixels {
func (i *Image) makeStale(rect image.Rectangle) {
i.stale = true
// If ReadPixels always reads pixels from GPU, staleRegions are never used.
if alwaysReadPixelsFromGPU() {
// If ReadPixels Always reads pixels from GPU, staleRegions are never used.
if AlwaysReadPixelsFromGPU() {
return
}
@ -414,8 +414,8 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageCount]*Imag
if i.stale || !i.needsRestoring() {
panic("restorable: an image must not be stale or need restoring at appendDrawTrianglesHistory")
}
if alwaysReadPixelsFromGPU() {
panic("restorable: appendDrawTrianglesHistory must not be called when alwaysReadPixelsFromGPU() returns true")
if AlwaysReadPixelsFromGPU() {
panic("restorable: appendDrawTrianglesHistory must not be called when AlwaysReadPixelsFromGPU() returns true")
}
// TODO: Would it be possible to merge draw image history items?
@ -461,7 +461,7 @@ func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics
}
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, x, y, width, height int) error {
if alwaysReadPixelsFromGPU() {
if AlwaysReadPixelsFromGPU() {
if err := i.image.ReadPixels(graphicsDriver, pixels, x, y, width, height); err != nil {
return err
}

View File

@ -16,7 +16,6 @@ package restorable
import (
"runtime"
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/debug"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
@ -33,10 +32,9 @@ func needsRestoring() bool {
return forceRestoring || needsRestoringByGraphicsDriver
}
// alwaysReadPixelsFromGPU reports whether ReadPixels alwasy reads pixels from GPU or not.
// This is true for low-end machines like 32bit architecture without much memory.
func alwaysReadPixelsFromGPU() bool {
return !needsRestoring() && unsafe.Sizeof(uintptr(0)) < 8
// AlwaysReadPixelsFromGPU reports whether ReadPixels always reads pixels from GPU or not.
func AlwaysReadPixelsFromGPU() bool {
return !needsRestoring()
}
// EnableRestoringForTesting forces to enable restoring for testing.