mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 19:28:57 +01:00
restorable: Defer operations between the end and the beginning of frames
Updates #913
This commit is contained in:
parent
de49bbab12
commit
1a2a0aeb76
@ -122,6 +122,8 @@ func init() {
|
||||
//
|
||||
// Note that Dispose is not called automatically.
|
||||
func NewImage(width, height int) *Image {
|
||||
// As this should not affect the information for restoring, this doesn't have to be deferred.
|
||||
|
||||
i := &Image{
|
||||
image: graphicscommand.NewImage(width, height),
|
||||
}
|
||||
@ -173,6 +175,8 @@ func (i *Image) MakeVolatile() {
|
||||
//
|
||||
// Note that Dispose is not called automatically.
|
||||
func NewScreenFramebufferImage(width, height int) *Image {
|
||||
// As this should not affect the information for restoring, this doesn't have to be deferred.
|
||||
|
||||
i := &Image{
|
||||
image: graphicscommand.NewScreenFramebufferImage(width, height),
|
||||
screen: true,
|
||||
@ -183,6 +187,18 @@ func NewScreenFramebufferImage(width, height int) *Image {
|
||||
}
|
||||
|
||||
func (i *Image) Clear() {
|
||||
select {
|
||||
case theImages.deferCh <- struct{}{}:
|
||||
break
|
||||
default:
|
||||
theImages.deferUntilBeginFrame(i.Clear)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
<-theImages.deferCh
|
||||
}()
|
||||
|
||||
theImages.makeStaleIfDependingOn(i)
|
||||
i.clear()
|
||||
}
|
||||
@ -293,6 +309,20 @@ func (i *Image) ClearPixels(x, y, width, height int) {
|
||||
//
|
||||
// ReplacePixels for a part is forbidden if the image is rendered with DrawTriangles or Fill.
|
||||
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
||||
select {
|
||||
case theImages.deferCh <- struct{}{}:
|
||||
break
|
||||
default:
|
||||
theImages.deferUntilBeginFrame(func() {
|
||||
i.ReplacePixels(pixels, x, y, width, height)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
<-theImages.deferCh
|
||||
}()
|
||||
|
||||
w, h := i.image.Size()
|
||||
if width <= 0 || height <= 0 {
|
||||
panic("restorable: width/height must be positive")
|
||||
@ -346,6 +376,20 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
||||
|
||||
// DrawTriangles draws a given image img to the image.
|
||||
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
|
||||
select {
|
||||
case theImages.deferCh <- struct{}{}:
|
||||
break
|
||||
default:
|
||||
theImages.deferUntilBeginFrame(func() {
|
||||
i.DrawTriangles(img, vertices, indices, colorm, mode, filter, address)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
<-theImages.deferCh
|
||||
}()
|
||||
|
||||
if i.priority {
|
||||
panic("restorable: DrawTriangles cannot be called on a priority image")
|
||||
}
|
||||
@ -400,6 +444,8 @@ func (i *Image) readPixelsFromGPUIfNeeded() {
|
||||
//
|
||||
// Note that this must not be called until context is available.
|
||||
func (i *Image) At(x, y int) (byte, byte, byte, byte) {
|
||||
// As this should not affect the information for restoring, this doesn't have to be deferred.
|
||||
|
||||
w, h := i.image.Size()
|
||||
if x < 0 || y < 0 || w <= x || h <= y {
|
||||
return 0, 0, 0, 0
|
||||
@ -528,7 +574,6 @@ func (i *Image) restore() error {
|
||||
// After disposing, calling the function of the image causes unexpected results.
|
||||
func (i *Image) Dispose() {
|
||||
theImages.remove(i)
|
||||
|
||||
i.image.Dispose()
|
||||
i.image = nil
|
||||
i.basePixels = Pixels{}
|
||||
|
@ -16,6 +16,7 @@ package restorable
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/graphicscommand"
|
||||
)
|
||||
@ -40,11 +41,18 @@ func EnableRestoringForTesting() {
|
||||
type images struct {
|
||||
images map[*Image]struct{}
|
||||
lastTarget *Image
|
||||
|
||||
deferred []func()
|
||||
mDeferred sync.Mutex
|
||||
deferCh chan struct{}
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// theImages represents the images for the current process.
|
||||
var theImages = &images{
|
||||
images: map[*Image]struct{}{},
|
||||
deferCh: make(chan struct{}, 1),
|
||||
}
|
||||
|
||||
// ResolveStaleImages flushes the queued draw commands and resolves
|
||||
@ -52,6 +60,11 @@ var theImages = &images{
|
||||
//
|
||||
// ResolveStaleImages is intended to be called at the end of a frame.
|
||||
func ResolveStaleImages() {
|
||||
defer func() {
|
||||
// Until the begin o the frame (by RestoreIfNeeded, any operations are deferred.
|
||||
theImages.deferCh <- struct{}{}
|
||||
}()
|
||||
|
||||
graphicscommand.FlushCommands()
|
||||
if !needsRestoring() {
|
||||
return
|
||||
@ -63,6 +76,25 @@ func ResolveStaleImages() {
|
||||
//
|
||||
// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers.
|
||||
func RestoreIfNeeded() error {
|
||||
// Unlock except for the first time.
|
||||
//
|
||||
// In each frame, restoring images and resolving images happen respectively:
|
||||
//
|
||||
// [Restore -> Resolve] -> [Restore -> Resolve] -> ...
|
||||
//
|
||||
// Between each frame, any image operations are not permitted, or stale images would remain when restoring
|
||||
// (#913).
|
||||
firsttime := false
|
||||
theImages.once.Do(func() {
|
||||
firsttime = true
|
||||
})
|
||||
if !firsttime {
|
||||
<-theImages.deferCh
|
||||
}
|
||||
|
||||
// Deferred functions should be resolved after restoring.
|
||||
defer theImages.resolveDeferred()
|
||||
|
||||
if !needsRestoring() {
|
||||
return nil
|
||||
}
|
||||
@ -113,9 +145,26 @@ func (i *images) remove(img *Image) {
|
||||
delete(i.images, img)
|
||||
}
|
||||
|
||||
func (i *images) deferUntilBeginFrame(f func()) {
|
||||
i.mDeferred.Lock()
|
||||
defer i.mDeferred.Unlock()
|
||||
i.deferred = append(i.deferred, f)
|
||||
}
|
||||
|
||||
func (i *images) resolveDeferred() {
|
||||
i.mDeferred.Lock()
|
||||
defer i.mDeferred.Unlock()
|
||||
|
||||
for _, f := range i.deferred {
|
||||
f()
|
||||
}
|
||||
i.deferred = nil
|
||||
}
|
||||
|
||||
// resolveStaleImages resolves stale images.
|
||||
func (i *images) resolveStaleImages() {
|
||||
i.lastTarget = nil
|
||||
i.resolveDeferred()
|
||||
for img := range i.images {
|
||||
img.resolveStale()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user