internal/restorable: optimize WritePixels

This adds (*Image).makeStaleIfDependingOnWithRegion to reduce the
possibility of making an image stale.
This commit is contained in:
Hajime Hoshi 2024-09-08 01:04:51 +09:00
parent af9bd6a282
commit 6a51e5b003
2 changed files with 43 additions and 3 deletions

View File

@ -277,9 +277,7 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl
panic(fmt.Sprintf("restorable: out of range %v", region)) panic(fmt.Sprintf("restorable: out of range %v", region))
} }
// TODO: Avoid making other images stale if possible. (#514) theImages.makeStaleIfDependingOnAtRegion(i, region)
// For this purpose, images should remember which part of that is used for DrawTriangles.
theImages.makeStaleIfDependingOn(i)
if pixels != nil { if pixels != nil {
i.image.WritePixels(pixels, region) i.image.WritePixels(pixels, region)
@ -343,6 +341,9 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice
if len(vertices) == 0 { if len(vertices) == 0 {
return return
} }
// makeStaleIfDependingOnAtRegion is not available here.
// This might create cyclic dependency.
theImages.makeStaleIfDependingOn(i) theImages.makeStaleIfDependingOn(i)
// TODO: Add tests to confirm this logic. // TODO: Add tests to confirm this logic.
@ -510,6 +511,17 @@ func (i *Image) makeStaleIfDependingOn(src *Image) {
} }
} }
// makeStaleIfDependingOnAtRegion makes the image stale if the image depends on src at srcRegion.
func (i *Image) makeStaleIfDependingOnAtRegion(src *Image, srcRegion image.Rectangle) {
if i.stale {
return
}
if i.dependsOnAtRegion(src, srcRegion) {
// There is no new region to make stale.
i.makeStale(image.Rectangle{})
}
}
// makeStaleIfDependingOnShader makes the image stale if the image depends on shader. // makeStaleIfDependingOnShader makes the image stale if the image depends on shader.
func (i *Image) makeStaleIfDependingOnShader(shader *Shader) { func (i *Image) makeStaleIfDependingOnShader(shader *Shader) {
if i.stale { if i.stale {
@ -603,6 +615,21 @@ func (i *Image) dependsOn(src *Image) bool {
return false return false
} }
// dependsOnAtRegion reports whether the image depends on src at srcRegion.
func (i *Image) dependsOnAtRegion(src *Image, srcRegion image.Rectangle) bool {
for _, c := range i.drawTrianglesHistory {
for i, img := range c.srcImages {
if img != src {
continue
}
if c.srcRegions[i].Overlaps(srcRegion) {
return true
}
}
}
return false
}
// dependsOnShader reports whether the image depends on shader. // dependsOnShader reports whether the image depends on shader.
func (i *Image) dependsOnShader(shader *Shader) bool { func (i *Image) dependsOnShader(shader *Shader) bool {
for _, c := range i.drawTrianglesHistory { for _, c := range i.drawTrianglesHistory {

View File

@ -177,6 +177,19 @@ func (i *images) makeStaleIfDependingOn(src *Image) {
} }
} }
// makeStaleIfDependingOnAtRegion makes all the images stale that depend on src at srcRegion.
//
// When src is modified, all images depending on src can't be restored with src at srcRegion.
// makeStaleIfDependingOnAtRegion is called in such situation.
func (i *images) makeStaleIfDependingOnAtRegion(src *Image, srcRegion image.Rectangle) {
if src == nil {
panic("restorable: src must not be nil at makeStaleIfDependingOnAtRegion")
}
for img := range i.images {
img.makeStaleIfDependingOnAtRegion(src, srcRegion)
}
}
// makeStaleIfDependingOn makes all the images stale that depend on shader. // makeStaleIfDependingOn makes all the images stale that depend on shader.
func (i *images) makeStaleIfDependingOnShader(shader *Shader) { func (i *images) makeStaleIfDependingOnShader(shader *Shader) {
if shader == nil { if shader == nil {