restorable: Move Images to resotrable package

This commit is contained in:
Hajime Hoshi 2017-05-03 23:11:43 +09:00
parent d94e9c9d86
commit 9f5cf48d24
4 changed files with 144 additions and 112 deletions

View File

@ -20,6 +20,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/restorable"
"github.com/hajimehoshi/ebiten/internal/ui"
)
@ -160,11 +161,11 @@ func (c *graphicsContext) UpdateAndDraw(context *opengl.Context, updateCount int
if err := c.initializeIfNeeded(context); err != nil {
return err
}
if err := theImagesForRestoring.resolveStalePixels(context); err != nil {
if err := restorable.Images().ResolveStalePixels(context); err != nil {
return err
}
for i := 0; i < updateCount; i++ {
theImagesForRestoring.clearVolatileImages()
restorable.Images().ClearVolatileImages()
setRunningSlowly(i < updateCount-1)
if err := c.f(c.offscreen); err != nil {
return err
@ -185,7 +186,7 @@ func (c *graphicsContext) restore(context *opengl.Context) error {
if err := graphics.Reset(context); err != nil {
return err
}
if err := theImagesForRestoring.restore(context); err != nil {
if err := restorable.Images().Restore(context); err != nil {
return err
}
c.invalidated = false

122
image.go
View File

@ -19,7 +19,6 @@ import (
"image"
"image/color"
"runtime"
"sync"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
@ -38,99 +37,6 @@ func glContext() *opengl.Context {
return g.GLContext()
}
type images struct {
images map[*restorable.Image]struct{}
m sync.Mutex
lastChecked *restorable.Image
}
var theImagesForRestoring = images{
images: map[*restorable.Image]struct{}{},
}
func (i *images) add(img *restorable.Image) *Image {
i.m.Lock()
defer i.m.Unlock()
i.images[img] = struct{}{}
eimg := &Image{img}
runtime.SetFinalizer(eimg, theImagesForRestoring.remove)
return eimg
}
func (i *images) remove(img *Image) {
r := img.restorable
if err := img.Dispose(); err != nil {
panic(err)
}
i.m.Lock()
defer i.m.Unlock()
delete(i.images, r)
}
func (i *images) resolveStalePixels(context *opengl.Context) error {
i.m.Lock()
defer i.m.Unlock()
i.lastChecked = nil
for img := range i.images {
if err := img.ResolveStalePixels(context); err != nil {
return err
}
}
return nil
}
func (i *images) resetPixelsIfDependingOn(target *Image) {
i.m.Lock()
defer i.m.Unlock()
if i.lastChecked == target.restorable {
return
}
i.lastChecked = target.restorable
if target.restorable == nil {
// disposed
return
}
for img := range i.images {
img.MakeStaleIfDependingOn(target.restorable)
}
}
func (i *images) restore(context *opengl.Context) error {
i.m.Lock()
defer i.m.Unlock()
// Framebuffers/textures cannot be disposed since framebuffers/textures that
// don't belong to the current context.
imagesWithoutDependency := []*restorable.Image{}
imagesWithDependency := []*restorable.Image{}
for img := range i.images {
if img.HasDependency() {
imagesWithDependency = append(imagesWithDependency, img)
} else {
imagesWithoutDependency = append(imagesWithoutDependency, img)
}
}
// Images depending on other images should be processed first.
for _, img := range imagesWithoutDependency {
if err := img.Restore(context); err != nil {
return err
}
}
for _, img := range imagesWithDependency {
if err := img.Restore(context); err != nil {
return err
}
}
return nil
}
func (i *images) clearVolatileImages() {
i.m.Lock()
defer i.m.Unlock()
for img := range i.images {
img.ClearIfVolatile()
}
}
// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
@ -151,7 +57,6 @@ func (i *Image) Size() (width, height int) {
//
// Clear always returns nil as of 1.5.0-alpha.
func (i *Image) Clear() error {
theImagesForRestoring.resetPixelsIfDependingOn(i)
i.restorable.Fill(color.RGBA{})
return nil
}
@ -162,7 +67,6 @@ func (i *Image) Clear() error {
//
// Fill always returns nil as of 1.5.0-alpha.
func (i *Image) Fill(clr color.Color) error {
theImagesForRestoring.resetPixelsIfDependingOn(i)
rgba := color.RGBAModel.Convert(clr).(color.RGBA)
i.restorable.Fill(rgba)
return nil
@ -194,7 +98,6 @@ func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error {
if i.restorable == nil {
return nil
}
theImagesForRestoring.resetPixelsIfDependingOn(i)
// Calculate vertices before locking because the user can do anything in
// options.ImageParts interface without deadlock (e.g. Call Image functions).
if options == nil {
@ -264,10 +167,8 @@ func (i *Image) Dispose() error {
if i.restorable == nil {
return nil
}
theImagesForRestoring.resetPixelsIfDependingOn(i)
i.restorable.Dispose()
restorable.Images().Remove(i.restorable)
i.restorable = nil
runtime.SetFinalizer(i, nil)
return nil
}
@ -286,7 +187,6 @@ func (i *Image) ReplacePixels(p []uint8) error {
if i.restorable == nil {
return nil
}
theImagesForRestoring.resetPixelsIfDependingOn(i)
w, h := i.restorable.Size()
if l := 4 * w * h; len(p) != l {
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
@ -320,7 +220,10 @@ func NewImage(width, height int, filter Filter) (*Image, error) {
checkSize(width, height)
r := restorable.NewImage(width, height, glFilter(filter), false)
r.Fill(color.RGBA{})
return theImagesForRestoring.add(r), nil
restorable.Images().Add(r)
i := &Image{r}
runtime.SetFinalizer(i, (*Image).Dispose)
return i, nil
}
// newVolatileImage returns an empty 'volatile' image.
@ -340,7 +243,10 @@ func newVolatileImage(width, height int, filter Filter) (*Image, error) {
checkSize(width, height)
r := restorable.NewImage(width, height, glFilter(filter), true)
r.Fill(color.RGBA{})
return theImagesForRestoring.add(r), nil
restorable.Images().Add(r)
i := &Image{r}
runtime.SetFinalizer(i, (*Image).Dispose)
return i, nil
}
// NewImageFromImage creates a new image with the given image (source).
@ -354,13 +260,19 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
checkSize(w, h)
rgbaImg := graphics.CopyImage(source)
r := restorable.NewImageFromImage(rgbaImg, w, h, glFilter(filter))
return theImagesForRestoring.add(r), nil
restorable.Images().Add(r)
i := &Image{r}
runtime.SetFinalizer(i, (*Image).Dispose)
return i, nil
}
func newImageWithScreenFramebuffer(width, height int) (*Image, error) {
checkSize(width, height)
r := restorable.NewScreenFramebufferImage(width, height)
return theImagesForRestoring.add(r), nil
restorable.Images().Add(r)
i := &Image{r}
runtime.SetFinalizer(i, (*Image).Dispose)
return i, nil
}
const MaxImageSize = graphics.MaxImageSize

View File

@ -103,6 +103,7 @@ func (p *Image) ClearIfVolatile() {
}
func (p *Image) Fill(clr color.RGBA) {
theImages.resetPixelsIfDependingOn(p)
p.basePixels = nil
p.baseColor = clr
p.drawImageHistory = nil
@ -111,6 +112,7 @@ func (p *Image) Fill(clr color.RGBA) {
}
func (p *Image) ReplacePixels(pixels []uint8) {
theImages.resetPixelsIfDependingOn(p)
p.image.ReplacePixels(pixels)
p.basePixels = pixels
p.baseColor = color.RGBA{}
@ -119,6 +121,7 @@ func (p *Image) ReplacePixels(pixels []uint8) {
}
func (p *Image) DrawImage(img *Image, vertices []float32, colorm affine.ColorM, mode opengl.CompositeMode) {
theImages.resetPixelsIfDependingOn(p)
if img.stale || img.volatile {
p.makeStale()
} else {
@ -190,7 +193,7 @@ func (p *Image) readPixelsFromVRAM(image *graphics.Image, context *opengl.Contex
return nil
}
func (p *Image) ResolveStalePixels(context *opengl.Context) error {
func (p *Image) resolveStalePixels(context *opengl.Context) error {
if p.volatile {
return nil
}
@ -200,7 +203,7 @@ func (p *Image) ResolveStalePixels(context *opengl.Context) error {
return p.readPixelsFromVRAM(p.image, context)
}
func (p *Image) HasDependency() bool {
func (p *Image) hasDependency() bool {
if p.stale {
return false
}
@ -248,7 +251,7 @@ func (p *Image) Restore(context *opengl.Context) error {
}
for _, c := range p.drawImageHistory {
// c.image.image must be already restored.
if c.image.HasDependency() {
if c.image.hasDependency() {
panic("not reach")
}
gimg.DrawImage(c.image.image, c.vertices, c.colorm, c.mode)
@ -266,7 +269,8 @@ func (p *Image) Restore(context *opengl.Context) error {
return nil
}
func (p *Image) Dispose() {
func (p *Image) dispose() {
theImages.resetPixelsIfDependingOn(p)
p.image.Dispose()
p.image = nil
p.basePixels = nil

View File

@ -0,0 +1,115 @@
// Copyright 2017 The Ebiten 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
import (
"runtime"
"sync"
"github.com/hajimehoshi/ebiten/internal/opengl"
)
type images struct {
images map[*Image]struct{}
m sync.Mutex
lastChecked *Image
}
var theImages = &images{
images: map[*Image]struct{}{},
}
func Images() *images {
return theImages
}
func (i *images) Add(img *Image) {
i.m.Lock()
defer i.m.Unlock()
i.images[img] = struct{}{}
runtime.SetFinalizer(img, theImages.Remove)
}
func (i *images) Remove(img *Image) {
img.dispose()
i.m.Lock()
defer i.m.Unlock()
delete(i.images, img)
runtime.SetFinalizer(img, nil)
}
func (i *images) ResolveStalePixels(context *opengl.Context) error {
i.m.Lock()
defer i.m.Unlock()
i.lastChecked = nil
for img := range i.images {
if err := img.resolveStalePixels(context); err != nil {
return err
}
}
return nil
}
func (i *images) resetPixelsIfDependingOn(target *Image) {
i.m.Lock()
defer i.m.Unlock()
if target == nil {
// disposed
return
}
if i.lastChecked == target {
return
}
i.lastChecked = target
for img := range i.images {
img.MakeStaleIfDependingOn(target)
}
}
func (i *images) Restore(context *opengl.Context) error {
i.m.Lock()
defer i.m.Unlock()
// Framebuffers/textures cannot be disposed since framebuffers/textures that
// don't belong to the current context.
imagesWithoutDependency := []*Image{}
imagesWithDependency := []*Image{}
for img := range i.images {
if img.hasDependency() {
imagesWithDependency = append(imagesWithDependency, img)
} else {
imagesWithoutDependency = append(imagesWithoutDependency, img)
}
}
// Images depending on other images should be processed first.
for _, img := range imagesWithoutDependency {
if err := img.Restore(context); err != nil {
return err
}
}
for _, img := range imagesWithDependency {
if err := img.Restore(context); err != nil {
return err
}
}
return nil
}
func (i *images) ClearVolatileImages() {
i.m.Lock()
defer i.m.Unlock()
for img := range i.images {
img.ClearIfVolatile()
}
}