Syncframe
From codeTank
Contents |
Description
syncframe will attempt to synchronize the program to a certain frequency, by pausing the program if the previous call took place in less time than the specified time frame.
It might sound a little complicated, but it's much easier to understand when you consider a game loop. In a game loop, you might want the frame rate to be fixed at 30 frames per second. To achieve this, you would call syncframe(0.033) inside the main loop (you calculate 0.033 from 1 / 30). When the game loops around, syncframe will check to see if it's taken 0.033 seconds to get called in succession. If it's taken longer than 0.033 seconds, then it returns false, and exits immediately. If it's taken shorter than 0.033 seconds, then it pauses for the remaining amount (so the total time is 0.033 seconds), and returns true.
syncframe was created to perform this high-precision task. If implemented in Lua code, the delays caused by processing the script would interfere with the timing precision. By implementing it in the Brain Damage library, it's ensured to run as precise and efficient as possible, ensuring a more stable frame rate.
Arguments
syncframe(seconds_per_frame)
syncframe takes one argument - the number of seconds it takes per frame. If you want to achieve a certain frames per second, then simply invert that number to get the seconds per frame (seconds_per_frame = 1 / frames_per_second).
Please be aware that seconds_per_frame does not have to be constant. Every time you call syncframe, it checks to see if the previous call happened less than seconds_per_frame seconds ago. If it did, then it delays the difference, and returns true. If it didn't, then it returns false immediately.
The first call to syncframe will measure the difference in relation to the start of the program. Each successive call after that measures the difference in relation to the previous syncframe call.
Please note that syncframe is limited to a millisecond resolution. The smallest value that makes sense would be 0.001, because anything smaller would be simplified to 0 (which would always make syncframe return false).
Returns
syncframe will return true if it is successful at maintaining the frame rate, and false if it fails. More accurately, it returns true if a delay had to be added, and false if no delay was added. You can ignore this value if you want, or you can adjust your main loop in order to speed up or slow down dynamically according to this value.
Example
maxn = 0 failures = 0 while failures < 100 do -- perform our "work" cls() print(maxn) for i = 1, maxn do c = {} -- busy work end -- ensure our work only takes 0.01 seconds if (syncframe(0.01)) then -- syncframe succeeded, so we can afford to waste more time maxn = maxn + 5 failures = failures - 1 else -- syncframe failed, so we are spending too much time in our loop maxn = maxn - 1 failures = failures + 2 end end cls() print("To achieve 100-loops a second, we must create " .. maxn .. " dummy tables")
If you run this code, you will notice that it quickly climbs in the beginning, and then starts to hover around a particular value, until it exits. This shows how syncframe can be used to figure out how much work we can get done to maintain a frame rate (on my 2.8GHz machine, this code settled on around 7500 dummy tables).
Real-World Example
This is how you would use syncframe in a real game:
-- configuration: frames_per_second = 60 -- create your window: wnd = window.create{ -- create some function that does the "thinking" in the game: think = function(wnd) -- your code here, which should be called at the rate -- of frames_per_second end, -- create some function that does the "drawing" in the game: draw = function(wnd) -- your code here, which isn't called at any specific rate -- you'd probably just wnd:invalidate(false) for this end, -- other window stuff here } -- main loop: seconds_per_frame = 1 / frames_per_second frame_skip = 1 time_between = seconds_per_frame * frame_skip while window.getcount() > 0 do if (window.hasmessages()) then window.pumpmessages() else -- try to sync the frame... if it fails, then we need to think -- some more, and draw less if (syncframe(time_between)) then -- syncframe succeeds, so we can afford to waste more time... -- in that case, then draw more if (frame_skip > 1) then frame_skip = frame_skip - 1 time_between = seconds_per_frame * frame_skip end else -- syncframe fails, so we can't afford to waste any time... -- trim back our drawing frame_skip = frame_skip + 1 time_between = seconds_per_frame * frame_skip end -- think a bunch for frame_skip = 1, frame_skip do wnd:think() end -- draw what we just thought about wnd:draw() end end

