ebiten: provide SetScreenFilterEnabled, a way to disable the default screen filter (#1997)

Using FilterNearest rather than filterScreen or FilterLinear speeds up a
Dell E6500 from 23fps->36fps and 27fps->48fps.

Updates #1772
This commit is contained in:
divVerent 2022-02-23 13:46:27 -05:00 committed by GitHub
parent 99aef33970
commit ea35296be7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 3 deletions

View File

@ -73,7 +73,7 @@ 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, clearScreenEveryFrame bool) error { func (c *gameForUI) Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection, clearScreenEveryFrame, filterEnabled bool) error {
c.offscreen.mipmap.SetVolatile(clearScreenEveryFrame) c.offscreen.mipmap.SetVolatile(clearScreenEveryFrame)
// Even though updateCount == 0, the offscreen is cleared and Draw is called. // Even though updateCount == 0, the offscreen is cleared and Draw is called.
@ -107,6 +107,8 @@ func (c *gameForUI) Draw(screenScale float64, offsetX, offsetY float64, needsCle
op.CompositeMode = CompositeModeCopy op.CompositeMode = CompositeModeCopy
switch { switch {
case !filterEnabled:
op.Filter = FilterNearest
case math.Floor(s) == s: case math.Floor(s) == s:
op.Filter = FilterNearest op.Filter = FilterNearest
case s > 1: case s > 1:

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, screenClearedEveryFrame bool) error Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection, screenClearedEveryFrame, filterEnabled bool) error
} }
type contextImpl struct { type contextImpl struct {
@ -106,7 +106,7 @@ func (c *contextImpl) updateFrameImpl(updateCount int, outsideWidth, outsideHeig
// 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(), theGlobalState.isScreenClearedEveryFrame()); err != nil { if err := c.game.Draw(screenScale, offsetX, offsetY, graphics().NeedsClearingScreen(), graphics().FramebufferYDirection(), theGlobalState.isScreenClearedEveryFrame(), theGlobalState.isScreenFilterEnabled()); err != nil {
return err return err
} }
@ -163,6 +163,7 @@ func (c *contextImpl) screenScaleAndOffsets(deviceScaleFactor float64) (float64,
var theGlobalState = globalState{ var theGlobalState = globalState{
maxTPS_: DefaultTPS, maxTPS_: DefaultTPS,
isScreenClearedEveryFrame_: 1, isScreenClearedEveryFrame_: 1,
screenFilterEnabled_: 1,
} }
// globalState represents a global state in this package. // globalState represents a global state in this package.
@ -172,6 +173,7 @@ type globalState struct {
fpsMode_ int32 fpsMode_ int32
maxTPS_ int32 maxTPS_ int32
isScreenClearedEveryFrame_ int32 isScreenClearedEveryFrame_ int32
screenFilterEnabled_ int32
} }
func (g *globalState) err() error { func (g *globalState) err() error {
@ -220,6 +222,18 @@ func (g *globalState) setScreenClearedEveryFrame(cleared bool) {
atomic.StoreInt32(&g.isScreenClearedEveryFrame_, v) atomic.StoreInt32(&g.isScreenClearedEveryFrame_, v)
} }
func (g *globalState) isScreenFilterEnabled() bool {
return graphicsdriver.Filter(atomic.LoadInt32(&g.screenFilterEnabled_)) != 0
}
func (g *globalState) setScreenFilterEnabled(enabled bool) {
v := int32(0)
if enabled {
v = 1
}
atomic.StoreInt32(&g.screenFilterEnabled_, v)
}
func SetError(err error) { func SetError(err error) {
theGlobalState.setError(err) theGlobalState.setError(err)
} }
@ -248,3 +262,11 @@ func IsScreenClearedEveryFrame() bool {
func SetScreenClearedEveryFrame(cleared bool) { func SetScreenClearedEveryFrame(cleared bool) {
theGlobalState.setScreenClearedEveryFrame(cleared) theGlobalState.setScreenClearedEveryFrame(cleared)
} }
func IsScreenFilterEnabled() bool {
return theGlobalState.isScreenFilterEnabled()
}
func SetScreenFilterEnabled(enabled bool) {
theGlobalState.setScreenFilterEnabled(enabled)
}

20
run.go
View File

@ -94,6 +94,26 @@ func IsScreenClearedEveryFrame() bool {
return ui.IsScreenClearedEveryFrame() return ui.IsScreenClearedEveryFrame()
} }
// SetScreenFilterEnabled enables/disables the use of the "screen" filter Ebiten uses.
//
// The "screen" filter is a box filter from game to display resolution.
//
// If disabled, nearest-neighbor filtering will be used for scaling instead.
//
// The default state is true.
//
// SetScreenFilterEnabled is concurrent-safe, but takes effect only at the next Draw call.
func SetScreenFilterEnabled(enabled bool) {
ui.SetScreenFilterEnabled(enabled)
}
// IsScreenFilterEnabled returns true if Ebiten's "screen" filter is enabled.
//
// IsScreenFilterEnabled is concurrent-safe.
func IsScreenFilterEnabled() bool {
return ui.IsScreenFilterEnabled()
}
type imageDumperGame struct { type imageDumperGame struct {
game Game game Game
d *imageDumper d *imageDumper