-
-
Notifications
You must be signed in to change notification settings - Fork 674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API to adjust FPS #2889
Comments
Adjusting FPS from program is impossible so I don't think I'll introduce this feature. Have you tried |
func main() {
SetScreenClearedEveryFrame(false)
SetVsyncEnabled(false)
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}
func (g *Game) Draw(screen *ebiten.Image) {
now := time.Now()
if now.Sub(g.lastRender) < time.Second / 60 {
return
}
g.lastRender = now
screen.Clear()
// ...
} |
yes I did try something similar long time ago, but then when the vsync was off, the FPS (number of the actual Draw calls) was gigantic and the CPU was still terribly devastated. It looks like the ebitengine with vsync off is trying it's best to squeeze every ounce of processing power that it can grab, no matter what's happening inside the Draw function |
Try this with the latest Ebitengine now. This should suppress CPU usages as long as Draw returns immediately. https://ebitengine.org/en/documents/2.5.html#GPU_Optimization_with_SetScreenClearedEveryFrame(false) |
Now I tested this package main
import (
"sync"
"github.com/hajimehoshi/ebiten/v2"
)
type Game struct {
once sync.Once
}
func (g *Game) Update() error {
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
}
func (g *Game) Layout(width, height int) (int, int) {
return width, height
}
func main() {
ebiten.SetScreenClearedEveryFrame(false)
ebiten.SetVsyncEnabled(false)
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
} On Windows, the CPU usage was about 50%, which was not expected. This is a bug and I'll try to fix this. On macOS, the CPU usage was less than 5%. |
it works a lot better now, good job!
Please try to run this code, on my machine when I set the FPS to 10, my ActualFPS is closer to around 80 (on previous versions of ebitengine swaps could be as high as 4k+ per second so we already have a massive upgrade) |
This method doesn' affect the actual FPS (i.e. how many times Draw is called per second), so |
Sure, I'll take a look tomorrow.
I guess this calculation can be more accurate by multiplying by time.Second / now.Sub(g.lastFpsCheck) |
I tested this program based on your program: package main
import (
"fmt"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
const FPS = 10 // change_me
func main() {
ebiten.SetScreenClearedEveryFrame(false)
ebiten.SetVsyncEnabled(false)
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}
type Game struct {
lastRender time.Time
lastFpsCheck time.Time
lastRealFps float64
realFps int
}
func (g *Game) Update() error {
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
now := time.Now()
if now.Sub(g.lastRender) < time.Second/FPS {
return
}
if delta := now.Sub(g.lastFpsCheck); delta > time.Second {
g.lastRealFps = float64(g.realFps) * float64(time.Second) / float64(delta)
g.realFps = 0
g.lastFpsCheck = now
}
g.realFps++
g.lastRender = now
screen.Clear()
ebitenutil.DebugPrint(screen, fmt.Sprintf("Desired FPS: %v\nReal FPS: %v\n\nFPS: %v\nTPS: %v", FPS, g.lastRealFps, ebiten.ActualFPS(), ebiten.ActualTPS()))
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
return outsideWidth, outsideHeight
} The result was about 8-9 real FPS.
I also tried this, and I could reproduce the 30ms things. My guess is that Windows sleeping timer is not so accurate as we expect unfortunately... This results seems to have a contradition with the previous experiment result (8-9 FPS), so I'll investigate more. |
OK I think this is not a contradition. (Real) FPS means the average Draw count per second, while there can be a big gap between two Draw calls. And, I found that the timer precision on Windows is pretty bad, and the smallest sleeping time was about 16[ms] unfortunately. This means that we cannot reach over 60FPS in this way. (Also, this means Ebitengine would not be able to implement good FPS adjustment). Sigh... |
@erexo Can other game engines adjust FPS by the way? |
of course, my previous game client could do that https://github.com/edubart/otclient/blob/e6861d79c90d1808bde3fd41d30b6458d1616bfe/src/framework/core/graphicalapplication.cpp#L198 I do however understand that this isn't that big of an issue and that it may be painful to implement, so please don't bother too much about it |
Note for myself: golang/go#44343 |
When SetScreenClearedEveryFalse(false) and SetVsyncEnabled(false), Draw might not be called as often as expected on Windows due to its timer precision. This change improves the situation. Updates #2889
@erexo Try the main branch (7e4cdf5). This is not a perfect solution and FPS might not reach to 60, but this should be better than before. In order to emulate higher FPS, we need shorter sleeping, which means more CPU consumption. I don't plan to backport this change as this is a little risky. Thanks! |
Operating System
What feature would you like to be added?
Currently we are able to somewhat limit the FPS by mingling with the
SetVsyncEnabled
function.The problem I have with this approach is that it accepts boolean, so we are in control of the FPS limiting but our control is limited to two states.
What I would really appreciate to see is something like
SetMaxFPS
which limits the FPS to given value when VSync is disabled.Why is this needed?
When VSync is off, the game can consume insane amount of processing power which is very inconvenient for a lot of users with older (and newer) PCs.
When VSync is on, players sometimes report that the game is laggy and that generally the performance is throttled, this is usually because their Monitor's FPS has invalid configuration on their Windows system (ie. Monitor is 144hz and Windows states that it's 60hz).
Usually a quick reconfiguration of player's system settings helps, but I encounter this problem over and over and I can't tell how many people just left the game in silence
It would be much much easier for me as a developer to just limit the FPS to 140 in code, so that despite the Monitor settings user will have a smooth experience that is not killing his machine at the same time.
The text was updated successfully, but these errors were encountered: