ebiten: refactoring: remove setScreenClearedEveryFrame

This commit is contained in:
Hajime Hoshi 2022-02-13 20:30:32 +09:00
parent 0c1c40995c
commit 82b9f7dc56
4 changed files with 45 additions and 33 deletions

View File

@ -62,7 +62,6 @@ func (c *gameForUI) Layout(outsideWidth, outsideHeight float64, deviceScaleFacto
} }
if c.offscreen == nil { if c.offscreen == nil {
c.offscreen = NewImage(ow, oh) c.offscreen = NewImage(ow, oh)
c.offscreen.mipmap.SetVolatile(IsScreenClearedEveryFrame())
// Keep the offscreen an independent image from an atlas (#1938). // Keep the offscreen an independent image from an atlas (#1938).
// The shader program for the screen is special and doesn't work well with an image on an atlas. // The shader program for the screen is special and doesn't work well with an image on an atlas.
@ -74,24 +73,17 @@ func (c *gameForUI) Layout(outsideWidth, outsideHeight float64, deviceScaleFacto
return ow, oh return ow, oh
} }
func (c *gameForUI) setScreenClearedEveryFrame(cleared bool) {
c.m.Lock()
defer c.m.Unlock()
if c.offscreen != nil {
c.offscreen.mipmap.SetVolatile(cleared)
}
}
func (c *gameForUI) Update() error { func (c *gameForUI) Update() error {
return c.game.Update() return c.game.Update()
} }
func (c *gameForUI) Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection) error { func (c *gameForUI) Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection, screenClearedEveryFrame bool) error {
c.offscreen.mipmap.SetVolatile(screenClearedEveryFrame)
// Even though updateCount == 0, the offscreen is cleared and Draw is called. // Even though updateCount == 0, the offscreen is cleared and Draw is called.
// Draw should not update the game state and then the screen should not be updated without Update, but // Draw should not update the game state and then the screen should not be updated without Update, but
// users might want to process something at Draw with the time intervals of FPS. // users might want to process something at Draw with the time intervals of FPS.
if IsScreenClearedEveryFrame() { if screenClearedEveryFrame {
c.offscreen.Clear() c.offscreen.Clear()
} }
c.game.Draw(c.offscreen) c.game.Draw(c.offscreen)

View File

@ -64,6 +64,10 @@ func (m *Mipmap) SetIndependent(independent bool) {
} }
func (m *Mipmap) SetVolatile(volatile bool) { func (m *Mipmap) SetVolatile(volatile bool) {
if m.volatile == volatile {
return
}
m.volatile = volatile m.volatile = volatile
if m.volatile { if m.volatile {
m.disposeMipmaps() m.disposeMipmaps()

View File

@ -32,7 +32,7 @@ const DefaultTPS = 60
type Game interface { type Game interface {
Layout(outsideWidth, outsideHeight float64, deviceScaleFactor float64) (int, int) Layout(outsideWidth, outsideHeight float64, deviceScaleFactor float64) (int, int)
Update() error Update() error
Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection) error Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection, screenClearedEveryFrame bool) error
} }
type contextImpl struct { type contextImpl struct {
@ -100,7 +100,7 @@ func (c *contextImpl) updateFrameImpl(updateCount int, deviceScaleFactor float64
// Draw the game. // Draw the game.
screenScale, offsetX, offsetY := c.screenScaleAndOffsets(deviceScaleFactor) screenScale, offsetX, offsetY := c.screenScaleAndOffsets(deviceScaleFactor)
if err := c.game.Draw(screenScale, offsetX, offsetY, graphics().NeedsClearingScreen(), graphics().FramebufferYDirection()); err != nil { if err := c.game.Draw(screenScale, offsetX, offsetY, graphics().NeedsClearingScreen(), graphics().FramebufferYDirection(), theGlobalState.isScreenClearedEveryFrame()); err != nil {
return err return err
} }
@ -166,19 +166,21 @@ func (c *contextImpl) screenScaleAndOffsets(deviceScaleFactor float64) (float64,
} }
var theGlobalState = globalState{ var theGlobalState = globalState{
currentMaxTPS: DefaultTPS, maxTPS_: DefaultTPS,
isScreenClearedEveryFrame_: 1,
} }
// globalState represents a global state in this package. // globalState represents a global state in this package.
// This is available even before the game loop starts. // This is available even before the game loop starts.
type globalState struct { type globalState struct {
currentErr atomic.Value err_ atomic.Value
currentFPSMode int32 fpsMode_ int32
currentMaxTPS int32 maxTPS_ int32
isScreenClearedEveryFrame_ int32
} }
func (g *globalState) err() error { func (g *globalState) err() error {
err, ok := g.currentErr.Load().(error) err, ok := g.err_.Load().(error)
if !ok { if !ok {
return nil return nil
} }
@ -186,29 +188,41 @@ func (g *globalState) err() error {
} }
func (g *globalState) setError(err error) { func (g *globalState) setError(err error) {
g.currentErr.Store(err) g.err_.Store(err)
} }
func (g *globalState) fpsMode() FPSModeType { func (g *globalState) fpsMode() FPSModeType {
return FPSModeType(atomic.LoadInt32(&g.currentFPSMode)) return FPSModeType(atomic.LoadInt32(&g.fpsMode_))
} }
func (g *globalState) setFPSMode(fpsMode FPSModeType) { func (g *globalState) setFPSMode(fpsMode FPSModeType) {
atomic.StoreInt32(&g.currentFPSMode, int32(fpsMode)) atomic.StoreInt32(&g.fpsMode_, int32(fpsMode))
} }
func (g *globalState) maxTPS() int { func (g *globalState) maxTPS() int {
if g.fpsMode() == FPSModeVsyncOffMinimum { if g.fpsMode() == FPSModeVsyncOffMinimum {
return clock.SyncWithFPS return clock.SyncWithFPS
} }
return int(atomic.LoadInt32(&g.currentMaxTPS)) return int(atomic.LoadInt32(&g.maxTPS_))
} }
func (g *globalState) setMaxTPS(tps int) { func (g *globalState) setMaxTPS(tps int) {
if tps < 0 && tps != clock.SyncWithFPS { if tps < 0 && tps != clock.SyncWithFPS {
panic("ebiten: tps must be >= 0 or SyncWithFPS") panic("ebiten: tps must be >= 0 or SyncWithFPS")
} }
atomic.StoreInt32(&g.currentMaxTPS, int32(tps)) atomic.StoreInt32(&g.maxTPS_, int32(tps))
}
func (g *globalState) isScreenClearedEveryFrame() bool {
return atomic.LoadInt32(&g.isScreenClearedEveryFrame_) != 0
}
func (g *globalState) setScreenClearedEveryFrame(cleared bool) {
v := int32(0)
if cleared {
v = 1
}
atomic.StoreInt32(&g.isScreenClearedEveryFrame_, v)
} }
func SetError(err error) { func SetError(err error) {
@ -231,3 +245,11 @@ func MaxTPS() int {
func SetMaxTPS(tps int) { func SetMaxTPS(tps int) {
theGlobalState.setMaxTPS(tps) theGlobalState.setMaxTPS(tps)
} }
func IsScreenClearedEveryFrame() bool {
return theGlobalState.isScreenClearedEveryFrame()
}
func SetScreenClearedEveryFrame(cleared bool) {
theGlobalState.setScreenClearedEveryFrame(cleared)
}

12
run.go
View File

@ -77,8 +77,7 @@ func CurrentFPS() float64 {
} }
var ( var (
isScreenClearedEveryFrame = int32(1) isRunGameEnded_ = int32(0)
isRunGameEnded_ = int32(0)
) )
// SetScreenClearedEveryFrame enables or disables the clearing of the screen at the beginning of each frame. // SetScreenClearedEveryFrame enables or disables the clearing of the screen at the beginning of each frame.
@ -86,19 +85,14 @@ var (
// //
// SetScreenClearedEveryFrame is concurrent-safe. // SetScreenClearedEveryFrame is concurrent-safe.
func SetScreenClearedEveryFrame(cleared bool) { func SetScreenClearedEveryFrame(cleared bool) {
v := int32(0) ui.SetScreenClearedEveryFrame(cleared)
if cleared {
v = 1
}
atomic.StoreInt32(&isScreenClearedEveryFrame, v)
theGameForUI.setScreenClearedEveryFrame(cleared)
} }
// IsScreenClearedEveryFrame returns true if the frame isn't cleared at the beginning. // IsScreenClearedEveryFrame returns true if the frame isn't cleared at the beginning.
// //
// IsScreenClearedEveryFrame is concurrent-safe. // IsScreenClearedEveryFrame is concurrent-safe.
func IsScreenClearedEveryFrame() bool { func IsScreenClearedEveryFrame() bool {
return atomic.LoadInt32(&isScreenClearedEveryFrame) != 0 return ui.IsScreenClearedEveryFrame()
} }
type imageDumperGame struct { type imageDumperGame struct {