mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
internal/restorable: optimize removeDuplicatedRegions from O((n+m)^2) to O(n*m + m^2) (#2631)
This is achieved by replacing the function by one that only adds a single new region, and only considers duplicates between the previously existing region and the one newly added one, thereby removing previously redundant checking of each previously existing region against each other. This speeds up AAAAXY loading on a Moto G7 Play from 52.27 seconds to 8.15 seconds. Closes #2626
This commit is contained in:
parent
ce71c31a27
commit
cc24796270
@ -18,6 +18,6 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RemoveDuplicatedRegions(regions []image.Rectangle) int {
|
func AppendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) {
|
||||||
return removeDuplicatedRegions(regions)
|
appendRegionRemovingDuplicates(regions, region)
|
||||||
}
|
}
|
||||||
|
@ -240,26 +240,26 @@ func (i *Image) makeStale(rect image.Rectangle) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
origNum := len(i.staleRegions)
|
var addedRegions []image.Rectangle
|
||||||
i.staleRegions = i.appendRegionsForDrawTriangles(i.staleRegions)
|
i.appendRegionsForDrawTriangles(&addedRegions)
|
||||||
if !rect.Empty() {
|
if !rect.Empty() {
|
||||||
i.staleRegions = append(i.staleRegions, rect)
|
appendRegionRemovingDuplicates(&addedRegions, rect)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rect := range addedRegions {
|
||||||
|
appendRegionRemovingDuplicates(&i.staleRegions, rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
i.clearDrawTrianglesHistory()
|
i.clearDrawTrianglesHistory()
|
||||||
|
|
||||||
// Clear pixels to save memory.
|
// Clear pixels to save memory.
|
||||||
for _, r := range i.staleRegions[origNum:] {
|
for _, r := range addedRegions {
|
||||||
if r.Empty() {
|
if r.Empty() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
i.basePixels.Clear(r.Min.X, r.Min.Y, r.Dx(), r.Dy())
|
i.basePixels.Clear(r.Min.X, r.Min.Y, r.Dx(), r.Dy())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove duplicated regions to avoid unnecessary reading pixels from GPU.
|
|
||||||
n := removeDuplicatedRegions(i.staleRegions)
|
|
||||||
i.staleRegions = i.staleRegions[:n]
|
|
||||||
|
|
||||||
// Don't have to call makeStale recursively here.
|
// Don't have to call makeStale recursively here.
|
||||||
// Restoring is done after topological sorting is done.
|
// Restoring is done after topological sorting is done.
|
||||||
// If an image depends on another stale image, this means that
|
// If an image depends on another stale image, this means that
|
||||||
@ -483,12 +483,11 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error
|
|||||||
if i.stale {
|
if i.stale {
|
||||||
rs = i.staleRegions
|
rs = i.staleRegions
|
||||||
} else {
|
} else {
|
||||||
i.regionsCache = i.appendRegionsForDrawTriangles(i.regionsCache)
|
i.appendRegionsForDrawTriangles(&i.regionsCache)
|
||||||
defer func() {
|
defer func() {
|
||||||
i.regionsCache = i.regionsCache[:0]
|
i.regionsCache = i.regionsCache[:0]
|
||||||
}()
|
}()
|
||||||
n := removeDuplicatedRegions(i.regionsCache)
|
rs = i.regionsCache
|
||||||
rs = i.regionsCache[:n]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range rs {
|
for _, r := range rs {
|
||||||
@ -625,14 +624,12 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
|
|
||||||
// In order to clear the draw-triangles history, read pixels from GPU.
|
// In order to clear the draw-triangles history, read pixels from GPU.
|
||||||
if len(i.drawTrianglesHistory) > 0 {
|
if len(i.drawTrianglesHistory) > 0 {
|
||||||
i.regionsCache = i.appendRegionsForDrawTriangles(i.regionsCache)
|
i.appendRegionsForDrawTriangles(&i.regionsCache)
|
||||||
defer func() {
|
defer func() {
|
||||||
i.regionsCache = i.regionsCache[:0]
|
i.regionsCache = i.regionsCache[:0]
|
||||||
}()
|
}()
|
||||||
n := removeDuplicatedRegions(i.regionsCache)
|
|
||||||
rs := i.regionsCache[:n]
|
|
||||||
|
|
||||||
for _, r := range rs {
|
for _, r := range i.regionsCache {
|
||||||
if r.Empty() {
|
if r.Empty() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -698,18 +695,14 @@ func (i *Image) InternalSize() (int, int) {
|
|||||||
return i.image.InternalSize()
|
return i.image.InternalSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) appendRegionsForDrawTriangles(regions []image.Rectangle) []image.Rectangle {
|
func (i *Image) appendRegionsForDrawTriangles(regions *[]image.Rectangle) {
|
||||||
n := len(regions)
|
|
||||||
for _, d := range i.drawTrianglesHistory {
|
for _, d := range i.drawTrianglesHistory {
|
||||||
r := regionToRectangle(d.dstRegion)
|
r := regionToRectangle(d.dstRegion)
|
||||||
if r.Empty() {
|
if r.Empty() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
regions = append(regions, r)
|
appendRegionRemovingDuplicates(regions, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
nn := removeDuplicatedRegions(regions[n:])
|
|
||||||
return regions[:n+nn]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func regionToRectangle(region graphicsdriver.Region) image.Rectangle {
|
func regionToRectangle(region graphicsdriver.Region) image.Rectangle {
|
||||||
@ -720,34 +713,29 @@ func regionToRectangle(region graphicsdriver.Region) image.Rectangle {
|
|||||||
int(math.Ceil(float64(region.Y+region.Height))))
|
int(math.Ceil(float64(region.Y+region.Height))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeDuplicatedRegions removes duplicated regions and returns the new size of the slice.
|
// appendRegionRemovingDuplicates adds a region to a given list of regions,
|
||||||
// If a region covers other regions, the covered regions are removed.
|
// but removes any duplicate between the newly added region and any existing regions.
|
||||||
func removeDuplicatedRegions(regions []image.Rectangle) int {
|
//
|
||||||
for i, r := range regions {
|
// In case the newly added region is fully contained in any pre-existing region, this function does nothing.
|
||||||
if r.Empty() {
|
// Otherwise, any pre-existing regions that are fully contained in the newly added region are removed.
|
||||||
continue
|
//
|
||||||
}
|
// This is done to avoid unnecessary reading pixels from GPU.
|
||||||
for j, rr := range regions {
|
func appendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) {
|
||||||
if i == j {
|
for _, r := range *regions {
|
||||||
continue
|
if region.In(r) {
|
||||||
}
|
// The newly added rectangle is fully contained in one of the input regions.
|
||||||
if rr.Empty() {
|
// Nothing to add.
|
||||||
continue
|
return
|
||||||
}
|
|
||||||
if rr.In(r) {
|
|
||||||
regions[j] = image.Rectangle{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Separate loop, as regions must not get mutated before above return.
|
||||||
n := 0
|
n := 0
|
||||||
for _, r := range regions {
|
for _, r := range *regions {
|
||||||
if r.Empty() {
|
if r.In(region) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
regions[n] = r
|
(*regions)[n] = r
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
*regions = append((*regions)[:n], region)
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
|
@ -37,94 +37,129 @@ func areEqualRectangles(a, b []image.Rectangle) bool {
|
|||||||
|
|
||||||
func TestRemoveDuplicatedRegions(t *testing.T) {
|
func TestRemoveDuplicatedRegions(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
In []image.Rectangle
|
Regions []image.Rectangle
|
||||||
Out []image.Rectangle
|
NewRegions []image.Rectangle
|
||||||
|
Expected []image.Rectangle
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
In: nil,
|
Regions: nil,
|
||||||
Out: nil,
|
NewRegions: nil,
|
||||||
|
Expected: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: []image.Rectangle{
|
NewRegions: []image.Rectangle{
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
},
|
},
|
||||||
Out: []image.Rectangle{
|
Expected: []image.Rectangle{
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: []image.Rectangle{
|
NewRegions: []image.Rectangle{
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
image.Rect(0, 0, 1, 1),
|
image.Rect(0, 0, 1, 1),
|
||||||
},
|
},
|
||||||
Out: []image.Rectangle{
|
Expected: []image.Rectangle{
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: []image.Rectangle{
|
NewRegions: []image.Rectangle{
|
||||||
image.Rect(0, 0, 1, 1),
|
image.Rect(0, 0, 1, 1),
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
},
|
},
|
||||||
Out: []image.Rectangle{
|
Expected: []image.Rectangle{
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: []image.Rectangle{
|
NewRegions: []image.Rectangle{
|
||||||
image.Rect(0, 0, 1, 3),
|
image.Rect(0, 0, 1, 3),
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
image.Rect(0, 0, 3, 1),
|
image.Rect(0, 0, 3, 1),
|
||||||
},
|
},
|
||||||
Out: []image.Rectangle{
|
Expected: []image.Rectangle{
|
||||||
image.Rect(0, 0, 1, 3),
|
image.Rect(0, 0, 1, 3),
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
image.Rect(0, 0, 3, 1),
|
image.Rect(0, 0, 3, 1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: []image.Rectangle{
|
NewRegions: []image.Rectangle{
|
||||||
image.Rect(0, 0, 1, 3),
|
image.Rect(0, 0, 1, 3),
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
image.Rect(0, 0, 3, 1),
|
image.Rect(0, 0, 3, 1),
|
||||||
image.Rect(0, 0, 4, 4),
|
image.Rect(0, 0, 4, 4),
|
||||||
},
|
},
|
||||||
Out: []image.Rectangle{
|
Expected: []image.Rectangle{
|
||||||
image.Rect(0, 0, 4, 4),
|
image.Rect(0, 0, 4, 4),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: []image.Rectangle{
|
NewRegions: []image.Rectangle{
|
||||||
image.Rect(0, 0, 1, 3),
|
image.Rect(0, 0, 1, 3),
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
image.Rect(0, 0, 3, 1),
|
image.Rect(0, 0, 3, 1),
|
||||||
image.Rect(0, 0, 4, 4),
|
image.Rect(0, 0, 4, 4),
|
||||||
image.Rect(1, 1, 2, 2),
|
image.Rect(1, 1, 2, 2),
|
||||||
},
|
},
|
||||||
Out: []image.Rectangle{
|
Expected: []image.Rectangle{
|
||||||
image.Rect(0, 0, 4, 4),
|
image.Rect(0, 0, 4, 4),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: []image.Rectangle{
|
NewRegions: []image.Rectangle{
|
||||||
image.Rect(0, 0, 1, 3),
|
image.Rect(0, 0, 1, 3),
|
||||||
image.Rect(0, 0, 2, 2),
|
image.Rect(0, 0, 2, 2),
|
||||||
image.Rect(0, 0, 3, 1),
|
image.Rect(0, 0, 3, 1),
|
||||||
image.Rect(0, 0, 4, 4),
|
image.Rect(0, 0, 4, 4),
|
||||||
image.Rect(0, 0, 5, 5),
|
image.Rect(0, 0, 5, 5),
|
||||||
},
|
},
|
||||||
Out: []image.Rectangle{
|
Expected: []image.Rectangle{
|
||||||
|
image.Rect(0, 0, 5, 5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Regions: []image.Rectangle{
|
||||||
|
image.Rect(0, 0, 1, 3),
|
||||||
|
image.Rect(0, 0, 2, 2),
|
||||||
|
image.Rect(0, 0, 3, 1),
|
||||||
|
image.Rect(0, 0, 4, 4),
|
||||||
|
},
|
||||||
|
NewRegions: []image.Rectangle{
|
||||||
|
image.Rect(0, 0, 5, 5),
|
||||||
|
},
|
||||||
|
Expected: []image.Rectangle{
|
||||||
|
image.Rect(0, 0, 5, 5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Regions: []image.Rectangle{
|
||||||
|
image.Rect(0, 0, 2, 2),
|
||||||
|
image.Rect(0, 0, 3, 1),
|
||||||
|
image.Rect(0, 0, 4, 4),
|
||||||
|
image.Rect(0, 0, 5, 5),
|
||||||
|
},
|
||||||
|
NewRegions: []image.Rectangle{
|
||||||
|
image.Rect(0, 0, 1, 3),
|
||||||
|
},
|
||||||
|
Expected: []image.Rectangle{
|
||||||
|
image.Rect(0, 0, 2, 2),
|
||||||
|
image.Rect(0, 0, 3, 1),
|
||||||
|
image.Rect(0, 0, 4, 4),
|
||||||
image.Rect(0, 0, 5, 5),
|
image.Rect(0, 0, 5, 5),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
n := restorable.RemoveDuplicatedRegions(c.In)
|
got := c.Regions
|
||||||
got := c.In[:n]
|
for _, r := range c.NewRegions {
|
||||||
want := c.Out
|
restorable.AppendRegionRemovingDuplicates(&got, r)
|
||||||
|
}
|
||||||
|
want := c.Expected
|
||||||
if !areEqualRectangles(got, want) {
|
if !areEqualRectangles(got, want) {
|
||||||
t.Errorf("restorable.RemoveDuplicatedRegions(%#v): got: %#v, want: %#v", c.In, got, want)
|
t.Errorf("restorable.RemoveDuplicatedRegions(%#v): got: %#v, want: %#v", c.NewRegions, got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user