Syncframe

From codeTank

Jump to: navigation, search

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

See Also

Personal tools