Re: Post-0.10.0 feature wishlist
Posted: Tue Oct 25, 2016 9:17 am
You can always track your own time using timer function. It's a high resolution timer so there aren't repercussions to using it.
Thats all true, but i dont want to track "my own time for something". I want to fine tune the love.run() loop.raidho36 wrote:You can always track your own time using timer function. It's a high resolution timer so there aren't repercussions to using it.
I'm pretty sure you do want to time how much time your code spends in the current love.run loop cycle.Fuzzlix wrote:Thats all true, but i dont want to track "my own time for something". I want to fine tune the love.run() loop.raidho36 wrote:You can always track your own time using timer function. It's a high resolution timer so there aren't repercussions to using it.
Well, that depends on the current screen's refresh rate, if it works, that is. I wouldn't rely on it a 100%.Fuzzlix wrote:One in most cases sufficient way is vsync=true but this is somehow out of my control.
Despite how the default love.run works, update time usually does not equal draw(render) time. Besides, with 20 FPS, your input will be choppy as well, even for a turn-based game as chess, 1/3th of a second of potential input delay is noticeable, and to be honest, quite irritating.Fuzzlix wrote:Please imagine the following situation: I create a chess like game. Most of the time the window needs no update. Therefore 20fps are more than enouth and my weak gpu will bcome less stressed.
Maintaining a constant loop time means you not messing with love.run (and love.timer.sleep) though. Also, dynamically calculating how much time has elapsed and sleeping that amount will never sleep for exactly how much you want it to... OS schedulers won't be nice to your program, just because you'd ask nicely; there may and probably will be drifts in the timing.Fuzzlix wrote:To maintain a constant loop time i need to know as exact as possible the time went by since the last love.update(). Knowing this time would allow me to sleep() until the next love.update() call. To figure this time out the getDelta() function is not helpfull because the returned deltatime is outdated (late by one loop).
I don't think it makes sense to have such a function. Your func1() describes exactly [wiki]love.timer.getDelta[/wiki]() and your func2() can easily be implemented like this:Fuzzlix wrote:It looks like, getDelta() returns the time between the last 2 step() calls? In this case the result of getDelta() would be outdated by one loop! Shure it is not a big issue but i cant get any information about the actual time spent since the last step() command and sometimes i need this information.
I suggest a new pair of functions which may be used in addition or as a replacement to the existing ones:
func1(): returning the time since the last step() command.
func2(): returning the time since the last step() command AND executing step().
Inside your love.run() you call......to get the time spent since the last love.update(dt) and reset the timer automatically.Code: Select all
love.update(func2())
In the next love.run() loop you can call func1() as often you wish to get the time spent since the last love.update() call without the 1-loop-delay the getDelta() function has.
Code: Select all
function func2()
love.timer.step()
return love.timer.getDelta()
end
Code: Select all
function func2()
local elapsed = love.timer.getDelta()
love.timer.step()
return elapsed
end
Code: Select all
local frozenDelta = 0
local lastTime = love.timer.getTime()
function step()
local oldTime = lastTime
lastTime = love.timer.getTime()
frozenDelta = lastTime - oldTime
end
function getDelta()
return frozenDelta
end
Exactly! that is the state now. i can call getDelta() as often as i wish and i get the same result until i call step(). As you mentioned, this delta time is one frame late! THIS is the state now and i have no chance to get a information about the actual time spent since the last step() call. This limits my abilities to fine tune the love.run() loop.pgimeno wrote: Or viceversa, if that's what you mean:but then the returned time would be 1 frame behind.Code: Select all
function func2() local elapsed = love.timer.getDelta() love.timer.step() return elapsed end
Ok lets try it in pseudo code:pgimeno wrote: Without having looked at the sources, my understanding is that the implementations of [wiki]love.timer.step[/wiki]() and [wiki]love.timer.getDelta[/wiki]() are functionally equivalent to the following:How do you propose any new functions to work in terms of [wiki]love.timer.getTime[/wiki]()?Code: Select all
local frozenDelta = 0 local lastTime = love.timer.getTime() function step() local oldTime = lastTime lastTime = love.timer.getTime() frozenDelta = lastTime - oldTime end function getDelta() return frozenDelta end
Code: Select all
local lastTime = love.timer.getTime();
function newStep()
local t = lastTime;
lastTime = love.timer.getTime();
return lastTime - t;
end;
function newGetDelta()
return love.timer.getTime() - lastTime;
end;
This is the way it's supposed to work, you then use the delta time to simulate the period between the start of the previous frame and this one. It works well, without any big surprises, and you get a consistent end result, one where all entities are at the "same position in time", the start of this frame.Fuzzlix wrote: As you mentioned, this delta time is one frame late!
This example has one frame of delay compared to the default [wiki]love.run[/wiki]. In that example, the past delta is retrieved and then step is called which updates the internal delta, but it's not retrieved until the next frame.Fuzzlix wrote:Exactly! that is the state now.pgimeno wrote: Or viceversa, if that's what you mean:but then the returned time would be 1 frame behind.Code: Select all
function func2() local elapsed = love.timer.getDelta() love.timer.step() return elapsed end
True! and using vsync i am fixed to the screen refresh rate.zorg wrote:Well, that depends on the current screen's refresh rate, if it works, that is. I wouldn't rely on it a 100%.Fuzzlix wrote:One in most cases sufficient way is vsync=true but this is somehow out of my control.
All truezorg wrote:Despite how the default love.run works, update time usually does not equal draw(render) time. Besides, with 20 FPS, your input will be choppy as well, even for a turn-based game as chess, 1/3th of a second of potential input delay is noticeable, and to be honest, quite irritating.Fuzzlix wrote:Please imagine the following situation: I create a chess like game. Most of the time the window needs no update. Therefore 20fps are more than enouth and my weak gpu will bcome less stressed.
Also, until you benchmarked exactly how a simple chess game can melt down your GPU, i'd tend to say you're trying to over-optimize (prematurely) right now.
Ok lets talk about pros and cons:zorg wrote:Note that i'm not saying you shouldn't experiment with love.run, only that what you're trying to do doesn't really make much sense to me, for your use-case. It will probably bring you more bugs than whatever you intended for it to solve.
Code: Select all
...
-- Update dt, as we'll be passing it to update
if love.timer then
dt = love.timer.step() -- no need for love.timer.getDelta() anymore
end
-- Call update and draw
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
if love.graphics and love.graphics.isActive() then
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.origin()
if love.draw then love.draw() end
love.graphics.present()
end
if love.timer then love.timer.sleep(0.001) end
Code: Select all
local fps = 20 -- or whatever you want as fps.
local sleepTime = 1 / fps;
...
-- Update dt, as we'll be passing it to update
if love.timer then
love.timer.sleep(max(sleepTime - (love.timer.getDelta()), 0.001));
dt = love.timer.step() -- no need for love.timer.getDelta() anymore
end
-- Call update and draw
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
if love.graphics and love.graphics.isActive() then
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.origin()
if love.draw then love.draw() end
love.graphics.present()
end
--if love.timer then love.timer.sleep(0.001) end
Code: Select all
love.run = function()
if love.math then
love.math.setRandomSeed(os.time())
end
if love.load then love.load(arg) end
-- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end
local dt = 0.0 -- delta time
local tr = 1/100 -- tick rate
local fr = 1/75 -- frame rate
local da = 0.0 -- draw accumulator
local ua = 0.0 -- update accumulator
-- Main loop time.
while true do
-- Process events.
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
-- Update dt, as we'll be passing it to update
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
da = da + dt
ua = ua + dt
end
-- Call audio
if love.atomic then love.atomic(dt) end
-- Call update
if ua > tr then
if love.update then
love.update(tr) -- will pass 0 if love.timer is disabled
end
ua = ua % tr
end
-- Call draw
if da > fr then
if love.graphics and love.graphics.isActive() then
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.origin()
if love.draw then love.draw() end -- no interpolation
love.graphics.present()
end
da = da % fr
end
-- Optimal sleep time, anything higher does not go below 0.40 % cpu
-- utilization; 0.001 results in 0.72 %, so this is an improvement. (on my computer anyway, results may vary)
if love.timer then love.timer.sleep(0.002) end
end
end