mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 03:38:55 +01:00
internal/ui: refactoring
This commit is contained in:
parent
2eca476054
commit
f2acc3d9f7
@ -59,13 +59,13 @@ type context struct {
|
||||
|
||||
setContextOnce sync.Once
|
||||
|
||||
deferred []func()
|
||||
deferredM sync.Mutex
|
||||
funcsInFrameCh chan func()
|
||||
}
|
||||
|
||||
func newContext(game Game) *context {
|
||||
return &context{
|
||||
game: game,
|
||||
game: game,
|
||||
funcsInFrameCh: make(chan func()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,8 +118,8 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
|
||||
}()
|
||||
|
||||
// Flush deferred functions, like reading pixels from GPU.
|
||||
if err := c.flushDeferredFuncs(ui); err != nil {
|
||||
return err
|
||||
if err := c.processFuncsInFrame(ui); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForceUpdate can be invoked even if the context is not initialized yet (#1591).
|
||||
@ -297,30 +297,31 @@ func (u *UserInterface) LogicalPositionToClientPosition(x, y float64) (float64,
|
||||
return u.context.logicalPositionToClientPosition(x, y, u.DeviceScaleFactor())
|
||||
}
|
||||
|
||||
// appendDeferredFunc appends a function.
|
||||
// An appended function is called at the next frame.
|
||||
func (c *context) appendDeferredFunc(f func()) {
|
||||
c.deferredM.Lock()
|
||||
defer c.deferredM.Unlock()
|
||||
c.deferred = append(c.deferred, f)
|
||||
}
|
||||
|
||||
func (c *context) flushDeferredFuncs(ui *UserInterface) error {
|
||||
c.deferredM.Lock()
|
||||
fs := c.deferred
|
||||
c.deferred = nil
|
||||
c.deferredM.Unlock()
|
||||
|
||||
for _, f := range fs {
|
||||
func (c *context) runInFrame(f func()) {
|
||||
ch := make(chan struct{})
|
||||
c.funcsInFrameCh <- func() {
|
||||
defer close(ch)
|
||||
f()
|
||||
}
|
||||
<-ch
|
||||
return
|
||||
}
|
||||
|
||||
if len(fs) > 0 {
|
||||
// Catch the error that happened at (*Image).At.
|
||||
if err := ui.error(); err != nil {
|
||||
return err
|
||||
func (c *context) processFuncsInFrame(ui *UserInterface) error {
|
||||
var processed bool
|
||||
for {
|
||||
select {
|
||||
case f := <-c.funcsInFrameCh:
|
||||
f()
|
||||
processed = true
|
||||
default:
|
||||
if processed {
|
||||
// Catch the error that happened at (*Image).At.
|
||||
if err := ui.error(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -128,12 +128,16 @@ func (u *UserInterface) readPixels(mipmap *mipmap.Mipmap, pixels []byte, region
|
||||
// ReadPixels failed since this was called in between two frames.
|
||||
// Try this again at the next frame.
|
||||
if !ok {
|
||||
ch := make(chan error)
|
||||
u.context.appendDeferredFunc(func() {
|
||||
defer close(ch)
|
||||
// If this function is called from the same sequence as a game's Update and Draw,
|
||||
// this causes a dead lock.
|
||||
// This never happens so far, but if handling inputs after EndFrame is implemented,
|
||||
// this might be possible (#1704).
|
||||
|
||||
var err1 error
|
||||
u.context.runInFrame(func() {
|
||||
ok, err := mipmap.ReadPixels(u.graphicsDriver, pixels, region)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
err1 = err
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
@ -141,12 +145,7 @@ func (u *UserInterface) readPixels(mipmap *mipmap.Mipmap, pixels []byte, region
|
||||
panic("ui: ReadPixels unexpectedly failed")
|
||||
}
|
||||
})
|
||||
|
||||
// If this function is called from the game (Update/Draw) goroutine in between two frames,
|
||||
// this channel never returns and causes a dead lock.
|
||||
// This never happens so far, but if handling inputs after EndFrame is implemented,
|
||||
// this might be possible (#1704).
|
||||
return <-ch
|
||||
return err1
|
||||
}
|
||||
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user