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 {
c.offscreen = NewImage(ow, oh)
c.offscreen.mipmap.SetVolatile(IsScreenClearedEveryFrame())
// 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.
@ -74,24 +73,17 @@ func (c *gameForUI) Layout(outsideWidth, outsideHeight float64, deviceScaleFacto
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 {
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.
// 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.
if IsScreenClearedEveryFrame() {
if screenClearedEveryFrame {
c.offscreen.Clear()
}
c.game.Draw(c.offscreen)

View File

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

View File

@ -32,7 +32,7 @@ const DefaultTPS = 60
type Game interface {
Layout(outsideWidth, outsideHeight float64, deviceScaleFactor float64) (int, int)
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 {
@ -100,7 +100,7 @@ func (c *contextImpl) updateFrameImpl(updateCount int, deviceScaleFactor float64
// Draw the game.
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
}
@ -166,19 +166,21 @@ func (c *contextImpl) screenScaleAndOffsets(deviceScaleFactor float64) (float64,
}
var theGlobalState = globalState{
currentMaxTPS: DefaultTPS,
maxTPS_: DefaultTPS,
isScreenClearedEveryFrame_: 1,
}
// globalState represents a global state in this package.
// This is available even before the game loop starts.
type globalState struct {
currentErr atomic.Value
currentFPSMode int32
currentMaxTPS int32
err_ atomic.Value
fpsMode_ int32
maxTPS_ int32
isScreenClearedEveryFrame_ int32
}
func (g *globalState) err() error {
err, ok := g.currentErr.Load().(error)
err, ok := g.err_.Load().(error)
if !ok {
return nil
}
@ -186,29 +188,41 @@ func (g *globalState) err() error {
}
func (g *globalState) setError(err error) {
g.currentErr.Store(err)
g.err_.Store(err)
}
func (g *globalState) fpsMode() FPSModeType {
return FPSModeType(atomic.LoadInt32(&g.currentFPSMode))
return FPSModeType(atomic.LoadInt32(&g.fpsMode_))
}
func (g *globalState) setFPSMode(fpsMode FPSModeType) {
atomic.StoreInt32(&g.currentFPSMode, int32(fpsMode))
atomic.StoreInt32(&g.fpsMode_, int32(fpsMode))
}
func (g *globalState) maxTPS() int {
if g.fpsMode() == FPSModeVsyncOffMinimum {
return clock.SyncWithFPS
}
return int(atomic.LoadInt32(&g.currentMaxTPS))
return int(atomic.LoadInt32(&g.maxTPS_))
}
func (g *globalState) setMaxTPS(tps int) {
if tps < 0 && tps != clock.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) {
@ -231,3 +245,11 @@ func MaxTPS() int {
func SetMaxTPS(tps int) {
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 (
isScreenClearedEveryFrame = int32(1)
isRunGameEnded_ = int32(0)
isRunGameEnded_ = int32(0)
)
// SetScreenClearedEveryFrame enables or disables the clearing of the screen at the beginning of each frame.
@ -86,19 +85,14 @@ var (
//
// SetScreenClearedEveryFrame is concurrent-safe.
func SetScreenClearedEveryFrame(cleared bool) {
v := int32(0)
if cleared {
v = 1
}
atomic.StoreInt32(&isScreenClearedEveryFrame, v)
theGameForUI.setScreenClearedEveryFrame(cleared)
ui.SetScreenClearedEveryFrame(cleared)
}
// IsScreenClearedEveryFrame returns true if the frame isn't cleared at the beginning.
//
// IsScreenClearedEveryFrame is concurrent-safe.
func IsScreenClearedEveryFrame() bool {
return atomic.LoadInt32(&isScreenClearedEveryFrame) != 0
return ui.IsScreenClearedEveryFrame()
}
type imageDumperGame struct {