batteries icon indicating copy to clipboard operation
batteries copied to clipboard

Remove requirement to bit library in colour module.

Open MikuAuahDark opened this issue 3 years ago • 2 comments

I have not fully tested this, so please do.

MikuAuahDark avatar May 09 '22 02:05 MikuAuahDark

will need to find some time to write tests to ensure this is working as intended!

the amount of modulos make me a bit nervous performance wise though vs the bit operations, especially when compiled. i think i'd prefer a flag to use the bit library for speed rather than just removing the dependency entirely.

1bardesign avatar May 18 '22 23:05 1bardesign

While it's true that the modulos are scary, looking at luajit.me, the bitshifts itself perform a double-to-integer conversion, shift, then integer-to-double conversion. While the modulo version performs everything at double without any conversion.

Still, I think it's also good idea to check the correctness AND benchmark it.

MikuAuahDark avatar May 18 '22 23:05 MikuAuahDark

benchmark/test

do
	local start_time = love.timer.getTime()
	local test_repeats = 1000
	local iters = 100
	local abs = math.abs
	for _ = 1, test_repeats do
		local accum = 0
		for i = 1, iters do
			--computed so it can't be inlined
			local r, g, b, a = 1 / i, 0.5 / i, 0.25 / i, i / 100

			--test pack
			local rgb = colour.pack_rgb(r, g, b)
			local rgba = colour.pack_rgba(r, g, b, a)
			local argb = colour.pack_argb(r, g, b, a)
			accum = accum + rgb + rgba

			local r1, g1, b1 = colour.unpack_rgb(rgb)
			local r2, g2, b2, a2 = colour.unpack_rgba(rgba)
			local r3, g3, b3, a3 = colour.unpack_argb(argb)

			--check
			if abs(r1 - r) > 1 / 256 then error(("bad unpack: %f vs %f"):format(r1, r)) end
			if abs(g1 - g) > 1 / 256 then error(("bad unpack: %f vs %f"):format(g1, g)) end
			if abs(b1 - b) > 1 / 256 then error(("bad unpack: %f vs %f"):format(b1, b)) end

			if abs(r2 - r) > 1 / 256 then error(("bad unpack: %f vs %f"):format(r2, r)) end
			if abs(g2 - g) > 1 / 256 then error(("bad unpack: %f vs %f"):format(g2, g)) end
			if abs(b2 - b) > 1 / 256 then error(("bad unpack: %f vs %f"):format(b2, b)) end
			if abs(a2 - a) > 1 / 256 then error(("bad unpack: %f vs %f"):format(a2, a)) end

			if abs(r3 - r) > 1 / 256 then error(("bad unpack: %f vs %f"):format(r3, r)) end
			if abs(g3 - g) > 1 / 256 then error(("bad unpack: %f vs %f"):format(g3, g)) end
			if abs(b3 - b) > 1 / 256 then error(("bad unpack: %f vs %f"):format(b3, b)) end
			if abs(a3 - a) > 1 / 256 then error(("bad unpack: %f vs %f"):format(a3, a)) end
		end
	end
	local end_time = love.timer.getTime()
	local total_time = (end_time - start_time)

	print(("total time: %0.4fms per test: %0.4fms"):format(total_time * 1000, total_time * 1000 / test_repeats))
	love.event.quit()
	return
end

without bit

[max@compy-mj arco]$ love .
total time: 6.9082ms per test: 0.0069ms
[max@compy-mj arco]$ love .
total time: 6.6903ms per test: 0.0067ms
[max@compy-mj arco]$ love .
total time: 6.7483ms per test: 0.0067ms

with bit

[max@compy-mj arco]$ love .
total time: 6.5429ms per test: 0.0065ms
[max@compy-mj arco]$ love .
total time: 6.6917ms per test: 0.0067ms
[max@compy-mj arco]$ love .
total time: 6.5934ms per test: 0.0066ms

ie very slightly faster with bit

after talking with @slime73 via discord i decided to just keep both implementations and pick one depending on if the bit module is present or not, which doesn't cost much other than code size and maintainability. Will pull in your commit so you hopefully get correct attribution according to git, will see how the merge looks.

1bardesign avatar Sep 13 '22 01:09 1bardesign

we can consider something (global or inline local config var) to force no bitops if required :+1:

1bardesign avatar Sep 13 '22 01:09 1bardesign