Browse Source

Implemented LuaJIT FFI versions of love.math.random and RandomGenerator:random.

Alex Szpakowski 9 years ago
parent
commit
52b1bd78f2

+ 4 - 0
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -840,6 +840,7 @@
 		FA27B3C61B4985D8008A9DCE /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3C31B4985D8008A9DCE /* Video.cpp */; };
 		FA27B3C61B4985D8008A9DCE /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3C31B4985D8008A9DCE /* Video.cpp */; };
 		FA27B3C71B4985D8008A9DCE /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3C41B4985D8008A9DCE /* Video.h */; };
 		FA27B3C71B4985D8008A9DCE /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3C41B4985D8008A9DCE /* Video.h */; };
 		FA27B3C91B498623008A9DCE /* Theora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA27B3C81B498623008A9DCE /* Theora.framework */; };
 		FA27B3C91B498623008A9DCE /* Theora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA27B3C81B498623008A9DCE /* Theora.framework */; };
+		FA2E9BFF1C19E00C0004A1EE /* wrap_RandomGenerator.lua in Resources */ = {isa = PBXBuildFile; fileRef = FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */; };
 		FA317EBA18F28B6D00B0BCD7 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FA317EB918F28B6D00B0BCD7 /* libz.dylib */; };
 		FA317EBA18F28B6D00B0BCD7 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FA317EB918F28B6D00B0BCD7 /* libz.dylib */; };
 		FA41A3C81C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; };
 		FA41A3C81C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; };
 		FA41A3C91C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; };
 		FA41A3C91C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; };
@@ -1519,6 +1520,7 @@
 		FA27B3C81B498623008A9DCE /* Theora.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Theora.framework; path = /Library/Frameworks/Theora.framework; sourceTree = "<absolute>"; };
 		FA27B3C81B498623008A9DCE /* Theora.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Theora.framework; path = /Library/Frameworks/Theora.framework; sourceTree = "<absolute>"; };
 		FA283EDC1B27CFAA00C70067 /* nogame.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = nogame.lua; sourceTree = "<group>"; };
 		FA283EDC1B27CFAA00C70067 /* nogame.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = nogame.lua; sourceTree = "<group>"; };
 		FA283EDD1B27CFAA00C70067 /* nogame.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nogame.lua.h; sourceTree = "<group>"; };
 		FA283EDD1B27CFAA00C70067 /* nogame.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nogame.lua.h; sourceTree = "<group>"; };
+		FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_RandomGenerator.lua; sourceTree = "<group>"; };
 		FA317EB918F28B6D00B0BCD7 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
 		FA317EB918F28B6D00B0BCD7 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
 		FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ASTCHandler.cpp; sourceTree = "<group>"; };
 		FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ASTCHandler.cpp; sourceTree = "<group>"; };
 		FA41A3C71C0A1F950084430C /* ASTCHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTCHandler.h; sourceTree = "<group>"; };
 		FA41A3C71C0A1F950084430C /* ASTCHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTCHandler.h; sourceTree = "<group>"; };
@@ -2461,6 +2463,7 @@
 				FA7DA04C1C16874A0056B200 /* wrap_Math.lua */,
 				FA7DA04C1C16874A0056B200 /* wrap_Math.lua */,
 				FA0B7C0B1A95902C000E1D17 /* wrap_RandomGenerator.cpp */,
 				FA0B7C0B1A95902C000E1D17 /* wrap_RandomGenerator.cpp */,
 				FA0B7C0C1A95902C000E1D17 /* wrap_RandomGenerator.h */,
 				FA0B7C0C1A95902C000E1D17 /* wrap_RandomGenerator.h */,
+				FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */,
 			);
 			);
 			path = math;
 			path = math;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -3297,6 +3300,7 @@
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
 				FA19C4C81B4B0BD50059B0B3 /* wrap_Video.lua in Resources */,
 				FA19C4C81B4B0BD50059B0B3 /* wrap_Video.lua in Resources */,
+				FA2E9BFF1C19E00C0004A1EE /* wrap_RandomGenerator.lua in Resources */,
 				FA7DA04D1C16874A0056B200 /* wrap_Math.lua in Resources */,
 				FA7DA04D1C16874A0056B200 /* wrap_Math.lua in Resources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;

+ 11 - 3
src/modules/math/wrap_Math.cpp

@@ -39,9 +39,10 @@ namespace love
 namespace math
 namespace math
 {
 {
 
 
-int w_random(lua_State *L)
+int w__random(lua_State *L)
 {
 {
-	return luax_getrandom(L, 1, Math::instance.random());
+	lua_pushnumber(L, Math::instance.random());
+	return 1;
 }
 }
 
 
 int w_randomNormal(lua_State *L)
 int w_randomNormal(lua_State *L)
@@ -422,6 +423,8 @@ int w_decompress(lua_State *L)
 // C functions in a struct, necessary for the FFI versions of math functions.
 // C functions in a struct, necessary for the FFI versions of math functions.
 struct FFI_Math
 struct FFI_Math
 {
 {
+	double (*random)(void);
+
 	float (*noise1)(float x);
 	float (*noise1)(float x);
 	float (*noise2)(float x, float y);
 	float (*noise2)(float x, float y);
 	float (*noise3)(float x, float y, float z);
 	float (*noise3)(float x, float y, float z);
@@ -433,6 +436,11 @@ struct FFI_Math
 
 
 static FFI_Math ffifuncs =
 static FFI_Math ffifuncs =
 {
 {
+	[](void) -> double // random
+	{
+		return Math::instance.random();
+	},
+
 	[](float x) -> float // noise1
 	[](float x) -> float // noise1
 	{
 	{
 		return Math::instance.noise(x);
 		return Math::instance.noise(x);
@@ -463,7 +471,7 @@ static FFI_Math ffifuncs =
 // List of functions to wrap.
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
-	{ "random", w_random },
+	{ "_random", w__random }, // love.math.random is inside wrap_Math.lua.
 	{ "randomNormal", w_randomNormal },
 	{ "randomNormal", w_randomNormal },
 	{ "setRandomSeed", w_setRandomSeed },
 	{ "setRandomSeed", w_setRandomSeed },
 	{ "getRandomSeed", w_getRandomSeed },
 	{ "getRandomSeed", w_getRandomSeed },

+ 34 - 6
src/modules/math/wrap_Math.lua

@@ -22,7 +22,30 @@ misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.
 3. This notice may not be removed or altered from any source distribution.
 --]]
 --]]
 
 
-local math, ffifuncspointer = ...
+local love_math, ffifuncspointer = ...
+
+local type, tonumber, error = type, tonumber, error
+local floor = math.floor
+
+local _random = love_math._random
+
+local function getrandom(r, l, u)
+	if u ~= nil then
+		if type(r) ~= "number" then error("bad argument #1 to 'random' (number expected)", 2) end
+		if type(l) ~= "number" then error("bad argument #2 to 'random' (number expected)", 2) end
+		return floor(r * (u - l + 1)) + l
+	elseif l ~= nil then
+		if type(l) ~= "number" then error("bad argument #1 to 'random' (number expected)", 2) end
+		return floor(r * l) + 1
+	else
+		return r
+	end
+end
+
+function love_math.random(l, u)
+	local r = _random()
+	return getrandom(r, l, u)
+end
 
 
 if type(jit) ~= "table" or not jit.status() then
 if type(jit) ~= "table" or not jit.status() then
 	-- LuaJIT's FFI is *much* slower than LOVE's regular methods when the JIT
 	-- LuaJIT's FFI is *much* slower than LOVE's regular methods when the JIT
@@ -33,12 +56,12 @@ end
 local status, ffi = pcall(require, "ffi")
 local status, ffi = pcall(require, "ffi")
 if not status then return end
 if not status then return end
 
 
-local type, tonumber = type, tonumber
-
 -- Matches the struct declaration in wrap_Math.cpp.
 -- Matches the struct declaration in wrap_Math.cpp.
 pcall(ffi.cdef, [[
 pcall(ffi.cdef, [[
 typedef struct FFI_Math
 typedef struct FFI_Math
 {
 {
+	double (*random)(void);
+
 	float (*noise1)(float x);
 	float (*noise1)(float x);
 	float (*noise2)(float x, float y);
 	float (*noise2)(float x, float y);
 	float (*noise3)(float x, float y, float z);
 	float (*noise3)(float x, float y, float z);
@@ -54,7 +77,12 @@ local ffifuncs = ffi.cast("FFI_Math *", ffifuncspointer)
 
 
 -- Overwrite some regular love.math functions with FFI implementations.
 -- Overwrite some regular love.math functions with FFI implementations.
 
 
-function math.noise(x, y, z, w)
+function love_math.random(l, u)
+	local r = tonumber(ffifuncs.random())
+	return getrandom(r, l, u)
+end
+
+function love_math.noise(x, y, z, w)
 	if w ~= nil then
 	if w ~= nil then
 		return tonumber(ffifuncs.noise4(x, y, z, w))
 		return tonumber(ffifuncs.noise4(x, y, z, w))
 	elseif z ~= nil then
 	elseif z ~= nil then
@@ -73,7 +101,7 @@ local function gammaToLinear(c)
 	return c
 	return c
 end
 end
 
 
-function math.gammaToLinear(r, g, b, a)
+function love_math.gammaToLinear(r, g, b, a)
 	if type(r) == "table" then
 	if type(r) == "table" then
 		local t = r
 		local t = r
 		return gammaToLinear(t[1]), gammaToLinear(t[2]), gammaToLinear(t[3]), t[4]
 		return gammaToLinear(t[1]), gammaToLinear(t[2]), gammaToLinear(t[3]), t[4]
@@ -88,7 +116,7 @@ local function linearToGamma(c)
 	return c
 	return c
 end
 end
 
 
-function math.linearToGamma(r, g, b, a)
+function love_math.linearToGamma(r, g, b, a)
 	if type(r) == "table" then
 	if type(r) == "table" then
 		local t = r
 		local t = r
 		return linearToGamma(t[1]), linearToGamma(t[2]), linearToGamma(t[3]), t[4]
 		return linearToGamma(t[1]), linearToGamma(t[2]), linearToGamma(t[3]), t[4]

+ 46 - 30
src/modules/math/wrap_RandomGenerator.cpp

@@ -23,6 +23,11 @@
 #include <cmath>
 #include <cmath>
 #include <algorithm>
 #include <algorithm>
 
 
+// Put the Lua code directly into a raw string literal.
+static const char randomgenerator_lua[] =
+#include "wrap_RandomGenerator.lua"
+;
+
 namespace love
 namespace love
 {
 {
 namespace math
 namespace math
@@ -56,41 +61,16 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx)
 	return s;
 	return s;
 }
 }
 
 
-int luax_getrandom(lua_State *L, int startidx, double r)
-{
-	int l, u;
-	// from lua 5.1.4 source code: lmathlib.c:185 ff.
-	switch (lua_gettop(L) - (startidx - 1))
-	{
-	case 0:
-		lua_pushnumber(L, r);
-		break;
-	case 1:
-		u = (int) luaL_checknumber(L, startidx);
-		luaL_argcheck(L, 1 <= u, startidx, "interval is empty");
-		lua_pushnumber(L, floor(r * u) + 1);
-		break;
-	case 2:
-		l = (int) luaL_checknumber(L, startidx);
-		u = (int) luaL_checknumber(L, startidx + 1);
-		luaL_argcheck(L, l <= u, startidx + 1, "interval is empty");
-		lua_pushnumber(L, floor(r * (u - l + 1)) + l);
-		break;
-	default:
-		return luaL_error(L, "wrong number of arguments");
-	}
-	return 1;
-}
-
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
 {
 {
 	return luax_checktype<RandomGenerator>(L, idx, MATH_RANDOM_GENERATOR_ID);
 	return luax_checktype<RandomGenerator>(L, idx, MATH_RANDOM_GENERATOR_ID);
 }
 }
 
 
-int w_RandomGenerator_random(lua_State *L)
+int w_RandomGenerator__random(lua_State *L)
 {
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
-	return luax_getrandom(L, 2, rng->random());
+	lua_pushnumber(L, rng->random());
+	return 1;
 }
 }
 
 
 int w_RandomGenerator_randomNormal(lua_State *L)
 int w_RandomGenerator_randomNormal(lua_State *L)
@@ -135,9 +115,28 @@ int w_RandomGenerator_getState(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+// C functions in a struct, necessary for the FFI versions of RandomGenerator functions.
+struct FFI_RandomGenerator
+{
+	double (*random)(Proxy *p);
+};
+
+static FFI_RandomGenerator ffifuncs =
+{
+	[](Proxy *p) -> double // random()
+	{
+		// FIXME: We need better type-checking...
+		if (!typeFlags[p->type][MATH_RANDOM_GENERATOR_ID])
+			return 0.0;
+
+		RandomGenerator *rng = (RandomGenerator *) p->object;
+		return rng->random();
+	}
+};
+
 static const luaL_Reg w_RandomGenerator_functions[] =
 static const luaL_Reg w_RandomGenerator_functions[] =
 {
 {
-	{ "random", w_RandomGenerator_random },
+	{ "_random", w_RandomGenerator__random }, // random() is defined in wrap_RandomGenerator.lua.
 	{ "randomNormal", w_RandomGenerator_randomNormal },
 	{ "randomNormal", w_RandomGenerator_randomNormal },
 	{ "setSeed", w_RandomGenerator_setSeed },
 	{ "setSeed", w_RandomGenerator_setSeed },
 	{ "getSeed", w_RandomGenerator_getSeed },
 	{ "getSeed", w_RandomGenerator_getSeed },
@@ -148,7 +147,24 @@ static const luaL_Reg w_RandomGenerator_functions[] =
 
 
 extern "C" int luaopen_randomgenerator(lua_State *L)
 extern "C" int luaopen_randomgenerator(lua_State *L)
 {
 {
-	return luax_register_type(L, MATH_RANDOM_GENERATOR_ID, "RandomGenerator", w_RandomGenerator_functions, nullptr);
+	int n = luax_register_type(L, MATH_RANDOM_GENERATOR_ID, "RandomGenerator", w_RandomGenerator_functions, nullptr);
+
+	luax_gettypemetatable(L, MATH_RANDOM_GENERATOR_ID);
+
+	// Load and execute wrap_RandomGenerator.lua, sending the metatable and the
+	// ffi functions struct pointer as arguments.
+	if (lua_istable(L, -1))
+	{
+		luaL_loadbuffer(L, randomgenerator_lua, sizeof(randomgenerator_lua), "wrap_RandomGenerator.lua");
+		lua_pushvalue(L, -2);
+		lua_pushlightuserdata(L, &ffifuncs);
+		lua_call(L, 2, 0);
+	}
+
+	// Pop the metatable.
+	lua_pop(L, 1);
+
+	return n;
 }
 }
 
 
 } // math
 } // math

+ 0 - 1
src/modules/math/wrap_RandomGenerator.h

@@ -33,7 +33,6 @@ namespace math
 
 
 // Helper functions.
 // Helper functions.
 RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx);
 RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx);
-int luax_getrandom(lua_State *L, int startidx, double r);
 
 
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
 extern "C" int luaopen_randomgenerator(lua_State *L);
 extern "C" int luaopen_randomgenerator(lua_State *L);

+ 80 - 0
src/modules/math/wrap_RandomGenerator.lua

@@ -0,0 +1,80 @@
+R"luastring"--(
+-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
+-- There is a matching delimiter at the bottom of the file.
+
+--[[
+Copyright (c) 2006-2015 LOVE Development Team
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+--]]
+
+local RandomGenerator_mt, ffifuncspointer = ...
+local RandomGenerator = RandomGenerator_mt.__index
+
+local type, tonumber, error = type, tonumber, error
+local floor = math.floor
+
+local _random = RandomGenerator._random
+
+local function getrandom(r, l, u)
+	if u ~= nil then
+		if type(r) ~= "number" then error("bad argument #1 to 'random' (number expected)", 2) end
+		if type(l) ~= "number" then error("bad argument #2 to 'random' (number expected)", 2) end
+		return floor(r * (u - l + 1)) + l
+	elseif l ~= nil then
+		if type(l) ~= "number" then error("bad argument #1 to 'random' (number expected)", 2) end
+		return floor(r * l) + 1
+	else
+		return r
+	end
+end
+
+function RandomGenerator:random(l, u)
+	local r = _random(self)
+	return getrandom(r, l, u)
+end
+
+if type(jit) ~= "table" or not jit.status() then
+	-- LuaJIT's FFI is *much* slower than LOVE's regular methods when the JIT
+	-- compiler is disabled.
+	return
+end
+
+local status, ffi = pcall(require, "ffi")
+if not status then return end
+
+pcall(ffi.cdef, [[
+typedef struct Proxy Proxy;
+
+typedef struct FFI_RandomGenerator
+{
+	double (*random)(Proxy *p);
+} FFI_RandomGenerator;
+]])
+
+local ffifuncs = ffi.cast("FFI_RandomGenerator *", ffifuncspointer)
+
+
+-- Overwrite some regular love.math functions with FFI implementations.
+
+function RandomGenerator:random(l, u)
+	local r = tonumber(ffifuncs.random(self))
+	return getrandom(r, l, u)
+end
+
+-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
+--)luastring"--"