shareable: Use CopyPixels

CopyPixels is basically Pixels and ReplacePixels, but executed
lazily while Pixels reads pixels from GPU immediately. Thanks to
this, restorable.Image no longer need to keep pixel data if not
needed.
This commit is contained in:
Hajime Hoshi 2019-02-03 02:39:36 +09:00
parent b89602d900
commit d3d56c076d
5 changed files with 75 additions and 12 deletions

View File

@ -316,6 +316,45 @@ func (c *replacePixelsCommand) CanMerge(dst, src *Image, color *affine.ColorM, m
return false return false
} }
type copyPixelsCommand struct {
dst *Image
src *Image
}
func (c *copyPixelsCommand) String() string {
return fmt.Sprintf("copy-pixels: dst: %p <- src: %p", c.dst, c.src)
}
func (c *copyPixelsCommand) Exec(indexOffset int) error {
p, err := c.src.image.Pixels()
if err != nil {
return err
}
if c.dst.width < c.src.width || c.dst.height < c.src.height {
return fmt.Errorf("graphicscommand: the destination size (%d, %d) must include the source size (%d, %d)", c.dst.width, c.dst.height, c.src.width, c.src.height)
}
c.dst.image.ReplacePixels(p, 0, 0, c.src.width, c.src.height)
return nil
}
func (c *copyPixelsCommand) NumVertices() int {
return 0
}
func (c *copyPixelsCommand) NumIndices() int {
return 0
}
func (c *copyPixelsCommand) AddNumVertices(n int) {
}
func (c *copyPixelsCommand) AddNumIndices(n int) {
}
func (c *copyPixelsCommand) CanMerge(dst, src *Image, color *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter, address graphics.Address) bool {
return false
}
type pixelsCommand struct { type pixelsCommand struct {
result []byte result []byte
img *Image img *Image

View File

@ -131,6 +131,24 @@ func (i *Image) ReplacePixels(p []byte, x, y, width, height int) {
i.lastCommand = lastCommandReplacePixels i.lastCommand = lastCommandReplacePixels
} }
// CopyPixels is basically same as Pixels and ReplacePixels, but reading pixels from GPU is done lazily.
func (i *Image) CopyPixels(src *Image) {
if i.lastCommand == lastCommandDrawImage {
if i.width != src.width || i.height != src.height {
panic("graphicscommand: Copy for a part after DrawImage is forbidden")
}
}
c := &copyPixelsCommand{
dst: i,
src: src,
}
theCommandQueue.Enqueue(c)
// The execution is basically same as replacing pixels.
i.lastCommand = lastCommandReplacePixels
}
func (i *Image) IsInvalidated() bool { func (i *Image) IsInvalidated() bool {
// i.image can be nil before initializing. // i.image can be nil before initializing.
if i.image == nil { if i.image == nil {

View File

@ -168,6 +168,17 @@ func (i *Image) makeStale() {
// the former image can be restored from the latest state of the latter image. // the former image can be restored from the latest state of the latter image.
} }
func (i *Image) CopyPixels(src *Image) {
// TODO: Avoid making other images stale if possible. (#514)
// For this purpuse, images should remember which part of that is used for DrawImage.
theImages.makeStaleIfDependingOn(i)
i.image.CopyPixels(src.image)
// As pixels should not be obtained here, making the image stale is inevitable.
i.makeStale()
}
// ReplacePixels replaces the image pixels with the given pixels slice. // ReplacePixels replaces the image pixels with the given pixels slice.
// //
// If pixels is nil, ReplacePixels clears the specified reagion. // If pixels is nil, ReplacePixels clears the specified reagion.
@ -189,13 +200,10 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
} }
i.image.ReplacePixels(pixels, x, y, width, height) i.image.ReplacePixels(pixels, x, y, width, height)
// TODO: We wanted to skip copying pixels, but this can cause reading-pixels before the driver is initialized. if !IsRestoringEnabled() {
// For example, Pixels() is called at shareable package when enlarging the shareable images. i.makeStale()
// return
// if !IsRestoringEnabled() { }
// i.makeStale()
// return
// }
if x == 0 && y == 0 && width == w && height == h { if x == 0 && y == 0 && width == w && height == h {
if pixels != nil { if pixels != nil {

View File

@ -63,13 +63,9 @@ func (b *backend) TryAlloc(width, height int) (*packing.Node, bool) {
s := b.page.Size() s := b.page.Size()
newImg := restorable.NewImage(s, s, false) newImg := restorable.NewImage(s, s, false)
oldImg := b.restorable oldImg := b.restorable
w, h := oldImg.Size()
// Do not use DrawImage here. ReplacePixels will be called on a part of newImg later, and it looked like // Do not use DrawImage here. ReplacePixels will be called on a part of newImg later, and it looked like
// ReplacePixels on a part of image deletes other region that are rendered by DrawImage (#593, #758). // ReplacePixels on a part of image deletes other region that are rendered by DrawImage (#593, #758).
// newImg.CopyPixels(oldImg)
// Pixels() returns immediately as long as only oldImg.ReplacePixels is called.
pix := oldImg.Pixels()
newImg.ReplacePixels(pix, 0, 0, w, h)
oldImg.Dispose() oldImg.Dispose()
b.restorable = newImg b.restorable = newImg

View File

@ -293,3 +293,5 @@ func TestReplacePixelsAfterDrawImage(t *testing.T) {
} }
} }
} }
// TODO: Add tests to extend shareable image out of the main loop