mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 11:48:55 +01:00
shareable: Bug fix: Protect critical sections
This commit is contained in:
parent
c411ca492f
commit
48408cba11
@ -238,6 +238,7 @@ func TestImageScale(t *testing.T) {
|
|||||||
}
|
}
|
||||||
op := &DrawImageOptions{}
|
op := &DrawImageOptions{}
|
||||||
op.GeoM.Scale(float64(scale), float64(scale))
|
op.GeoM.Scale(float64(scale), float64(scale))
|
||||||
|
|
||||||
if err := img1.DrawImage(img0, op); err != nil {
|
if err := img1.DrawImage(img0, op); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
@ -317,7 +318,7 @@ func TestImageDotByDotInversion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplacePixels(t *testing.T) {
|
func TestImageReplacePixels(t *testing.T) {
|
||||||
// Create a dummy image so that the shared texture is used and origImg's position is shfited.
|
// Create a dummy image so that the shared texture is used and origImg's position is shfited.
|
||||||
dummyImg, _ := NewImageFromImage(image.NewRGBA(image.Rect(0, 0, 16, 16)), FilterDefault)
|
dummyImg, _ := NewImageFromImage(image.NewRGBA(image.Rect(0, 0, 16, 16)), FilterDefault)
|
||||||
defer dummyImg.Dispose()
|
defer dummyImg.Dispose()
|
||||||
|
@ -15,10 +15,6 @@
|
|||||||
// Package packing offers a packing algorithm in 2D space.
|
// Package packing offers a packing algorithm in 2D space.
|
||||||
package packing
|
package packing
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minSize = 1
|
minSize = 1
|
||||||
)
|
)
|
||||||
@ -26,7 +22,6 @@ const (
|
|||||||
type Page struct {
|
type Page struct {
|
||||||
root *Node
|
root *Node
|
||||||
maxSize int
|
maxSize int
|
||||||
m sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPage(maxSize int) *Page {
|
func NewPage(maxSize int) *Page {
|
||||||
@ -36,14 +31,10 @@ func NewPage(maxSize int) *Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) IsEmpty() bool {
|
func (p *Page) IsEmpty() bool {
|
||||||
p.m.Lock()
|
|
||||||
if p.root == nil {
|
if p.root == nil {
|
||||||
p.m.Unlock()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
r := !p.root.used && p.root.child0 == nil && p.root.child1 == nil
|
return !p.root.used && p.root.child0 == nil && p.root.child1 == nil
|
||||||
p.m.Unlock()
|
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
@ -145,7 +136,6 @@ func (p *Page) alloc(n *Node, width, height int) *Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) Alloc(width, height int) *Node {
|
func (p *Page) Alloc(width, height int) *Node {
|
||||||
p.m.Lock()
|
|
||||||
if width <= 0 || height <= 0 {
|
if width <= 0 || height <= 0 {
|
||||||
panic("bsp: width and height must > 0")
|
panic("bsp: width and height must > 0")
|
||||||
}
|
}
|
||||||
@ -162,17 +152,10 @@ func (p *Page) Alloc(width, height int) *Node {
|
|||||||
height = minSize
|
height = minSize
|
||||||
}
|
}
|
||||||
n := p.alloc(p.root, width, height)
|
n := p.alloc(p.root, width, height)
|
||||||
p.m.Unlock()
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) Free(node *Node) {
|
func (p *Page) Free(node *Node) {
|
||||||
p.m.Lock()
|
|
||||||
p.free(node)
|
|
||||||
p.m.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Page) free(node *Node) {
|
|
||||||
if node.child0 != nil || node.child1 != nil {
|
if node.child0 != nil || node.child1 != nil {
|
||||||
panic("bsp: can't free the node including children")
|
panic("bsp: can't free the node including children")
|
||||||
}
|
}
|
||||||
@ -186,6 +169,6 @@ func (p *Page) free(node *Node) {
|
|||||||
if node.parent.child0.canFree() && node.parent.child1.canFree() {
|
if node.parent.child0.canFree() && node.parent.child1.canFree() {
|
||||||
node.parent.child0 = nil
|
node.parent.child0 = nil
|
||||||
node.parent.child1 = nil
|
node.parent.child1 = nil
|
||||||
p.free(node.parent)
|
p.Free(node.parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@ type backend struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// backendsM is a mutex for critical sections of the backend and packing.Node objects.
|
||||||
|
backendsM sync.Mutex
|
||||||
|
|
||||||
// theBackends is a set of actually shared images.
|
// theBackends is a set of actually shared images.
|
||||||
theBackends = []*backend{}
|
theBackends = []*backend{}
|
||||||
)
|
)
|
||||||
@ -53,7 +56,7 @@ func (s *Image) ensureNotShared() {
|
|||||||
newImg := restorable.NewImage(w, h, false)
|
newImg := restorable.NewImage(w, h, false)
|
||||||
newImg.DrawImage(s.backend.restorable, x, y, w, h, nil, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
|
newImg.DrawImage(s.backend.restorable, x, y, w, h, nil, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
|
||||||
|
|
||||||
s.Dispose()
|
s.dispose()
|
||||||
s.backend = &backend{
|
s.backend = &backend{
|
||||||
restorable: newImg,
|
restorable: newImg,
|
||||||
}
|
}
|
||||||
@ -68,11 +71,19 @@ func (s *Image) region() (x, y, width, height int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) Size() (width, height int) {
|
func (s *Image) Size() (width, height int) {
|
||||||
|
backendsM.Lock()
|
||||||
_, _, w, h := s.region()
|
_, _, w, h := s.region()
|
||||||
|
backendsM.Unlock()
|
||||||
return w, h
|
return w, h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) DrawImage(img *Image, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
|
func (s *Image) DrawImage(img *Image, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
|
||||||
|
backendsM.Lock()
|
||||||
|
defer backendsM.Unlock()
|
||||||
|
s.drawImage(img, sx0, sy0, sx1, sy1, geom, colorm, mode, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Image) drawImage(img *Image, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
|
||||||
s.ensureNotShared()
|
s.ensureNotShared()
|
||||||
|
|
||||||
// Compare i and img after ensuring i is not shared, or
|
// Compare i and img after ensuring i is not shared, or
|
||||||
@ -90,6 +101,9 @@ func (s *Image) DrawImage(img *Image, sx0, sy0, sx1, sy1 int, geom *affine.GeoM,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) ReplacePixels(p []byte) {
|
func (s *Image) ReplacePixels(p []byte) {
|
||||||
|
backendsM.Lock()
|
||||||
|
defer backendsM.Unlock()
|
||||||
|
|
||||||
x, y, w, h := s.region()
|
x, y, w, h := s.region()
|
||||||
if l := 4 * w * h; len(p) != l {
|
if l := 4 * w * h; len(p) != l {
|
||||||
panic(fmt.Sprintf("shareable: len(p) was %d but must be %d", len(p), l))
|
panic(fmt.Sprintf("shareable: len(p) was %d but must be %d", len(p), l))
|
||||||
@ -98,11 +112,17 @@ func (s *Image) ReplacePixels(p []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) At(x, y int) (color.Color, error) {
|
func (s *Image) At(x, y int) (color.Color, error) {
|
||||||
|
backendsM.Lock()
|
||||||
|
|
||||||
ox, oy, w, h := s.region()
|
ox, oy, w, h := s.region()
|
||||||
if x < 0 || y < 0 || x >= w || y >= h {
|
if x < 0 || y < 0 || x >= w || y >= h {
|
||||||
|
backendsM.Unlock()
|
||||||
return color.RGBA{}, nil
|
return color.RGBA{}, nil
|
||||||
}
|
}
|
||||||
return s.backend.restorable.At(x+ox, y+oy)
|
|
||||||
|
clr, err := s.backend.restorable.At(x+ox, y+oy)
|
||||||
|
backendsM.Unlock()
|
||||||
|
return clr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) isDisposed() bool {
|
func (s *Image) isDisposed() bool {
|
||||||
@ -110,6 +130,12 @@ func (s *Image) isDisposed() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) Dispose() {
|
func (s *Image) Dispose() {
|
||||||
|
backendsM.Lock()
|
||||||
|
defer backendsM.Unlock()
|
||||||
|
s.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Image) dispose() {
|
||||||
if s.isDisposed() {
|
if s.isDisposed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -144,16 +170,17 @@ func (s *Image) Dispose() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) IsInvalidated() (bool, error) {
|
func (s *Image) IsInvalidated() (bool, error) {
|
||||||
return s.backend.restorable.IsInvalidated()
|
backendsM.Lock()
|
||||||
|
v, err := s.backend.restorable.IsInvalidated()
|
||||||
|
backendsM.Unlock()
|
||||||
|
return v, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var shareableImageLock sync.Mutex
|
|
||||||
|
|
||||||
func NewImage(width, height int) *Image {
|
func NewImage(width, height int) *Image {
|
||||||
const maxSize = 2048
|
const maxSize = 2048
|
||||||
|
|
||||||
shareableImageLock.Lock()
|
backendsM.Lock()
|
||||||
defer shareableImageLock.Unlock()
|
defer backendsM.Unlock()
|
||||||
|
|
||||||
if width > maxSize || height > maxSize {
|
if width > maxSize || height > maxSize {
|
||||||
s := &backend{
|
s := &backend{
|
||||||
|
Loading…
Reference in New Issue
Block a user