mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
internal/restorable: optimize removeDuplicatedRegions
Updates #2375 Updates #2626 Updates #3083
This commit is contained in:
parent
35f4884a74
commit
d50a438c07
@ -15,8 +15,6 @@
|
||||
package restorable
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||
)
|
||||
|
||||
@ -28,7 +26,3 @@ func EnableRestoringForTesting() {
|
||||
func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error {
|
||||
return resolveStaleImages(graphicsDriver, false)
|
||||
}
|
||||
|
||||
func AppendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) {
|
||||
appendRegionRemovingDuplicates(regions, region)
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package restorable
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"sort"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
|
||||
@ -215,20 +216,16 @@ func (i *Image) makeStale(rect image.Rectangle) {
|
||||
return
|
||||
}
|
||||
|
||||
var addedRegions []image.Rectangle
|
||||
i.appendRegionsForDrawTriangles(&addedRegions)
|
||||
origSize := len(i.staleRegions)
|
||||
i.staleRegions = i.appendRegionsForDrawTriangles(i.staleRegions)
|
||||
if !rect.Empty() {
|
||||
appendRegionRemovingDuplicates(&addedRegions, rect)
|
||||
}
|
||||
|
||||
for _, rect := range addedRegions {
|
||||
appendRegionRemovingDuplicates(&i.staleRegions, rect)
|
||||
i.staleRegions = append(i.staleRegions, rect)
|
||||
}
|
||||
|
||||
i.clearDrawTrianglesHistory()
|
||||
|
||||
// Clear pixels to save memory.
|
||||
for _, r := range addedRegions {
|
||||
for _, r := range i.staleRegions[origSize:] {
|
||||
i.basePixels.Clear(r)
|
||||
}
|
||||
|
||||
@ -450,13 +447,16 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error
|
||||
if i.stale {
|
||||
rs = i.staleRegions
|
||||
} else {
|
||||
i.appendRegionsForDrawTriangles(&i.regionsCache)
|
||||
i.regionsCache = i.appendRegionsForDrawTriangles(i.regionsCache)
|
||||
defer func() {
|
||||
i.regionsCache = i.regionsCache[:0]
|
||||
}()
|
||||
rs = i.regionsCache
|
||||
}
|
||||
|
||||
// Remove duplications. Is this heavy?
|
||||
rs = rs[:removeDuplicatedRegions(rs)]
|
||||
|
||||
args := make([]graphicsdriver.PixelsArgs, 0, len(rs))
|
||||
for _, r := range rs {
|
||||
if r.Empty() {
|
||||
@ -607,7 +607,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
|
||||
|
||||
// In order to clear the draw-triangles history, read pixels from GPU.
|
||||
if len(i.drawTrianglesHistory) > 0 {
|
||||
i.appendRegionsForDrawTriangles(&i.regionsCache)
|
||||
i.regionsCache = i.appendRegionsForDrawTriangles(i.regionsCache)
|
||||
defer func() {
|
||||
i.regionsCache = i.regionsCache[:0]
|
||||
}()
|
||||
@ -683,38 +683,54 @@ func (i *Image) InternalSize() (int, int) {
|
||||
return i.image.InternalSize()
|
||||
}
|
||||
|
||||
func (i *Image) appendRegionsForDrawTriangles(regions *[]image.Rectangle) {
|
||||
func (i *Image) appendRegionsForDrawTriangles(regions []image.Rectangle) []image.Rectangle {
|
||||
for _, d := range i.drawTrianglesHistory {
|
||||
if d.dstRegion.Empty() {
|
||||
continue
|
||||
}
|
||||
appendRegionRemovingDuplicates(regions, d.dstRegion)
|
||||
regions = append(regions, d.dstRegion)
|
||||
}
|
||||
return regions
|
||||
}
|
||||
|
||||
// appendRegionRemovingDuplicates adds a region to a given list of regions,
|
||||
// but removes any duplicate between the newly added region and any existing regions.
|
||||
//
|
||||
// In case the newly added region is fully contained in any pre-existing region, this function does nothing.
|
||||
// Otherwise, any pre-existing regions that are fully contained in the newly added region are removed.
|
||||
//
|
||||
// This is done to avoid unnecessary reading pixels from GPU.
|
||||
func appendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) {
|
||||
for _, r := range *regions {
|
||||
if region.In(r) {
|
||||
// The newly added rectangle is fully contained in one of the input regions.
|
||||
// Nothing to add.
|
||||
return
|
||||
}
|
||||
}
|
||||
// Separate loop, as regions must not get mutated before above return.
|
||||
n := 0
|
||||
for _, r := range *regions {
|
||||
if r.In(region) {
|
||||
// removeDuplicatedRegions removes duplicated regions and returns a shrunk slice.
|
||||
// If a region covers preceding regions, the covered regions are removed.
|
||||
func removeDuplicatedRegions(regions []image.Rectangle) int {
|
||||
// Sweep and prune algorithm
|
||||
|
||||
sort.Slice(regions, func(i, j int) bool {
|
||||
return regions[i].Min.X < regions[j].Min.X
|
||||
})
|
||||
|
||||
for i, r := range regions {
|
||||
if r.Empty() {
|
||||
continue
|
||||
}
|
||||
(*regions)[n] = r
|
||||
for j := i + 1; j < len(regions); j++ {
|
||||
rr := regions[j]
|
||||
if rr.Empty() {
|
||||
continue
|
||||
}
|
||||
if r.Max.X <= rr.Min.X {
|
||||
break
|
||||
}
|
||||
if rr.In(r) {
|
||||
regions[j] = image.Rectangle{}
|
||||
} else if r.In(rr) {
|
||||
regions[i] = image.Rectangle{}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var n int
|
||||
for _, r := range regions {
|
||||
if r.Empty() {
|
||||
continue
|
||||
}
|
||||
regions[n] = r
|
||||
n++
|
||||
}
|
||||
*regions = append((*regions)[:n], region)
|
||||
|
||||
return n
|
||||
}
|
||||
|
@ -1,165 +0,0 @@
|
||||
// Copyright 2023 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package restorable_test
|
||||
|
||||
import (
|
||||
"image"
|
||||
"testing"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/restorable"
|
||||
)
|
||||
|
||||
func areEqualRectangles(a, b []image.Rectangle) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestRemoveDuplicatedRegions(t *testing.T) {
|
||||
cases := []struct {
|
||||
Regions []image.Rectangle
|
||||
NewRegions []image.Rectangle
|
||||
Expected []image.Rectangle
|
||||
}{
|
||||
{
|
||||
Regions: nil,
|
||||
NewRegions: nil,
|
||||
Expected: nil,
|
||||
},
|
||||
{
|
||||
NewRegions: []image.Rectangle{
|
||||
image.Rect(0, 0, 2, 2),
|
||||
},
|
||||
Expected: []image.Rectangle{
|
||||
image.Rect(0, 0, 2, 2),
|
||||
},
|
||||
},
|
||||
{
|
||||
NewRegions: []image.Rectangle{
|
||||
image.Rect(0, 0, 2, 2),
|
||||
image.Rect(0, 0, 1, 1),
|
||||
},
|
||||
Expected: []image.Rectangle{
|
||||
image.Rect(0, 0, 2, 2),
|
||||
},
|
||||
},
|
||||
{
|
||||
NewRegions: []image.Rectangle{
|
||||
image.Rect(0, 0, 1, 1),
|
||||
image.Rect(0, 0, 2, 2),
|
||||
},
|
||||
Expected: []image.Rectangle{
|
||||
image.Rect(0, 0, 2, 2),
|
||||
},
|
||||
},
|
||||
{
|
||||
NewRegions: []image.Rectangle{
|
||||
image.Rect(0, 0, 1, 3),
|
||||
image.Rect(0, 0, 2, 2),
|
||||
image.Rect(0, 0, 3, 1),
|
||||
},
|
||||
Expected: []image.Rectangle{
|
||||
image.Rect(0, 0, 1, 3),
|
||||
image.Rect(0, 0, 2, 2),
|
||||
image.Rect(0, 0, 3, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
NewRegions: []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),
|
||||
},
|
||||
Expected: []image.Rectangle{
|
||||
image.Rect(0, 0, 4, 4),
|
||||
},
|
||||
},
|
||||
{
|
||||
NewRegions: []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),
|
||||
image.Rect(1, 1, 2, 2),
|
||||
},
|
||||
Expected: []image.Rectangle{
|
||||
image.Rect(0, 0, 4, 4),
|
||||
},
|
||||
},
|
||||
{
|
||||
NewRegions: []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),
|
||||
image.Rect(0, 0, 5, 5),
|
||||
},
|
||||
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),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
got := c.Regions
|
||||
for _, r := range c.NewRegions {
|
||||
restorable.AppendRegionRemovingDuplicates(&got, r)
|
||||
}
|
||||
want := c.Expected
|
||||
if !areEqualRectangles(got, want) {
|
||||
t.Errorf("restorable.RemoveDuplicatedRegions(%#v): got: %#v, want: %#v", c.NewRegions, got, want)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user