-
-
Notifications
You must be signed in to change notification settings - Fork 652
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
ebiten: deprecate FPSMode
, undeprecate SetVsyncEnable
, and add more APIs to control TPS/FPS
#2338
Comments
One point I'm not fullly convinced is that whether we can allow |
While I think it's ok to use Right now, users are led to think about how |
Hmm. Another behavior and implementation question for |
We can add |
SetUpdateMode(ebitengine.UpdateModeTicksPerSecond)
SetUpdateMode(ebitengine.UpdateModeImmediate) // maybe ebitengine.UpdateModeNoWait is better?
SetUpdateMode(ebitengine.UpdateModeWaitForInput)
SetTPS(40) // only applies if mode is ticks per second This doesn't break compatibility if |
Thanks,
I'm not convinced with the name. Update is followed by Draw, but what is 'immediate'? |
Immediate or no_wait means that when update can be called in the loop, it's called without any previous wait (either due to time or due to waiting for input events). But the loop of update + draw means you still have to wait for draw to be called before being able to call update again (as opposed to an infinite update loop). The fact that in this mode update is called exactly as many times as draw is another particularity that we may want to reflect in the name (this doesn't happen with the ticks_per_second mode). But this also happens with the wait_for_input mode. I simply didn't find any good way to refer to this in the name. |
Would this be clearer? SetUpdateMode(ebitengine.UpdateModeTicks)
SetUpdateMode(ebitengine.UpdateModeFrames)
SetUpdateMode(ebitengine.UpdateModeWaitForInput) |
I don't really understand why is it called |
A frame is a time unit to call Draw. (FPS is how many Draw is called per frame) |
So I adopted the word |
For Update,
For Draw,
So there are a lot of pairs, but these are not orthogonal... |
Frame: Draw timing. This is based on vsync-on, vsync-off, or input (FPSModeOffMinimum) @tinne26 do we agree with these definitions? |
I assume you meant how many
The substitution makes sense in this context, but my point is that this context on itself, the idea of
I don't agree with separating this as two cases. They are both the same, update doesn't wait. The only part that may force waits is vsync. If vsync is on, buffer swaps on draw will block. If vsync is off, no one will block and the loop will run as fast as possible.
I don't know why you have 4 cases. To me, it's all one case: after the update part of the loop (and doesn't matter whether update has been called zero, one or twenty times), draw is called one time unless we are skipping the draw. Whether the buffer swap blocks or not depends only on the vsync configuration.
This definition is ok, but the problem is that this is something that we can estimate based on the refresh rate and we can measure in practice, but this is not something we can set directly. We do not use this value to wait, we do not create a timer to control it, so we shouldn't use it in contexts where we are setting values.
A tick is a time-based "signal" that indicates us that we can call |
My opinion is different: Tick is a time unit for one Update, and |
Even with the 'immediate' mode, TPS (ticks per second) still represents how many times Update is called per second, whichever this value is useful or not. |
Ok, now we are finally getting somehwere. So, there are two options here:
If you want to call any of those ticks, ok. Let's temporarily rename |
No, in the immediate mode the TPS value is not used for anything. |
I'm talking about the current situation (how Tick is used) |
Hmm, ok. I'm fine with changing the definition of tick from Update to time, to avoid confustion. I don't want to deprecate I'll try to read your suggestion more carefully later (as I'm gonna bed soon) |
My understanding is what you are suggesting is:
Is that correct?
|
Yeah the first part is fundamentally correct. Only that I don't really say much about a "frame". For me, a frame is the time elapsed between draws or the resulting image created during that time. So, the current model also matches my understanding of frame. It's just that I don't use that value for configuring anything, so it's not really very relevant. For the second part, that's not quite right:
|
I feel like your 'loop' is close to the current 'frame'.
So even if vsync is off, Draw is called only after Update, if the mode is wait_for_input, right? It's because Update and Draw share the same 'loop' and the wait_for_input mode blocks Update. So the next question is, if TPS is very low like 1, is Draw affected? |
In the case of wait_for_input, |
So |
It can be weird, I don't know. Here's the pseudo-code structure to make sure we are on the same page, I find it mostly reasonable: https://tinne26.github.io/ebi-main-loop/ |
So there can be a better name, like Nice! |
I think this can be slightly different, since even if Draw is skipped, swapping buffer should happen to wait for vsync correctly. My understanding is this:
|
Sure, I mean, that part is unclear to me. If the buffer swap won't increase the GPU usage and serves as a wait, sure, that would be better. I don't know how it would be done in DirectX and Metal, but yeah, if that's possible it would be ideal. Also, |
What about adding these APIs?
We would keep The advantage of my suggestion compared to @tinne26's suggestion is that 1) the blocking behavior is very clear, 2) we don't have to change the notion of 'tick' and 'frame'. The odd combination would be like |
This is interesting, and I basically agree with your general evaluation, but I'm not convinced. As you say, the blocking behavior is clear, but I think it moves us away from the main concept of a loop with
Definitely ignore the TPS. To me this extremely clear with the About the notions of tick and frame, I don't think our notions of frame are very different, as I've already mentioned in other comments. And as for ticks, we don't know what most Ebitengine users have in mind, so I don't think this point is unequivocal. Right now I'd rather take Summarizing all the main proposals up to this point:
Given that there's still considerable work to do to get |
A third approach is this one, which I mentioned ealier: SetLoopMode(ebiten.UpdateTPS, ebiten.DrawVsyncOff) // or UpdateFixedTimestep, etc.
SetLoopMode(ebiten.UpdateOnce, ebiten.DrawVsyncOn) // or Immediate, NoBlock, something about frames, etc.
SetLoopMode(ebiten.UpdateWaitInput, ebiten.DrawVsyncOn) // or UpdateOnInput, etc.
// SetLoopMode(ebiten.UpdateOnRequest, ebiten.DrawVsyncOff) // example, hypothetical addition for async input I like it conceptually (if we improve the constant names, and maybe |
What I'm not convined is mixing
Yeah I separate SyncWithFPS by keeping the existing API on purpose, as I described above. |
I totally agree with this point, but I do not believe that highlighting the blocking at the expense of being less explicit about What about focusing on intended usage instead of update / draw / blocking / ticks / frames? SetLoopMode(ebitengine.LoopModeFixedTimestep)
SetLoopMode(ebitengine.LoopModeDeltaTime) // or VariableTimestep
SetLoopMode(ebitengine.LoopModeInputBlock) It's true that modes behave very differently (in fact, fixed timestep is also very different because it's the only that has 0-N updates), but they are different loop modes anyway, and the loop involves both update and draw, so it seems fair to me. |
I am late to the discussion, @tinne26 suggestions make sense but look heavy for a single issue I believe. // FPS capped to refresh rate or not, FPS as the number of calls to Draw() per second
SetVsyncEnabled(bool) // Undeprecate as suggested
// Sets the number of times Update() will be called
const (
TPSOnInputEvent = -2 // Whatever the name
TPSUncapped = -1
)
SetTPS(int) // Special values as UncappedTPS / OnInputEvent
var SkipDraw error // Whatever the name
Update() error // Special error to skip the next few Draw() before the next update, by re-using same buffer These are the simplest changes I can come up with, as a first step not to complicate our dear "dead simple library"
I think this welcomes most (I might have missed some) use cases without changing the API too much. The special error returned from // This would not be the SetFPSMode we know, but an extra addition that can synergize with previously suggested SetVsyncEnabled
const (
FPSModeAuto mode = 0
FPSModeManual = 1
)
SetFPSMode(mode) // Mode being FPSModeManual as on-request and would default to FPSModeAuto (no specific behaviour)
// Probably known previously as ScheduleFrame, but allows for scheduling next few frames (not a single one, but those between the next call to Update) in a lower TPS + high FPS setup for example
// If this function is called in FPSModeAuto, it has no effect
// If this function is not called in FPSModeManual, the previous buffer is re-used
ScheduleFrame() // with a 's' or "NextFrames" whatever name we can come up with. But this would create the same thing I pointed out with SkipDraw as it can be called anytime but should only be called in Update
|
What you explain is the
Because...
But it is sus, both semantically and technically. Even under Hajime's understanding of ticks, We haven't mentioned it explicitly, but I think we all agree that if the changes are considered too messy, we can always leave it for v3. But both On the topic of
Maybe there was some confusion, but at least I didn't suggest this in this issue (though we did mention it on Discord). My preference is to detect if the |
We discussed on the Discord server and concluded that:
Let's focus on the first two items. |
FPSModeVsyncOffMinimum
is quite odd since a) this affects TPS and b) this consumes GPU power unnecessarily (e.g. when a mouse is dragged).To solve these issues, we have discussed at the Discord server and came up with these ideas, instead of introducing a new FPS mode like
FPSModeVsyncOnMinimum
:SetVsyncEnabled
and deprecateFPSMode
to make the API orthogonal with new APIsSyncWithInput
, with whichUpdate
is called only when inputting occurs.Draw
's timing is also adjusted. When vsync is on, Draw is also called only when inputting occurs, but the maximum is limited by vsync. When vsync is off, Draw is not limited by vsync. This should be the same asFPSModeVsyncOffMinimum
. AsSetTPS
affectsDraw
, this might be odd, but basically we cannot control the timings ofDraw
directly in the first place. This solves the problem a) by the clearner APIs and also b) partially bySyncWithInput
with vsync-on.SkipDraw
, which skips Draw (and the previous framebuffer is used for swapping framebuffers). This solves the problem b) by suppressing Draw (e.g. when a mouse is dragged).@tinne26 @Zyko0 Any thoughts?
The text was updated successfully, but these errors were encountered: