Expand vectors to 4 components using compile time switch (#214)

Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
This commit is contained in:
petrihakkinen 2021-11-22 17:42:33 +02:00 committed by GitHub
parent a5bb3ee2af
commit 2740f69f32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 145 additions and 42 deletions

View File

@ -159,7 +159,11 @@ LUA_API void lua_pushnil(lua_State* L);
LUA_API void lua_pushnumber(lua_State* L, double n);
LUA_API void lua_pushinteger(lua_State* L, int n);
LUA_API void lua_pushunsigned(lua_State* L, unsigned n);
#if LUA_VECTOR_SIZE == 4
LUA_API void lua_pushvector(lua_State* L, float x, float y, float z, float w);
#else
LUA_API void lua_pushvector(lua_State* L, float x, float y, float z);
#endif
LUA_API void lua_pushlstring(lua_State* L, const char* s, size_t l);
LUA_API void lua_pushstring(lua_State* L, const char* s);
LUA_API const char* lua_pushvfstring(lua_State* L, const char* fmt, va_list argp);

View File

@ -122,3 +122,7 @@
void* s; \
long l; \
}
#define LUA_VECTOR_SIZE 3 /* must be 3 or 4 */
#define LUA_EXTRA_SIZE LUA_VECTOR_SIZE - 2

View File

@ -550,12 +550,21 @@ void lua_pushunsigned(lua_State* L, unsigned u)
return;
}
void lua_pushvector(lua_State* L, float x, float y, float z)
#if LUA_VECTOR_SIZE == 4
void lua_pushvector(lua_State* L, float x, float y, float z, float w)
{
setvvalue(L->top, x, y, z);
setvvalue(L->top, x, y, z, w);
api_incr_top(L);
return;
}
#else
void lua_pushvector(lua_State* L, float x, float y, float z)
{
setvvalue(L->top, x, y, z, 0.0f);
api_incr_top(L);
return;
}
#endif
void lua_pushlstring(lua_State* L, const char* s, size_t len)
{

View File

@ -478,7 +478,11 @@ LUALIB_API const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
case LUA_TVECTOR:
{
const float* v = lua_tovector(L, idx);
#if LUA_VECTOR_SIZE == 4
lua_pushfstring(L, LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT, v[0], v[1], v[2], v[3]);
#else
lua_pushfstring(L, LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT, v[0], v[1], v[2]);
#endif
break;
}
default:

View File

@ -1018,13 +1018,23 @@ static int luauF_tunpack(lua_State* L, StkId res, TValue* arg0, int nresults, St
static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
#if LUA_VECTOR_SIZE == 4
if (nparams >= 4 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1) && ttisnumber(args + 2))
#else
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
#endif
{
double x = nvalue(arg0);
double y = nvalue(args);
double z = nvalue(args + 1);
setvvalue(res, float(x), float(y), float(z));
#if LUA_VECTOR_SIZE == 4
double w = nvalue(args + 2);
setvvalue(res, float(x), float(y), float(z), float(w));
#else
setvvalue(res, float(x), float(y), float(z), 0.0f);
#endif
return 1;
}

View File

@ -33,11 +33,17 @@
#define ABISWITCH(x64, ms32, gcc32) (sizeof(void*) == 8 ? x64 : ms32)
#endif
#if LUA_VECTOR_SIZE == 4
static_assert(sizeof(TValue) == ABISWITCH(24, 24, 24), "size mismatch for value");
static_assert(sizeof(LuaNode) == ABISWITCH(48, 48, 48), "size mismatch for table entry");
#else
static_assert(sizeof(TValue) == ABISWITCH(16, 16, 16), "size mismatch for value");
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
#endif
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
static_assert(offsetof(Udata, data) == ABISWITCH(24, 16, 16), "size mismatch for userdata header");
static_assert(sizeof(Table) == ABISWITCH(56, 36, 36), "size mismatch for table header");
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
const size_t kSizeClasses = LUA_SIZECLASSES;
const size_t kMaxSmallSize = 512;

View File

@ -18,12 +18,20 @@
inline bool luai_veceq(const float* a, const float* b)
{
#if LUA_VECTOR_SIZE == 4
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
#else
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
#endif
}
inline bool luai_vecisnan(const float* a)
{
#if LUA_VECTOR_SIZE == 4
return a[0] != a[0] || a[1] != a[1] || a[2] != a[2] || a[3] != a[3];
#else
return a[0] != a[0] || a[1] != a[1] || a[2] != a[2];
#endif
}
LUAU_FASTMATH_BEGIN

View File

@ -15,7 +15,7 @@
const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};
const TValue luaO_nilobject_ = {{NULL}, {0}, LUA_TNIL};
int luaO_log2(unsigned int x)
{

View File

@ -47,7 +47,7 @@ typedef union
typedef struct lua_TValue
{
Value value;
int extra;
int extra[LUA_EXTRA_SIZE];
int tt;
} TValue;
@ -105,7 +105,19 @@ typedef struct lua_TValue
i_o->tt = LUA_TNUMBER; \
}
#define setvvalue(obj, x, y, z) \
#if LUA_VECTOR_SIZE == 4
#define setvvalue(obj, x, y, z, w) \
{ \
TValue* i_o = (obj); \
float* i_v = i_o->value.v; \
i_v[0] = (x); \
i_v[1] = (y); \
i_v[2] = (z); \
i_v[3] = (w); \
i_o->tt = LUA_TVECTOR; \
}
#else
#define setvvalue(obj, x, y, z, w) \
{ \
TValue* i_o = (obj); \
float* i_v = i_o->value.v; \
@ -114,6 +126,7 @@ typedef struct lua_TValue
i_v[2] = (z); \
i_o->tt = LUA_TVECTOR; \
}
#endif
#define setpvalue(obj, x) \
{ \
@ -364,7 +377,7 @@ typedef struct Closure
typedef struct TKey
{
::Value value;
int extra;
int extra[LUA_EXTRA_SIZE];
unsigned tt : 4;
int next : 28; /* for chaining */
} TKey;
@ -381,7 +394,7 @@ typedef struct LuaNode
LuaNode* n_ = (node); \
const TValue* i_o = (obj); \
n_->key.value = i_o->value; \
n_->key.extra = i_o->extra; \
memcpy(n_->key.extra, i_o->extra, sizeof(n_->key.extra)); \
n_->key.tt = i_o->tt; \
checkliveness(L->global, i_o); \
}
@ -392,7 +405,7 @@ typedef struct LuaNode
TValue* i_o = (obj); \
const LuaNode* n_ = (node); \
i_o->value = n_->key.value; \
i_o->extra = n_->key.extra; \
memcpy(i_o->extra, n_->key.extra, sizeof(i_o->extra)); \
i_o->tt = n_->key.tt; \
checkliveness(L->global, i_o); \
}

View File

@ -31,18 +31,19 @@ LUAU_FASTFLAGVARIABLE(LuauArrayBoundary, false)
#define MAXSIZE (1 << MAXBITS)
static_assert(offsetof(LuaNode, val) == 0, "Unexpected Node memory layout, pointer cast in gval2slot is incorrect");
// TKey is bitpacked for memory efficiency so we need to validate bit counts for worst case
static_assert(TKey{{NULL}, 0, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt");
static_assert(TKey{{NULL}, 0, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
static_assert(TKey{{NULL}, 0, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
static_assert(TKey{{NULL}, {0}, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt");
static_assert(TKey{{NULL}, {0}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
static_assert(TKey{{NULL}, {0}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
// reset cache of absent metamethods, cache is updated in luaT_gettm
#define invalidateTMcache(t) t->flags = 0
// empty hash data points to dummynode so that we can always dereference it
const LuaNode luaH_dummynode = {
{{NULL}, 0, LUA_TNIL}, /* value */
{{NULL}, 0, LUA_TNIL, 0} /* key */
{{NULL}, {0}, LUA_TNIL}, /* value */
{{NULL}, {0}, LUA_TNIL, 0} /* key */
};
#define dummynode (&luaH_dummynode)
@ -96,7 +97,7 @@ static LuaNode* hashnum(const Table* t, double n)
static LuaNode* hashvec(const Table* t, const float* v)
{
unsigned int i[3];
unsigned int i[LUA_VECTOR_SIZE];
memcpy(i, v, sizeof(i));
// convert -0 to 0 to make sure they hash to the same value
@ -112,6 +113,12 @@ static LuaNode* hashvec(const Table* t, const float* v)
// Optimized Spatial Hashing for Collision Detection of Deformable Objects
unsigned int h = (i[0] * 73856093) ^ (i[1] * 19349663) ^ (i[2] * 83492791);
#if LUA_VECTOR_SIZE == 4
i[3] = (i[3] == 0x8000000) ? 0 : i[3];
i[3] ^= i[3] >> 17;
h ^= i[3] * 39916801;
#endif
return hashpow2(t, h);
}

View File

@ -601,7 +601,13 @@ static void luau_execute(lua_State* L)
const char* name = getstr(tsvalue(kv));
int ic = (name[0] | ' ') - 'x';
if (unsigned(ic) < 3 && name[1] == '\0')
#if LUA_VECTOR_SIZE == 4
// 'w' is before 'x' in ascii, so ic is -1 when indexing with 'w'
if (ic == -1)
ic = 3;
#endif
if (unsigned(ic) < LUA_VECTOR_SIZE && name[1] == '\0')
{
setnvalue(ra, rb->value.v[ic]);
VM_NEXT();
@ -1526,7 +1532,7 @@ static void luau_execute(lua_State* L)
{
const float* vb = rb->value.v;
const float* vc = rc->value.v;
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2]);
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2], vb[3] + vc[3]);
VM_NEXT();
}
else
@ -1572,7 +1578,7 @@ static void luau_execute(lua_State* L)
{
const float* vb = rb->value.v;
const float* vc = rc->value.v;
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2]);
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2], vb[3] - vc[3]);
VM_NEXT();
}
else
@ -1618,21 +1624,21 @@ static void luau_execute(lua_State* L)
{
const float* vb = rb->value.v;
float vc = cast_to(float, nvalue(rc));
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc);
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc, vb[3] * vc);
VM_NEXT();
}
else if (ttisvector(rb) && ttisvector(rc))
{
const float* vb = rb->value.v;
const float* vc = rc->value.v;
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2]);
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2], vb[3] * vc[3]);
VM_NEXT();
}
else if (ttisnumber(rb) && ttisvector(rc))
{
float vb = cast_to(float, nvalue(rb));
const float* vc = rc->value.v;
setvvalue(ra, vb * vc[0], vb * vc[1], vb * vc[2]);
setvvalue(ra, vb * vc[0], vb * vc[1], vb * vc[2], vb * vc[3]);
VM_NEXT();
}
else
@ -1679,21 +1685,21 @@ static void luau_execute(lua_State* L)
{
const float* vb = rb->value.v;
float vc = cast_to(float, nvalue(rc));
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc);
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc, vb[3] / vc);
VM_NEXT();
}
else if (ttisvector(rb) && ttisvector(rc))
{
const float* vb = rb->value.v;
const float* vc = rc->value.v;
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2]);
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2], vb[3] / vc[3]);
VM_NEXT();
}
else if (ttisnumber(rb) && ttisvector(rc))
{
float vb = cast_to(float, nvalue(rb));
const float* vc = rc->value.v;
setvvalue(ra, vb / vc[0], vb / vc[1], vb / vc[2]);
setvvalue(ra, vb / vc[0], vb / vc[1], vb / vc[2], vb / vc[3]);
VM_NEXT();
}
else
@ -1826,7 +1832,7 @@ static void luau_execute(lua_State* L)
{
const float* vb = rb->value.v;
float vc = cast_to(float, nvalue(kv));
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc);
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc, vb[3] * vc);
VM_NEXT();
}
else
@ -1872,7 +1878,7 @@ static void luau_execute(lua_State* L)
{
const float* vb = rb->value.v;
float vc = cast_to(float, nvalue(kv));
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc);
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc, vb[3] / vc);
VM_NEXT();
}
else
@ -2037,7 +2043,7 @@ static void luau_execute(lua_State* L)
else if (ttisvector(rb))
{
const float* vb = rb->value.v;
setvvalue(ra, -vb[0], -vb[1], -vb[2]);
setvvalue(ra, -vb[0], -vb[1], -vb[2], -vb[3]);
VM_NEXT();
}
else

View File

@ -401,19 +401,19 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
switch (op)
{
case TM_ADD:
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2]);
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2], vb[3] + vc[3]);
return;
case TM_SUB:
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2]);
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2], vb[3] - vc[3]);
return;
case TM_MUL:
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2]);
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2], vb[3] * vc[3]);
return;
case TM_DIV:
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2]);
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2], vb[3] / vc[3]);
return;
case TM_UNM:
setvvalue(ra, -vb[0], -vb[1], -vb[2]);
setvvalue(ra, -vb[0], -vb[1], -vb[2], -vb[3]);
return;
default:
break;
@ -430,10 +430,10 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
switch (op)
{
case TM_MUL:
setvvalue(ra, vb[0] * nc, vb[1] * nc, vb[2] * nc);
setvvalue(ra, vb[0] * nc, vb[1] * nc, vb[2] * nc, vb[3] * nc);
return;
case TM_DIV:
setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc);
setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc, vb[3] / nc);
return;
default:
break;
@ -451,10 +451,10 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
switch (op)
{
case TM_MUL:
setvvalue(ra, nb * vc[0], nb * vc[1], nb * vc[2]);
setvvalue(ra, nb * vc[0], nb * vc[1], nb * vc[2], nb * vc[3]);
return;
case TM_DIV:
setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2]);
setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2], nb / vc[3]);
return;
default:
break;

View File

@ -67,7 +67,12 @@ static int lua_vector(lua_State* L)
double y = luaL_checknumber(L, 2);
double z = luaL_checknumber(L, 3);
#if LUA_VECTOR_SIZE == 4
double w = luaL_optnumber(L, 4, 0.0);
lua_pushvector(L, float(x), float(y), float(z), float(w));
#else
lua_pushvector(L, float(x), float(y), float(z));
#endif
return 1;
}
@ -373,11 +378,17 @@ TEST_CASE("Pack")
TEST_CASE("Vector")
{
ScopedFastFlag sff{"LuauIfElseExpressionBaseSupport", true};
runConformance("vector.lua", [](lua_State* L) {
lua_pushcfunction(L, lua_vector, "vector");
lua_setglobal(L, "vector");
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
#else
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
#endif
luaL_newmetatable(L, "vector");
lua_pushstring(L, "__index");

View File

@ -1,6 +1,9 @@
-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
print('testing vectors')
-- detect vector size
local vector_size = if pcall(function() return vector(0, 0, 0).w end) then 4 else 3
-- equality
assert(vector(1, 2, 3) == vector(1, 2, 3))
assert(vector(0, 1, 2) == vector(-0, 1, 2))
@ -13,8 +16,14 @@ assert(not rawequal(vector(1, 2, 3), vector(1, 2, 4)))
-- type & tostring
assert(type(vector(1, 2, 3)) == "vector")
assert(tostring(vector(1, 2, 3)) == "1, 2, 3")
assert(tostring(vector(-1, 2, 0.5)) == "-1, 2, 0.5")
if vector_size == 4 then
assert(tostring(vector(1, 2, 3, 4)) == "1, 2, 3, 4")
assert(tostring(vector(-1, 2, 0.5, 0)) == "-1, 2, 0.5, 0")
else
assert(tostring(vector(1, 2, 3)) == "1, 2, 3")
assert(tostring(vector(-1, 2, 0.5)) == "-1, 2, 0.5")
end
local t = {}
@ -42,12 +51,19 @@ assert(8 * vector(8, 16, 24) == vector(64, 128, 192));
assert(vector(1, 2, 4) * '8' == vector(8, 16, 32));
assert('8' * vector(8, 16, 24) == vector(64, 128, 192));
assert(vector(1, 2, 4) / vector(8, 16, 24) == vector(1/8, 2/16, 4/24));
if vector_size == 4 then
assert(vector(1, 2, 4, 8) / vector(8, 16, 24, 32) == vector(1/8, 2/16, 4/24, 8/32));
assert(8 / vector(8, 16, 24, 32) == vector(1, 1/2, 1/3, 1/4));
assert('8' / vector(8, 16, 24, 32) == vector(1, 1/2, 1/3, 1/4));
else
assert(vector(1, 2, 4) / vector(8, 16, 24, 1) == vector(1/8, 2/16, 4/24));
assert(8 / vector(8, 16, 24) == vector(1, 1/2, 1/3));
assert('8' / vector(8, 16, 24) == vector(1, 1/2, 1/3));
end
assert(vector(1, 2, 4) / 8 == vector(1/8, 1/4, 1/2));
assert(vector(1, 2, 4) / (1 / val) == vector(1/8, 2/8, 4/8));
assert(8 / vector(8, 16, 24) == vector(1, 1/2, 1/3));
assert(vector(1, 2, 4) / '8' == vector(1/8, 1/4, 1/2));
assert('8' / vector(8, 16, 24) == vector(1, 1/2, 1/3));
assert(-vector(1, 2, 4) == vector(-1, -2, -4));
@ -71,4 +87,9 @@ assert(pcall(function() local t = {} rawset(t, vector(0/0, 2, 3), 1) end) == fal
-- make sure we cover both builtin and C impl
assert(vector(1, 2, 4) == vector("1", "2", "4"))
-- additional checks for 4-component vectors
if vector_size == 4 then
assert(vector(1, 2, 3, 4).w == 4)
end
return 'OK'