init.lua 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. --[===================================================================[--
  2. ffi_accel - Mod for Minetest to accelerate handling of LuaVoxelManip
  3. and PerlinNoiseMap through the foreign function interface
  4. Copyright © 2019 Pedro Gimeno Fortea. All rights reserved.
  5. Permission is hereby granted to everyone to copy, modify, distribute
  6. and use this file, for any purpose, in whole or in part, free of
  7. charge, under the sole condition that the above copyright notice,
  8. together with this permission grant and the disclaimer below, are
  9. included in all copies of this software or of a substantial portion
  10. of it.
  11. THIS SOFTWARE COMES WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED.
  12. --]===================================================================]--
  13. -- Based on an idea of EvidenceBKidsCode.
  14. -- This mod needs to apply a patch to the engine in order to work; see
  15. --
  16. local modname = "Mod ffi_accel"
  17. local err_prefix = modname .. " won't have any effect because "
  18. local ie = minetest.request_insecure_environment()
  19. if not ie then
  20. minetest.log("warning", err_prefix .. "it's not declared in secure.trusted_mods")
  21. return
  22. end
  23. -- We need raw read access to the metatables for monkey-patching
  24. local getmetatable = ie.debug.getmetatable
  25. local setmetatable = setmetatable
  26. local ok, ffi = pcall(ie.require, 'ffi')
  27. if not ok or not ffi then
  28. minetest.log("warning", err_prefix .. "FFI is not available in this system")
  29. return
  30. end
  31. ffi.cdef[[
  32. typedef uint8_t u8;
  33. typedef uint16_t u16;
  34. typedef int32_t s32;
  35. typedef struct {
  36. u16 nodeid;
  37. u8 light;
  38. u8 param2;
  39. } MapNode;
  40. int get_mapnode_version(void);
  41. void VoxelManip_get_pointer(void **lvmp, void **ptr);
  42. s32 VoxelManip_get_volume(void **lvmp);
  43. void PerlinNoiseMap_get_pointer(void **pnmp, void **ptr);
  44. size_t PerlinNoiseMap_get_area(void **pnmp);
  45. size_t PerlinNoiseMap_get_volume(void **pnmp);
  46. ]]
  47. local ffiC = ffi.C
  48. local ffinew = ffi.new
  49. local VoidPtrPtr = ffi.typeof('void*[1]')
  50. local MapNodePtr = ffi.typeof('MapNode *')
  51. local mt_set_data, mt_set_light_data, mt_set_param2_data
  52. if not pcall(function ()
  53. assert(ffiC.get_mapnode_version and ffiC.VoxelManip_get_pointer
  54. and ffiC.VoxelManip_get_volume and ffiC.PerlinNoiseMap_get_pointer)
  55. end)
  56. then
  57. minetest.log("warning", err_prefix .. "your Minetest executable has not been patched to support FFI")
  58. return
  59. end
  60. if ffiC.get_mapnode_version() ~= 1 then
  61. minetest.log("warning", err_prefix .. "of incompatible map node structure")
  62. return
  63. end
  64. -- Table of pointers (keys are weak refs)
  65. local ptrtable = setmetatable({}, {__mode = 'k'})
  66. -- Table of sizes (keys are weak refs)
  67. local sizetable = setmetatable({}, {__mode = 'k'})
  68. -- Metamethod: get a value
  69. local function mm_get(t, idx)
  70. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1] or rawget(t, idx)
  71. end
  72. -- Metamethod: set a value
  73. local function mm_set(t, idx, value)
  74. if idx >= 1 and idx <= sizetable[t] then
  75. ptrtable[t][idx - 1] = value
  76. else
  77. rawset(t, idx, value)
  78. end
  79. end
  80. -- Metamethod: get the nodeid of an index
  81. local function mm_get_nodeid(t, idx)
  82. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].nodeid or rawget(t, idx)
  83. end
  84. -- Metamethod: set the nodeid of an index to a value
  85. local function mm_set_nodeid(t, idx, value)
  86. if idx >= 1 and idx <= sizetable[t] then
  87. ptrtable[t][idx - 1].nodeid = value
  88. else
  89. rawset(t, idx, value)
  90. end
  91. end
  92. -- Metamethod: get the light of an index
  93. local function mm_get_light(t, idx)
  94. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].light or rawget(t, idx)
  95. end
  96. -- Metamethod: set the light of an index to a value
  97. local function mm_set_light(t, idx, value)
  98. if idx >= 1 and idx <= sizetable[t] then
  99. ptrtable[t][idx - 1].light = value
  100. else
  101. rawset(t, idx, value)
  102. end
  103. end
  104. -- Metamethod: get the param2 of an index
  105. local function mm_get_param2(t, idx)
  106. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].param2 or rawget(t, idx)
  107. end
  108. -- Metamethod: set the param2 of an index to a value
  109. local function mm_set_param2(t, idx, value)
  110. if idx >= 1 and idx <= sizetable[t] then
  111. ptrtable[t][idx - 1].param2 = value
  112. else
  113. rawset(t, idx, value)
  114. end
  115. end
  116. -- Metatables for VoxelManip nodeid, light and param2
  117. local VoxelManip_nodeid_mt = { __index = mm_get_nodeid, __newindex = mm_set_nodeid }
  118. local VoxelManip_light_mt = { __index = mm_get_light, __newindex = mm_set_light }
  119. local VoxelManip_param2_mt = { __index = mm_get_param2, __newindex = mm_set_param2 }
  120. -- Replacement VoxelManip.get_data and VoxelManip.set_data methods
  121. local function VoxelManip_get_data(self, buffer)
  122. -- Set up the metatable and pointer
  123. buffer = setmetatable(buffer or {}, VoxelManip_nodeid_mt)
  124. do
  125. local ptr = VoidPtrPtr()
  126. ffiC.VoxelManip_get_pointer(self, ptr)
  127. ptrtable[buffer] = MapNodePtr(ptr[0])
  128. end
  129. sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
  130. return buffer
  131. end
  132. local function VoxelManip_set_data(self, buffer)
  133. -- Call the original set_data method if the metatable is not
  134. -- the one we've set
  135. if getmetatable(buffer) ~= VoxelManip_nodeid_mt then
  136. mt_set_data(self, buffer)
  137. end
  138. end
  139. -- Replacement VoxelManip.get/set_light_data methods
  140. local function VoxelManip_get_light_data(self, buffer)
  141. -- Set up the metatable and pointer
  142. buffer = setmetatable(buffer or {}, VoxelManip_light_mt)
  143. do
  144. local ptr = VoidPtrPtr()
  145. ffiC.VoxelManip_get_pointer(self, ptr)
  146. ptrtable[buffer] = MapNodePtr(ptr[0])
  147. end
  148. sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
  149. return buffer
  150. end
  151. local function VoxelManip_set_light_data(self, buffer)
  152. -- Call the original set_light_data method if the metatable is not
  153. -- the one we've set
  154. if getmetatable(buffer) ~= VoxelManip_light_mt then
  155. mt_set_light_data(self, buffer)
  156. end
  157. end
  158. -- Replacement VoxelManip.get/set_param2_data methods
  159. local function VoxelManip_get_param2_data(self, buffer)
  160. -- Set up the metatable and pointer
  161. buffer = setmetatable(buffer or {}, VoxelManip_param2_mt)
  162. do
  163. local ptr = VoidPtrPtr()
  164. ffiC.VoxelManip_get_pointer(self, ptr)
  165. ptrtable[buffer] = MapNodePtr(ptr[0])
  166. end
  167. sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
  168. return buffer
  169. end
  170. local function VoxelManip_set_param2_data(self, buffer)
  171. -- Call the original set_param2_data method if the metatable is not
  172. -- the one we've set
  173. if getmetatable(buffer) ~= VoxelManip_param2_mt then
  174. mt_set_param2_data(self, buffer)
  175. end
  176. end
  177. -- Metatable for PerlinNoiseMap
  178. local PerlinNoiseMap_mt = { __index = mm_get }
  179. -- Replacement methods for PerlinNoiseMap.*
  180. local function PerlinNoiseMap_get_2d_map_flat(self, pos, buffer)
  181. -- Calculate the noise
  182. self:calc_2d_map(pos)
  183. -- Prepare the metatable for direct access to the data
  184. buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
  185. do
  186. local ptr = VoidPtrPtr()
  187. ffiC.PerlinNoiseMap_get_pointer(self, ptr)
  188. ptrtable[buffer] = MapNodePtr(ptr[0])
  189. end
  190. sizetable[buffer] = ffiC.PerlinNoiseMap_get_area(self)
  191. return buffer
  192. end
  193. local function PerlinNoiseMap_get_3d_map_flat(self, pos, buffer)
  194. -- Calculate the noise
  195. self:calc_3d_map(pos)
  196. -- Prepare the metatable for direct access to the data
  197. buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
  198. do
  199. local ptr = VoidPtrPtr()
  200. ffiC.PerlinNoiseMap_get_pointer(self, ptr)
  201. ptrtable[buffer] = MapNodePtr(ptr[0])
  202. end
  203. sizetable[buffer] = ffiC.PerlinNoiseMap_get_volume(self)
  204. return buffer
  205. end
  206. --[[
  207. local function PerlinNoiseMap_get_map_slice(slice_offset, slice_size, buffer)
  208. -- TODO
  209. end
  210. --]]
  211. -- Monkey-patch VoxelManip and PerlinNoiseMap
  212. minetest.after(0, function()
  213. -- VoxelManip buffer-intensive methods
  214. local mt = getmetatable(minetest.get_voxel_manip())
  215. mt_set_data = mt.set_data
  216. mt_set_light_data = mt.set_light_data
  217. mt_set_param2_data = mt.set_param2_data
  218. mt.get_data = VoxelManip_get_data
  219. mt.set_data = VoxelManip_set_data
  220. mt.get_light_data = VoxelManip_get_light_data
  221. mt.set_light_data = VoxelManip_set_light_data
  222. mt.get_param2_data = VoxelManip_get_param2_data
  223. mt.set_param2_data = VoxelManip_set_param2_data
  224. -- PerlinNoiseMap buffer-intensive methods
  225. mt = getmetatable(minetest.get_perlin_map({spread = {x=100, y=100, z=100}}, {x=1, y=1, z=1}))
  226. mt.get_2d_map_flat = PerlinNoiseMap_get_2d_map_flat
  227. mt.get_3d_map_flat = PerlinNoiseMap_get_3d_map_flat
  228. -- mt.get_map_slice = PerlinNoiseMap_get_map_slice
  229. -- Aliases
  230. mt.get2DMap_flat = mt.get_2d_map_flat
  231. mt.get3DMap_flat = mt.get_3d_map_flat
  232. -- mt.getMapSlice = mt.get_map_slice
  233. end)
  234. minetest.log("action", modname .. " activated!")