Lunatic: working tsprite methods, static members, struct array timing test.

git-svn-id: https://svn.eduke32.com/eduke32@3468 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-02-10 16:23:54 +00:00
parent 1dd52a4d6e
commit 90793f6a67
3 changed files with 190 additions and 34 deletions

View File

@ -11,6 +11,7 @@ ffi.cdef "enum { _DEBUG_LUNATIC=1 }"
local bit = require("bit") local bit = require("bit")
local string = require("string") local string = require("string")
local table = require("table")
local assert = assert local assert = assert
local error = error local error = error
@ -103,8 +104,9 @@ end
ffi.cdef([[ ffi.cdef([[
typedef $ sectortype; typedef $ sectortype;
typedef $ walltype; typedef $ walltype;
// NOTE: spritetype and tspritetype are different types with the same data members.
typedef $ spritetype; typedef $ spritetype;
typedef $ tspritetype; typedef struct { spritetype; } tspritetype;
typedef struct { typedef struct {
const uint32_t mdanimtims; const uint32_t mdanimtims;
@ -132,9 +134,7 @@ typedef struct {
} hitdata_t; } hitdata_t;
]], ]],
ffi.typeof(SECTOR_STRUCT), ffi.typeof(WALL_STRUCT), ffi.typeof(SECTOR_STRUCT), ffi.typeof(WALL_STRUCT),
ffi.typeof(SPRITE_STRUCT), ffi.typeof(SPRITE_STRUCT)) ffi.typeof(SPRITE_STRUCT))
-- NOTE: spritetype and tspritetype are different types with the same layout.
-- (XXX: is there a better way?)
-- Define the "palette_t" type, which for us has .{r,g,b} fields and a -- Define the "palette_t" type, which for us has .{r,g,b} fields and a
-- bound-checking array of length 3 overlaid. -- bound-checking array of length 3 overlaid.
@ -337,6 +337,12 @@ local sectortype_mt = {
} }
ffi.metatype("sectortype", sectortype_mt) ffi.metatype("sectortype", sectortype_mt)
local band = bit.band
local bor = bit.bor
local bnot = bit.bnot
local lshift = bit.lshift
local rshift = bit.rshift
local walltype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(WALL_STRUCT))) local walltype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(WALL_STRUCT)))
local walltype_mt = { local walltype_mt = {
@ -366,19 +372,19 @@ local walltype_mt = {
--- Predicates --- Predicates
isblocking = function(w) isblocking = function(w)
return (bit.band(w.cstat, 1)~=0) return (band(w.cstat, 1)~=0)
end, end,
ismasked = function(w) ismasked = function(w)
return (bit.band(w.cstat, 16)~=0) return (band(w.cstat, 16)~=0)
end, end,
isoneway = function(w) isoneway = function(w)
return (bit.band(w.cstat, 32)~=0) return (band(w.cstat, 32)~=0)
end, end,
ishittable = function(w) ishittable = function(w)
return (bit.band(w.cstat, 64)~=0) return (band(w.cstat, 64)~=0)
end, end,
} }
} }
@ -388,13 +394,15 @@ local spriteext_mt = {
__index = { __index = {
-- Enable EVENT_ANIMATESPRITES for this sprite. -- Enable EVENT_ANIMATESPRITES for this sprite.
make_animated = function(sx) make_animated = function(sx)
sx.flags = bit.bor(sx.flags, 16) sx.flags = bor(sx.flags, 16)
end, end,
}, },
} }
ffi.metatype("spriteext_t", spriteext_mt) ffi.metatype("spriteext_t", spriteext_mt)
local spritetype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(SPRITE_STRUCT))) local spritetype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(SPRITE_STRUCT)))
-- NOTE: this is the *protected* tspritetype pointer.
local tspritetype_ptr_ct = ffi.typeof("$ *", ffi.typeof("tspritetype"))
local spritetype_mt = { local spritetype_mt = {
__pow = function(s, zofs) __pow = function(s, zofs)
@ -412,6 +420,15 @@ local spritetype_mt = {
-- XXX: for now, no checking -- XXX: for now, no checking
ffi.cast(spritetype_ptr_ct, s).yvel = yvel ffi.cast(spritetype_ptr_ct, s).yvel = yvel
end, end,
--- Custom setters
set_cstat_bits = function(s, bits)
s.cstat = bor(s.cstat, bits)
end,
clear_cstat_bits = function(s, bits)
s.cstat = band(s.cstat, bnot(bits))
end,
}, },
} }
@ -430,20 +447,29 @@ end
local tspritetype_mt = deep_copy(spritetype_mt) local tspritetype_mt = deep_copy(spritetype_mt)
print(spritetype_mt)
print(spritetype_mt.__index)
print(tspritetype_mt)
print(tspritetype_mt.__index)
-- Methods that are specific to tsprites -- Methods that are specific to tsprites
-- XXX: doesn't work with LuaJIT git f772bed34b39448e3a9ab8d07f6d5c0c26300e1b
function tspritetype_mt.__index.dup(tspr) function tspritetype_mt.__index.dup(tspr)
if (ffiC.spritesortcnt >= ffiC.MAXSPRITESONSCREEN+0ULL) then if (ffiC.spritesortcnt >= ffiC.MAXSPRITESONSCREEN+0ULL) then
return nil return nil
end end
local newtspr = ffi.tsprite[ffiC.spritesortcnt] local newtspr = ffiC.tsprite[ffiC.spritesortcnt]
ffi.copy(newtspr, tspr, ffi.sizeof(tspr)) ffi.copy(newtspr, tspr, ffi.sizeof(tspr))
ffiC.spritesortcnt = ffiC.spritesortcnt+1 ffiC.spritesortcnt = ffiC.spritesortcnt+1
return newtspr return newtspr
end end
function tspritetype_mt.__index.getspr(tspr)
return sprite[tspr.owner]
end
-- The user of this module can insert additional "spritetype" index -- The user of this module can insert additional "spritetype" index
-- methods and register them with "ffi.metatype". -- methods and register them with "ffi.metatype".
function finish_spritetype(mt_index) function finish_spritetype(mt_index)
@ -465,29 +491,76 @@ function setmtonce(tab, mt)
end end
---- indirect C array access ---- ---- indirect C array access ----
local sector_mt = {
__index = function(tab, key)
if (key >= 0 and key < ffiC.numsectors) then return ffiC.sector[key] end
error('out-of-bounds sector[] read access', 2)
end,
__newindex = function() error('cannot write directly to sector[]', 2) end, -- Construct const struct from table
local function conststruct(tab)
local strtab = { "const struct { int32_t " }
local vals = {}
for member, val in pairs(tab) do
strtab[#strtab+1] = member..","
vals[#vals+1] = val
end
strtab[#strtab] = strtab[#strtab]:gsub(',',';')
strtab[#strtab+1] = "}"
return ffi.new(table.concat(strtab), vals)
end
-- Static, non-instance members. Used to hold constants, for example
-- sprite.CSTAT.TRANSLUCENT1
local static_members = { sector={}, wall={}, sprite={} }
static_members.sector.STAT = conststruct
{
MASKED = 128,
-- NOTE the reversed order
TRANSLUCENT2 = 128,
TRANSLUCENT1 = 256,
TRANSLUCENT_BOTH_BITS = 256+128,
} }
local wall_mt = { static_members.wall.CSTAT = conststruct
__index = function(tab, key) {
if (key >= 0 and key < ffiC.numwalls) then return ffiC.wall[key] end MASKED = 64,
error('out-of-bounds wall[] read access', 2) TRANSLUCENT1 = 128,
end, TRANSLUCENT2 = 512,
TRANSLUCENT_BOTH_BITS = 512+128,
__newindex = function() error('cannot write directly to wall[]', 2) end,
} }
static_members.sprite.CSTAT = conststruct
{
TRANSLUCENT1 = 2,
TRANSLUCENT2 = 512,
TRANSLUCENT_BOTH_BITS = 512+2,
}
local function GenStructMetatable(Structname, Boundname)
return {
__index = function(tab, key)
if (type(key)=="number") then
if (key >= 0 and key < ffiC[Boundname]) then
return ffiC[Structname][key]
end
error("out-of-bounds "..Structname.."[] read access", 2)
elseif (type(key)=="string") then
return static_members[Structname][key]
end
end,
__newindex = function() error("cannot write directly to "..Structname.."[]", 2) end,
}
end
local sector_mt = GenStructMetatable("sector", "numsectors")
local wall_mt = GenStructMetatable("wall", "numwalls")
local sprite_mt = GenStructMetatable("sprite", "MAXSPRITES")
local atsprite_mt = { local atsprite_mt = {
__index = function(tab, idx) __index = function(tab, idx)
check_sprite_idx(idx) check_sprite_idx(idx)
local tspr = ffi.cast(spritetype_ptr_ct, ffiC.spriteext[idx]._tspr) local tspr = ffi.cast(tspritetype_ptr_ct, ffiC.spriteext[idx]._tspr)
if (tspr == nil) then if (tspr == nil) then
error("tsprite of actor "..idx.." unavailable", 2) error("tsprite of actor "..idx.." unavailable", 2)
end end
@ -530,7 +603,7 @@ end
sector = setmtonce({}, sector_mt) sector = setmtonce({}, sector_mt)
wall = setmtonce({}, wall_mt) wall = setmtonce({}, wall_mt)
sprite = creategtab(ffiC.sprite, ffiC.MAXSPRITES, 'sprite[]') sprite = setmtonce({}, sprite_mt)
spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[]') spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[]')
atsprite = setmtonce({}, atsprite_mt) atsprite = setmtonce({}, atsprite_mt)

View File

@ -111,7 +111,7 @@ checkfail('gv.sprite[0].yrepeat = 100', "access forbidden")
-- indexing struct array with non-numeric type -- indexing struct array with non-numeric type
checkfail('local i = sprite["qwe"]') -- this will produce an error inside the sprite metatable checkfail('local i = sprite["qwe"]') -- this will produce an error inside the sprite metatable
checkfail('print(sprite[100000].ceilingpal)', "out-of-bounds sprite[] struct read access") checkfail('print(sprite[100000].ceilingpal)', "out-of-bounds sprite[] read access")
checkfail('print(gv.sprite[0])', "access forbidden") checkfail('print(gv.sprite[0])', "access forbidden")
@ -186,9 +186,9 @@ checkfail("do local bt=require'bittest'; bt.QWE=1; end", "modifying module table
-- the cdata returned by player[] can't be made into a pointer! -- the cdata returned by player[] can't be made into a pointer!
checkfail("do local pl=player[0]; i=pl[1]; end") checkfail("do local pl=player[0]; i=pl[1]; end")
checkfail("do local ud=gv.ud.camerasprite; end", "access forbidden") -- test for proper decl() checkfail("do local ud=gv.ud.camerasprite; end", "access forbidden") -- test for proper decl()
checkfail("sprite[0]:set_picnum(-10)", "attempt to set invalid picnum") checkfail("sprite[0]:set_picnum(-10)", "invalid tile number")
checkfail("gv.g_sizes_of=nil; print(gv.g_sizes_of[0])", "write access forbidden") checkfail("gv.g_sizes_of=nil; print(gv.g_sizes_of[0])", "write access forbidden")
checkfail("gv.cam.sect=-1", "passed out-of-bounds sector number") checkfail("gv.cam.sect=-1", "invalid sector number")
printf('ceilingbunch of sector 0: %d', getbunch(0, gv.CEILING)) printf('ceilingbunch of sector 0: %d', getbunch(0, gv.CEILING))
@ -266,6 +266,29 @@ gameevent(gv.EVENT_ENTERLEVEL,
player[0].weapon.SHOTGUN.shoots = 1653 player[0].weapon.SHOTGUN.shoots = 1653
projectile[1653].drop = 0 projectile[1653].drop = 0
t = gv.gethitickms()
local N=1
for n=1,N do
for i=0,gv.MAXSPRITES-1 do
sprite[i].filler = 1
end
for i=gv.MAXSPRITES-1,0,-1 do
sprite[i].shade = 1
end
for i=0,gv.MAXSPRITES-1 do
sprite[i].xoffset = 0
end
for i=gv.MAXSPRITES-1,0,-1 do
sprite[i].yoffset = 1
end
end
t = gv.gethitickms()-t
printf("%d x four 0..MAXSPRITES-1 iterations took %.03f us per outer iteration", N, (1000*t)/N)
-- Results on x86:
-- N=1: 480-1000 us (too large variance)
-- N=10: 190-210 us * 10 = 1.9-2.1 ms
-- N=100: about 160 us * 100 = about 16 ms
checkfail("gameevent('GAME', function() print('qwe') end)", checkfail("gameevent('GAME', function() print('qwe') end)",
"must be called from top level") "must be called from top level")
end end
@ -327,15 +350,18 @@ gameevent("DISPLAYROOMS",
end end
) )
local CS = sprite.CSTAT
gameevent("ANIMATESPRITES", gameevent("ANIMATESPRITES",
function(aci) function(aci)
local tspr = atsprite[aci] local tspr = atsprite[aci]
if (tspr.picnum==1680) then if (tspr:getspr().picnum==1680) then
--[[
local tspr2 = tspr:dup() local tspr2 = tspr:dup()
tspr2.x = tspr2.x + 512*math.cos(gv.totalclock/120) if (tspr2) then
tspr2.y = tspr2.y + 512*math.sin(gv.totalclock/120) tspr2.x = tspr2.x + 512*math.cos(gv.totalclock/60)
--]] tspr2.y = tspr2.y + 512*math.sin(gv.totalclock/60)
tspr2:set_cstat_bits(CS.TRANSLUCENT_BOTH_BITS)
end
end end
end) end)

View File

@ -0,0 +1,57 @@
// Timing test for four 0..MAXSPRITES-1 access loops,
// fwd->back->fwd->back.
// Results with a LTO=1 RELEASE=1 C-CON build on x86: 42 ms for N=10.
// (Compare with Lunatic results in test.elua)
define N 10
gamevar n 0 0
gamevar i 0 0
gamevar t 0 0
gamevar t2 0 0
define MAXSPRITES 16384
define MAXSPRM1 16383 // MAXSPRITES-1
onevent EVENT_ENTERLEVEL
getticks t
setvar n 0
whilevarn n N
{
setvar i 0
whilevarvarn i MAXSPRITES
{
setactor[i].detail 1
addvar i 1
}
setvar i MAXSPRM1
whilevarvarn i -1
{
setactor[i].shade 1
subvar i 1
}
setvar i 0
whilevarvarn i MAXSPRITES
{
setactor[i].xoffset 0
addvar i 1
}
setvar i MAXSPRM1
whilevarvarn i -1
{
setactor[i].yoffset 0
subvar i 1
}
addvar n 1
}
getticks t2
subvarvar t2 t
redefinequote 114 %d x four 0..MAXSPITES-1 iterations took %d ms in total
qsprintf 115 114 n t2
echo 115
endevent