playdate.getElapsedTime (high resolution timer) only accurate to milliseconds (Lua)

Summary: Inside Playdate documents the result of playdate.getElapsedTime() as being "a floating-point number with microsecond accuracy." The Simulator works as expected with a precision to about 3 microseconds. On my physical Playdate, playdate.getElapsedTime() only ever returns values precise to milliseconds. That seems less precise than the documention suggests.

This may be the same bug reported for the C API: [C SDK] High resolution timer has only millisecond precision - #2 by fum1h1ro

I have a simple program (below). On start it repeatedly runs a spinloop waiting for playdate.getElapsedTime() to return a different result. It prints to screen how many loops were needed for each. Then, after 1 second, it collects playdate.getElapsedTime() for 9 frames and displays that when done.

In the simulator (1.11.1 for Linux under Ubuntu 20.04.4), I get plausible looking results. It appears to be precise to about 3 ms. A transcription is below.

On a physical Playdate (running Playdate OS 1.11.1), it only appears to be precise to 1000 microseconds. A transcription is below.

To reproduce:

  1. Create project using the source code below.
  2. Compile to pdx
  3. Load onto phsyical Playdate
  4. Run on Playdate

Expected Behavior: No more than the last 2 digits for each time value are identical across all samples (which would indicate sub-millisecond precision)

Observed Behavior: Last 3 digits for each time value are 000, suggesting only millisecond precision.

The code I'm using:

import "CoreLibs/object"

local gfx = playdate.graphics

log = ""

function addlog(msg)
	local stamp = string.format("%.6f", playdate.getElapsedTime())
	log = log .. stamp .. " " .. msg .. "\n"
end

local msg = ""
playdate.resetElapsedTime()
for i = 1,10 do
	local start = playdate.getElapsedTime()
	local done = false
	local count = 0
	while not done do
		local now = playdate.getElapsedTime()
		if now ~= start then
			done = true
		end
		count = count + 1
	end
	addlog(tostring(count))
end
local msg = "spinloop count\n"..log
gfx.drawText(msg, 2, 2)

log = ""

local frame = 0
playdate.resetElapsedTime()
function playdate.update()
	frame = frame + 1
	if frame > 30 and frame < 40 then
		addlog(tostring(frame))
	elseif frame == 61 then
		local msg = "frames\n"..log
		gfx.drawText(msg, 200, 2)
	end
	--gfx.sprite.update()
	--playdate.resetElapsedTime()
end

Results from PlaydateSimulator, manually transcribed, may have typos:

Elapsed      Loops
0.000008       1
0.000018       2
0.000022       1
0.000026       2
0.000029       1
0.000033       3
0.000037       2
0.000041       2
0.000044       1
0.000048       2

Elapsed    Frames
1.321656      31
1.354444      32
1.387243      33
1.453728      34
1.458024      35
1.490204      36
1.522406      37
1.555363      38
1.588089      39

Results from physical Playdate, also manually transcribed, may have typos:

Elapsed      Loops
0.001000     132
0.002000     120
0.003000      93
0.004000     116
0.005000     124
0.006000     127
0.007000     125
0.008000     128
0.009000     125
0.010000     117

Elapsed    Frames
0.991000     31
1.023000     32
1.056000     33
1.089000     34
1.122000     35
1.156000     36
1.188000     37
1.221000     38
1.254000     39

(All numbers are being formatted with string.format("%.6f", time). If you add more digits, the physical device reports numbers within 3 microseconds. I believe it's likely an artifact of what the float can store.)

1 Like

Can confirm this at various setRefreshRate()

Screenshots are from device.

0 (unlocked/unlimited)
getelapsedtime 2022-06-06 09.18.12

30
getelapsedtime 2022-06-06 09.19.06

45
getelapsedtime 2022-06-06 09.24.12

50
getelapsedtime 2022-06-06 09.19.44

Thanks for the test game and details -- a fix will be in the next release!

1 Like