Browse Source

Implemented efficient FFI versions of SoundData methods, which are used when the LuaJIT FFI and JIT compiler are present.

Alex Szpakowski 10 years ago
parent
commit
532c61c6c2

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

@@ -1513,6 +1513,7 @@
 		FAB17BF41ABFC4B100F9BA27 /* lz4hc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lz4hc.h; sourceTree = "<group>"; };
 		FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrueTypeRasterizer.cpp; sourceTree = "<group>"; };
 		FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrueTypeRasterizer.h; sourceTree = "<group>"; };
+		FAC734C11B2E021A00AB460A /* wrap_SoundData.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_SoundData.lua; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -2539,6 +2540,7 @@
 				FA0B7C971A95902C000E1D17 /* wrap_Sound.h */,
 				FA0B7C981A95902C000E1D17 /* wrap_SoundData.cpp */,
 				FA0B7C991A95902C000E1D17 /* wrap_SoundData.h */,
+				FAC734C11B2E021A00AB460A /* wrap_SoundData.lua */,
 			);
 			path = sound;
 			sourceTree = "<group>";

+ 18 - 15
src/common/runtime.cpp

@@ -262,7 +262,7 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name)
 	return 0;
 }
 
-int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f)
+int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pushmetatable)
 {
 	// Verify that this type name has a matching Type ID and type name mapping.
 	const char *tname = "Invalid";
@@ -270,9 +270,9 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f)
 		printf("Missing type name entry for type ID %d\n", type);
 
 	// Get the place for storing and re-using instantiated love types.
-	luax_getregistry(L, REGISTRY_TYPES);
+	luax_getregistry(L, REGISTRY_OBJECTS);
 
-	// Create registry._lovetypes if it doesn't exist yet.
+	// Create registry._loveobjects if it doesn't exist yet.
 	if (!lua_istable(L, -1))
 	{
 		lua_newtable(L);
@@ -288,8 +288,8 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f)
 		// setmetatable(newtable, metatable)
 		lua_setmetatable(L, -2);
 
-		// registry._lovetypes = newtable
-		lua_setfield(L, LUA_REGISTRYINDEX, "_lovetypes");
+		// registry._loveobjects = newtable
+		lua_setfield(L, LUA_REGISTRYINDEX, "_loveobjects");
 	}
 	else
 		lua_pop(L, 1);
@@ -322,9 +322,12 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f)
 	lua_pushcfunction(L, w__typeOf);
 	lua_setfield(L, -2, "typeOf");
 
-	if (f != 0)
+	if (f != nullptr)
 		luax_setfuncs(L, f);
 
+	if (pushmetatable)
+		return 1; // leave the metatable on the stack.
+
 	lua_pop(L, 1); // Pops metatable.
 	return 0;
 }
@@ -406,8 +409,8 @@ void luax_pushtype(lua_State *L, love::Type type, love::Object *object)
 		return;
 	}
 
-	// Fetch the registry table of instantiated types.
-	luax_getregistry(L, REGISTRY_TYPES);
+	// Fetch the registry table of instantiated objects.
+	luax_getregistry(L, REGISTRY_OBJECTS);
 
 	// The table might not exist - it should be insisted in luax_register_type.
 	if (!lua_istable(L, -1))
@@ -416,7 +419,7 @@ void luax_pushtype(lua_State *L, love::Type type, love::Object *object)
 		return luax_rawnewtype(L, type, object);
 	}
 
-	// Get the value of lovetypes[object] on the stack.
+	// Get the value of loveobjects[object] on the stack.
 	lua_pushlightuserdata(L, object);
 	lua_gettable(L, -2);
 
@@ -430,11 +433,11 @@ void luax_pushtype(lua_State *L, love::Type type, love::Object *object)
 		lua_pushlightuserdata(L, object);
 		lua_pushvalue(L, -2);
 
-		// lovetypes[object] = Proxy.
+		// loveobjects[object] = Proxy.
 		lua_settable(L, -4);
 	}
 
-	// Remove the lovetypes table from the stack.
+	// Remove the loveobjects table from the stack.
 	lua_remove(L, -2);
 
 	// Keep the Proxy userdata on the stack.
@@ -586,8 +589,8 @@ int luax_insistregistry(lua_State *L, Registry r)
 		return luax_insistlove(L, "_gc");
 	case REGISTRY_MODULES:
 		return luax_insistlove(L, "_modules");
-	case REGISTRY_TYPES:
-		return luax_insist(L, LUA_REGISTRYINDEX, "_lovetypes");
+	case REGISTRY_OBJECTS:
+		return luax_insist(L, LUA_REGISTRYINDEX, "_loveobjects");
 	default:
 		return luaL_error(L, "Attempted to use invalid registry.");
 	}
@@ -601,8 +604,8 @@ int luax_getregistry(lua_State *L, Registry r)
 		return luax_getlove(L, "_gc");
 	case REGISTRY_MODULES:
 		return luax_getlove(L, "_modules");
-	case REGISTRY_TYPES:
-		lua_getfield(L, LUA_REGISTRYINDEX, "_lovetypes");
+	case REGISTRY_OBJECTS:
+		lua_getfield(L, LUA_REGISTRYINDEX, "_loveobjects");
 		return 1;
 	default:
 		return luaL_error(L, "Attempted to use invalid registry.");

+ 3 - 2
src/common/runtime.h

@@ -51,7 +51,7 @@ enum Registry
 {
 	REGISTRY_GC,
 	REGISTRY_MODULES,
-	REGISTRY_TYPES
+	REGISTRY_OBJECTS
 };
 
 /**
@@ -249,8 +249,9 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name);
  * Register a new type.
  * @param type The type.
  * @param f The list of member functions for the type.
+ * @param pushmetatable Whether to push the type's metatable to the stack.
  **/
-int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f = 0);
+int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f = nullptr, bool pushmetatable = false);
 
 /**
  * Do a table.insert from C

+ 26 - 1
src/modules/sound/wrap_SoundData.cpp

@@ -22,11 +22,21 @@
 
 #include "common/wrap_Data.h"
 
+// Shove the wrap_SoundData.lua code directly into a raw string literal.
+static const char sounddata_lua[] =
+#include "wrap_SoundData.lua"
+;
+
 namespace love
 {
 namespace sound
 {
 
+/**
+ * NOTE: Additional wrapper code is in wrap_SoundData.lua. Be sure to keep it
+ * in sync with any changes made to this file!
+ **/
+
 SoundData *luax_checksounddata(lua_State *L, int idx)
 {
 	return luax_checktype<SoundData>(L, idx, SOUND_SOUND_DATA_ID);
@@ -105,7 +115,22 @@ static const luaL_Reg functions[] =
 
 extern "C" int luaopen_sounddata(lua_State *L)
 {
-	return luax_register_type(L, SOUND_SOUND_DATA_ID, functions);
+	// The last argument pushes the type's metatable onto the stack.
+	int ret = luax_register_type(L, SOUND_SOUND_DATA_ID, functions, true);
+
+	// Load and execute SoundData.lua, sending the metatable as an argument.
+	if (ret > 0)
+	{
+		luaL_loadbuffer(L, sounddata_lua, sizeof(sounddata_lua), "SoundData.lua");
+		lua_pushvalue(L, -2);
+		lua_call(L, 1, 0);
+
+		// Pop the metatable.
+		lua_pop(L, 1);
+		ret--;
+	}
+
+	return ret;
 }
 
 } // sound

+ 116 - 0
src/modules/sound/wrap_SoundData.lua

@@ -0,0 +1,116 @@
+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 SoundData_mt = ...
+
+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
+
+local tonumber, assert = tonumber, assert
+
+local float = ffi.typeof("float")
+local datatypes = {ffi.typeof("uint8_t *"), ffi.typeof("int16_t *")}
+
+local typemaxvals = {0x7F, 0x7FFF}
+
+local _getBitDepth = SoundData_mt.__index.getBitDepth
+local _getSampleCount = SoundData_mt.__index.getSampleCount
+local _getSampleRate = SoundData_mt.__index.getSampleRate
+local _getChannels = SoundData_mt.__index.getChannels
+
+-- Table which holds SoundData objects as keys, and information about the objects
+-- as values. Uses weak keys so the SoundData objects can still be GC'd properly.
+local objectcache = setmetatable({}, {
+	__mode = "k",
+	__index = function(self, sounddata)
+		local bytedepth = _getBitDepth(sounddata) / 8
+		local pointer = ffi.cast(datatypes[bytedepth], sounddata:getPointer())
+
+		local p = {
+			bytedepth = bytedepth,
+			pointer = pointer,
+			size = sounddata:getSize(),
+			maxvalue = typemaxvals[bytedepth],
+			samplecount = _getSampleCount(sounddata),
+			samplerate = _getSampleRate(sounddata),
+			channels = _getChannels(sounddata),
+		}
+
+		self[sounddata] = p
+		return p
+	end,
+})
+
+
+-- Overwrite existing functions with new FFI versions.
+
+function SoundData_mt.__index:getSample(i)
+	local p = objectcache[self]
+	assert(i >= 0 and i < p.size/p.bytedepth, "Attempt to get out-of-range sample!")
+	if p.bytedepth == 2 then
+		-- 16-bit data is stored as signed values internally.
+		return tonumber(p.pointer[i]) / p.maxvalue
+	else
+		-- 8-bit data is stored as unsigned values internally.
+		return (tonumber(p.pointer[i]) - 128) / 127
+	end
+end
+
+function SoundData_mt.__index:setSample(i, sample)
+	local p = objectcache[self]
+	assert(i >= 0 and i < p.size/p.bytedepth, "Attempt to set out-of-range sample!")
+	if p.bytedepth == 2 then
+		-- 16-bit data is stored as signed values internally.
+		p.pointer[i] = sample * p.maxvalue
+	else
+		-- 8-bit data is stored as unsigned values internally.
+		-- The float cast is needed to make values end up the same as in the
+		-- C++ version of this method.
+		p.pointer[i] = ffi.cast(float, (sample * 127) + 128)
+	end
+end
+
+function SoundData_mt.__index:getSampleCount()
+	local p = objectcache[self]
+	return p.samplecount
+end
+
+function SoundData_mt.__index:getSampleRate()
+	local p = objectcache[self]
+	return p.samplerate
+end
+
+function SoundData_mt.__index:getChannels()
+	local p = objectcache[self]
+	return p.channels
+end
+
+-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
+--)luastring"--"