mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
restorable: Move Images to resotrable package
This commit is contained in:
parent
d94e9c9d86
commit
9f5cf48d24
@ -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
122
image.go
@ -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
|
||||
|
@ -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
|
||||
|
115
internal/restorable/images.go
Normal file
115
internal/restorable/images.go
Normal 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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user