builtin/voxelworld: Remove slow Lua implementation of SpatialUpdateQueue and use higher max_operations

This commit is contained in:
Perttu Ahola 2014-10-14 23:57:23 +03:00
parent abad1e0e2f
commit 1728d15b43

View File

@ -27,219 +27,6 @@ M.chunk_size_voxels = nil
M.section_size_chunks = nil
M.section_size_voxels = nil
--[[
table.binsearch( table, value [, compval [, reversed] ] )
Searches the table through BinarySearch for the given value.
If the value is found:
it returns a table holding all the mathing indices (e.g. { startindice,endindice } )
endindice may be the same as startindice if only one matching indice was found
If compval is given:
then it must be a function that takes one value and returns a second value2,
to be compared with the input value, e.g.:
compvalue = function( value ) return value[1] end
If reversed is set to true:
then the search assumes that the table is sorted in reverse order (largest value at position 1)
note when reversed is given compval must be given as well, it can be nil/_ in this case
Return value:
on success: a table holding matching indices (e.g. { startindice,endindice } )
on failure: nil
]]--
do
-- Avoid heap allocs for performance
local default_fcompval = function(value) return value end
local fcompf = function(a,b) return a < b end
local fcompr = function(a,b) return a > b end
function table_binsearch(t,value,fcompval,reversed)
-- Initialise functions
local fcompval = fcompval or default_fcompval
local fcomp = reversed and fcompr or fcompf
-- Initialise numbers
local iStart,iEnd,iMid = 1,#t,0
-- Binary Search
while iStart <= iEnd do
-- calculate middle
iMid = math.floor((iStart+iEnd)/2)
-- get compare value
local value2 = fcompval(t[iMid])
-- get all values that match
if value == value2 then
local tfound,num = {iMid,iMid},iMid - 1
while value == fcompval(t[num]) do
tfound[1],num = num,num - 1
end
num = iMid + 1
while value == fcompval(t[num]) do
tfound[2],num = num,num + 1
end
return tfound
-- keep searching
elseif fcomp(value, value2) then
iEnd = iMid - 1
else
iStart = iMid + 1
end
end
end
end
--[[
table.bininsert( table, value [, comp] )
Inserts a given value through BinaryInsert into the table sorted by [, comp].
If 'comp' is given, then it must be a function that receives
two table elements, and returns true when the first is less
than the second, e.g. comp = function(a, b) return a > b end,
will give a sorted table, with the biggest value on position 1.
[, comp] behaves as in table.sort(table, value [, comp])
returns the index where 'value' was inserted
]]--
do
-- Avoid heap allocs for performance
local fcomp_default = function(a,b) return a < b end
function table_bininsert(t, value, fcomp)
-- Initialise compare function
local fcomp = fcomp or fcomp_default
-- Initialise numbers
local iStart,iEnd,iMid,iState = 1,#t,1,0
-- Get insert position
while iStart <= iEnd do
-- calculate middle
iMid = math.floor((iStart+iEnd)/2)
-- compare
if fcomp(value, t[iMid]) then
iEnd,iState = iMid - 1,0
else
iStart,iState = iMid + 1,1
end
end
table.insert(t,(iMid+iState),value)
return (iMid+iState)
end
end
local function SpatialUpdateQueue()
local function fcomp(a, b)
-- Always maintain all f<=1.0 items at the end of the table
if a.f > 1.0 and b.f <= 1.0 then
return true
end
if a.f <= 1.0 and b.f > 1.0 then
return false
end
return a.fw > b.fw
end
local self = {
p = buildat.Vector3(0, 0, 0),
queue_oldest_p = buildat.Vector3(0, 0, 0),
queue = {},
old_queue = nil,
-- This has to be called once per frame or so
update = function(self, max_operations)
max_operations = max_operations or 100
if self.old_queue then
log:debug("SpatialUpdateQueue(): Items in old queue: "..
#self.old_queue)
-- Move stuff from old queue to new queue
for i = 1, max_operations do
local item = table.remove(self.old_queue)
if not item then
self.old_queue = nil
break
end
self:put_item(item)
end
end
end,
set_p = function(self, p)
p = buildat.Vector3(p) -- Strip out the heavy Urho3D wrapper
self.p = p
if self.old_queue == nil and
(p - self.queue_oldest_p):length() > 20 then
-- Move queue to old_queue and reset queue
self.old_queue = self.queue
self.queue = {}
self.queue_oldest_p = self.p
end
end,
put_item = function(self, item)
local d = (item.p - self.p):length()
item.f = nil
item.fw = nil
if item.near_trigger_d then
local f_near = d / item.near_trigger_d
local fw_near = f_near / item.near_weight
if item.fw == nil or (fw_near < item.fw and
(item.f == nil or f_near < item.f)) then
item.f = f_near
item.fw = fw_near
end
end
if item.far_trigger_d then
local f_far = item.far_trigger_d / d
local fw_far = f_far / item.far_weight
if item.fw == nil or (fw_far < item.fw and
(item.f == nil or f_far < item.f)) then
item.f = f_far
item.fw = fw_far
end
end
assert(item.f)
assert(item.fw)
log:verbose("put_item"..
": d="..dump(d)..
", near_weight="..dump(item.near_weight)..
", near_trigger_d="..dump(item.near_trigger_d)..
", far_weight="..dump(item.far_weight)..
", far_trigger_d="..dump(item.far_trigger_d)..
" -> f="..item.f..", fw="..item.fw)
table_bininsert(self.queue, item, fcomp)
end,
-- Put something to be prioritized bidirectionally
put = function(self, p, near_weight, near_trigger_d,
far_weight, far_trigger_d, value)
if near_trigger_d and far_trigger_d then
assert(near_trigger_d < far_trigger_d)
assert(near_weight)
assert(far_weight)
else
assert((near_trigger_d and near_weight) or
(far_trigger_d and far_weight))
end
p = buildat.Vector3(p) -- Strip out the heavy Urho3D wrapper
self:put_item({p=p, value=value,
near_weight=near_weight, near_trigger_d=near_trigger_d,
far_weight=far_weight, far_trigger_d=far_trigger_d})
end,
get = function(self)
local item = table.remove(self.queue)
if not item then return nil end
return item.value
end,
-- item.f is the trigger_d value normalized so that f<1.0 means the item
-- has passed its trigger_d.
peek_next_f = function(self)
local item = self.queue[#self.queue]
if not item then return nil end
return item.f
end,
-- item.fw is a comparison value; both near_priority and far_priority
-- items are normalized so that values that have fw=1 have equal
-- priority.
peek_next_fw = function(self)
local item = self.queue[#self.queue]
if not item then return nil end
return item.fw
end,
get_length = function(self)
return #self.queue
end,
}
return self
end
function M.init()
log:info("voxelworld.init()")
@ -367,7 +154,7 @@ function M.init()
node_update_queue:set_p(camera_p)
-- Scale queue update operations according to the handling time of the
-- rest of the processing
node_update_queue:update(max_handling_time_us / 200 + 1)
node_update_queue:update(max_handling_time_us / 50 + 1)
for i = 1, 10 do -- Usually there is time only for a few
local f = node_update_queue:peek_next_f()