mirror of https://github.com/bjornbytes/lovr.git
Rework Buffer formats;
Summary: - Buffer length can be zero (indicates single value instead of array) - Internally, arrays aren't coerced to array-of-single-struct - Removes support for "flat table" data (array-of-structs w/o wrapping each struct in a nested table) - Use consistent syntax for reading table data into buffers: - Numbers are numbers - Vectors are numbers, tables, or vectors - Structs are tables with any combination of integer/string keys - Fields are assigned from integer keys in order, then any remaining fields use the string keys - Arrays are tables of elements, formatted as above - Try to improve error messages for Buffer:setData errors
This commit is contained in:
parent
35e285d53e
commit
48c0b4c167
|
@ -160,17 +160,10 @@ bool luax_writefile(const char* filename, const void* data, size_t size);
|
||||||
struct DataField;
|
struct DataField;
|
||||||
struct Material;
|
struct Material;
|
||||||
struct ColoredString;
|
struct ColoredString;
|
||||||
void luax_checkfieldn(lua_State* L, int index, int type, void* data);
|
void luax_checkbufferdata(lua_State* L, int index, const struct DataField* format, char* data);
|
||||||
void luax_checkfieldv(lua_State* L, int index, int type, void* data);
|
|
||||||
void luax_checkfieldt(lua_State* L, int index, int type, void* data);
|
|
||||||
uint32_t luax_checkfieldarray(lua_State* L, int index, const struct DataField* array, char* data);
|
|
||||||
void luax_checkdataflat(lua_State* L, int index, int subindex, uint32_t count, const struct DataField* format, char* data);
|
|
||||||
void luax_checkdatatuples(lua_State* L, int index, int start, uint32_t count, const struct DataField* format, char* data);
|
|
||||||
void luax_checkdatakeys(lua_State* L, int index, int start, uint32_t count, const struct DataField* array, char* data);
|
|
||||||
void luax_checkstruct(lua_State* L, int index, const struct DataField* fields, uint32_t count, char* data);
|
|
||||||
int luax_pushbufferdata(lua_State* L, const struct DataField* format, uint32_t count, char* data);
|
int luax_pushbufferdata(lua_State* L, const struct DataField* format, uint32_t count, char* data);
|
||||||
void luax_pushbufferformat(lua_State* L, const struct DataField* fields, uint32_t count);
|
void luax_pushbufferformat(lua_State* L, const struct DataField* fields, uint32_t count);
|
||||||
uint32_t luax_gettablestride(lua_State* L, int index, int subindex, struct DataField* fields, uint32_t count);
|
int luax_gettablestride(lua_State* L, int type);
|
||||||
uint32_t luax_checkcomparemode(lua_State* L, int index);
|
uint32_t luax_checkcomparemode(lua_State* L, int index);
|
||||||
struct Material* luax_optmaterial(lua_State* L, int index);
|
struct Material* luax_optmaterial(lua_State* L, int index);
|
||||||
struct ColoredString* luax_checkcoloredstrings(lua_State* L, int index, uint32_t* count, struct ColoredString* stack);
|
struct ColoredString* luax_checkcoloredstrings(lua_State* L, int index, uint32_t* count, struct ColoredString* stack);
|
||||||
|
|
|
@ -626,13 +626,12 @@ static int l_lovrGraphicsNewBuffer(lua_State* L) {
|
||||||
lovrCheck(blob->size < UINT32_MAX, "Blob is too big to create a Buffer");
|
lovrCheck(blob->size < UINT32_MAX, "Blob is too big to create a Buffer");
|
||||||
info.size = (uint32_t) blob->size;
|
info.size = (uint32_t) blob->size;
|
||||||
} else if (type == LUA_TSTRING) {
|
} else if (type == LUA_TSTRING) {
|
||||||
info.fieldCount = 2;
|
info.fieldCount = 1;
|
||||||
info.format = format;
|
info.format = format;
|
||||||
format[0] = (DataField) { .fieldCount = 1 };
|
format[0] = (DataField) { .type = luax_checkdatatype(L, 1) };
|
||||||
format[1] = (DataField) { .type = luax_checkdatatype(L, 1) };
|
|
||||||
} else if (type == LUA_TTABLE) {
|
} else if (type == LUA_TTABLE) {
|
||||||
info.format = format;
|
info.format = format;
|
||||||
format[0] = (DataField) { .fieldCount = luax_len(L, 1) };
|
format[0] = (DataField) { .fieldCount = luax_len(L, 1), .fields = format + 1 };
|
||||||
|
|
||||||
lua_rawgeti(L, 1, 1);
|
lua_rawgeti(L, 1, 1);
|
||||||
bool anonymous = lua_type(L, -1) == LUA_TSTRING;
|
bool anonymous = lua_type(L, -1) == LUA_TSTRING;
|
||||||
|
@ -647,6 +646,12 @@ static int l_lovrGraphicsNewBuffer(lua_State* L) {
|
||||||
format[i] = (DataField) { .type = luax_checkdatatype(L, -1) };
|
format[i] = (DataField) { .type = luax_checkdatatype(L, -1) };
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert single-field anonymous formats to regular arrays
|
||||||
|
if (format->fieldCount == 1) {
|
||||||
|
format->type = format[1].type;
|
||||||
|
format->fieldCount = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
info.fieldCount = 1;
|
info.fieldCount = 1;
|
||||||
luax_checkbufferformat(L, 1, format, &info.fieldCount, COUNTOF(format));
|
luax_checkbufferformat(L, 1, format, &info.fieldCount, COUNTOF(format));
|
||||||
|
@ -665,19 +670,18 @@ static int l_lovrGraphicsNewBuffer(lua_State* L) {
|
||||||
|
|
||||||
// Length/size
|
// Length/size
|
||||||
if (info.format) {
|
if (info.format) {
|
||||||
format->fields = format + 1;
|
|
||||||
lovrGraphicsAlignFields(format, layout);
|
lovrGraphicsAlignFields(format, layout);
|
||||||
switch (lua_type(L, 2)) {
|
switch (lua_type(L, 2)) {
|
||||||
case LUA_TNIL: case LUA_TNONE: format->length = 1; break;
|
case LUA_TNIL: case LUA_TNONE: format->length = 0; break;
|
||||||
case LUA_TNUMBER: format->length = luax_checku32(L, 2); break;
|
case LUA_TNUMBER: format->length = luax_checku32(L, 2); break;
|
||||||
case LUA_TTABLE:
|
case LUA_TTABLE:
|
||||||
lua_rawgeti(L, 2, 1);
|
lua_rawgeti(L, 2, 1);
|
||||||
if (lua_istable(L, -1)) {
|
if (lua_type(L, -1) == LUA_TNUMBER) {
|
||||||
format->length = luax_len(L, -2);
|
lovrCheck(format->fieldCount <= 1, "Struct data must be provided as a table of tables");
|
||||||
} else if (lua_isnil(L, -1) && format->fields[0].name) {
|
DataType type = format->fieldCount == 0 ? format->type : format->fields[0].type;
|
||||||
format->length = 1;
|
format->length = luax_len(L, -2) / luax_gettablestride(L, type);
|
||||||
} else {
|
} else {
|
||||||
format->length = luax_len(L, -2) / luax_gettablestride(L, 2, 1, format->fields, format->fieldCount);
|
format->length = luax_len(L, -2);
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
hasData = true;
|
hasData = true;
|
||||||
|
@ -689,7 +693,7 @@ static int l_lovrGraphicsNewBuffer(lua_State* L) {
|
||||||
format->length = info.size / format->stride;
|
format->length = info.size / format->stride;
|
||||||
break;
|
break;
|
||||||
} else if (luax_tovector(L, 2, NULL)) {
|
} else if (luax_tovector(L, 2, NULL)) {
|
||||||
format->length = 1;
|
format->length = 0;
|
||||||
hasData = true;
|
hasData = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -704,25 +708,7 @@ static int l_lovrGraphicsNewBuffer(lua_State* L) {
|
||||||
if (blob) {
|
if (blob) {
|
||||||
memcpy(data, blob->data, info.size);
|
memcpy(data, blob->data, info.size);
|
||||||
} else if (hasData) {
|
} else if (hasData) {
|
||||||
if (luax_tovector(L, 2, NULL)) {
|
luax_checkbufferdata(L, 2, format, data);
|
||||||
luax_checkfieldv(L, 2, format->fields[0].type, data);
|
|
||||||
} else if (luax_len(L, 2) == 0 && format->fields[0].name) {
|
|
||||||
luax_checkstruct(L, 2, format->fields, format->fieldCount, data);
|
|
||||||
} else {
|
|
||||||
lua_rawgeti(L, 2, 1);
|
|
||||||
bool complexFormat = lovrBufferGetInfo(buffer)->complexFormat;
|
|
||||||
bool tableOfTables = complexFormat || lua_istable(L, -1);
|
|
||||||
bool tuples = tableOfTables && !complexFormat && (luax_len(L, -1) > 0 || !format->fields[0].name);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
if (tuples) {
|
|
||||||
luax_checkdatatuples(L, 2, 1, format->length, format, data);
|
|
||||||
} else if (tableOfTables) {
|
|
||||||
luax_checkdatakeys(L, 2, 1, format->length, format, data);
|
|
||||||
} else {
|
|
||||||
luax_checkdataflat(L, 2, 1, format->length, format, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
luax_pushtype(L, Buffer, buffer);
|
luax_pushtype(L, Buffer, buffer);
|
||||||
|
@ -1404,7 +1390,7 @@ static int l_lovrGraphicsNewMesh(lua_State* L) {
|
||||||
if (blob) {
|
if (blob) {
|
||||||
memcpy(vertices, blob->data, blob->size);
|
memcpy(vertices, blob->data, blob->size);
|
||||||
} else if (hasData) {
|
} else if (hasData) {
|
||||||
luax_checkdatatuples(L, index, 1, format->length, lovrMeshGetVertexFormat(mesh), vertices);
|
luax_checkbufferdata(L, index, lovrMeshGetVertexFormat(mesh), vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
luax_pushtype(L, Mesh, mesh);
|
luax_pushtype(L, Mesh, mesh);
|
||||||
|
|
|
@ -62,11 +62,67 @@ typedef union {
|
||||||
float* f32;
|
float* f32;
|
||||||
} DataPointer;
|
} DataPointer;
|
||||||
|
|
||||||
void luax_checkfieldn(lua_State* L, int index, int type, void* data) {
|
#ifndef LOVR_UNCHECKED
|
||||||
|
#define luax_fieldcheck(L, cond, index, field, arr) if (!(cond)) luax_fielderror(L, index, field, arr)
|
||||||
|
#else
|
||||||
|
#define luax_fieldcheck(L, cond, index, field, arr) ((void) 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void luax_fielderror(lua_State* L, int index, const DataField* field, bool arr) {
|
||||||
|
if (index < 0) index += lua_gettop(L) + 1;
|
||||||
|
|
||||||
|
if (!field->parent) {
|
||||||
|
lua_pushliteral(L, "buffer data");
|
||||||
|
} else if (!field->name) {
|
||||||
|
lua_pushliteral(L, "<anonymous>");
|
||||||
|
} else {
|
||||||
|
lua_pushstring(L, field->name);
|
||||||
|
DataField* parent = field->parent;
|
||||||
|
while (parent && parent->name) {
|
||||||
|
if (parent->length > 0) {
|
||||||
|
lua_pushfstring(L, "%s[]", parent->name);
|
||||||
|
} else {
|
||||||
|
lua_pushstring(L, parent->name);
|
||||||
|
}
|
||||||
|
lua_insert(L, -2);
|
||||||
|
lua_pushliteral(L, ".");
|
||||||
|
lua_insert(L, -2);
|
||||||
|
lua_concat(L, 3);
|
||||||
|
parent = parent->parent;
|
||||||
|
}
|
||||||
|
lua_pushliteral(L, "'");
|
||||||
|
lua_insert(L, -2);
|
||||||
|
lua_pushliteral(L, "'");
|
||||||
|
lua_concat(L, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* kind;
|
||||||
|
const char* expected;
|
||||||
|
if (arr && field->length > 0) {
|
||||||
|
kind = "array";
|
||||||
|
expected = "table";
|
||||||
|
} else if (field->fieldCount > 0) {
|
||||||
|
kind = "struct";
|
||||||
|
expected = "table";
|
||||||
|
} else if (typeComponents[field->type] > 1) {
|
||||||
|
kind = "vector";
|
||||||
|
expected = "number, table, or vector";
|
||||||
|
} else {
|
||||||
|
kind = "scalar";
|
||||||
|
expected = "number";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = lua_tostring(L, -1);
|
||||||
|
const char* typename = luaL_typename(L, index);
|
||||||
|
luaL_error(L, "Bad type for %s %s: %s expected, got %s", kind, name, expected, typename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void luax_checkfieldn(lua_State* L, int index, const DataField* field, void* data) {
|
||||||
DataPointer p = { .raw = data };
|
DataPointer p = { .raw = data };
|
||||||
for (uint32_t i = 0; i < typeComponents[type]; i++) {
|
for (uint32_t i = 0; i < typeComponents[field->type]; i++) {
|
||||||
|
luax_fieldcheck(L, lua_type(L, index + i) == LUA_TNUMBER, index + i, field, false);
|
||||||
double x = lua_tonumber(L, index + i);
|
double x = lua_tonumber(L, index + i);
|
||||||
switch (type) {
|
switch (field->type) {
|
||||||
case TYPE_I8x4: p.i8[i] = (int8_t) x; break;
|
case TYPE_I8x4: p.i8[i] = (int8_t) x; break;
|
||||||
case TYPE_U8x4: p.u8[i] = (uint8_t) x; break;
|
case TYPE_U8x4: p.u8[i] = (uint8_t) x; break;
|
||||||
case TYPE_SN8x4: p.i8[i] = (int8_t) CLAMP(x, -1.f, 1.f) * INT8_MAX; break;
|
case TYPE_SN8x4: p.i8[i] = (int8_t) CLAMP(x, -1.f, 1.f) * INT8_MAX; break;
|
||||||
|
@ -107,19 +163,18 @@ void luax_checkfieldn(lua_State* L, int index, int type, void* data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void luax_checkfieldv(lua_State* L, int index, int type, void* data) {
|
static void luax_checkfieldv(lua_State* L, int index, const DataField* field, void* data) {
|
||||||
DataPointer p = { .raw = data };
|
DataPointer p = { .raw = data };
|
||||||
uint32_t n = typeComponents[type];
|
|
||||||
lovrCheck(n > 1, "Expected number for scalar data type, got vector");
|
|
||||||
VectorType vectorType;
|
VectorType vectorType;
|
||||||
float* v = luax_tovector(L, index, &vectorType);
|
float* v = luax_tovector(L, index, &vectorType);
|
||||||
lovrCheck(v, "Expected vector, got non-vector userdata");
|
uint32_t n = typeComponents[field->type];
|
||||||
if (n >= TYPE_MAT2 && n <= TYPE_MAT4) {
|
luax_fieldcheck(L, v && n > 1, index, field, false);
|
||||||
|
if (field->type >= TYPE_MAT2 && field->type <= TYPE_MAT4) {
|
||||||
lovrCheck(vectorType == V_MAT4, "Tried to send a non-matrix to a matrix type");
|
lovrCheck(vectorType == V_MAT4, "Tried to send a non-matrix to a matrix type");
|
||||||
} else {
|
} else {
|
||||||
lovrCheck(vectorComponents[vectorType] == n, "Expected %d vector components, got %d", n, vectorComponents[vectorType]);
|
lovrCheck(vectorComponents[vectorType] == n, "Expected %d vector components, got %d", n, vectorComponents[vectorType]);
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (field->type) {
|
||||||
case TYPE_I8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) v[i]; break;
|
case TYPE_I8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) v[i]; break;
|
||||||
case TYPE_U8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) v[i]; break;
|
case TYPE_U8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) v[i]; break;
|
||||||
case TYPE_SN8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) CLAMP(v[i], -1.f, 1.f) * INT8_MAX; break;
|
case TYPE_SN8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) CLAMP(v[i], -1.f, 1.f) * INT8_MAX; break;
|
||||||
|
@ -152,176 +207,121 @@ void luax_checkfieldv(lua_State* L, int index, int type, void* data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void luax_checkfieldt(lua_State* L, int index, int type, void* data) {
|
static void luax_checkfieldt(lua_State* L, int index, const DataField* field, void* data) {
|
||||||
|
luax_fieldcheck(L, lua_istable(L, index), index, field, false);
|
||||||
if (index < 0) index += lua_gettop(L) + 1;
|
if (index < 0) index += lua_gettop(L) + 1;
|
||||||
int n = typeComponents[type];
|
int n = typeComponents[field->type];
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
lua_rawgeti(L, index, i + 1);
|
lua_rawgeti(L, index, i);
|
||||||
}
|
}
|
||||||
luax_checkfieldn(L, -n, type, data);
|
luax_checkfieldn(L, -n, field, data);
|
||||||
lua_pop(L, n);
|
lua_pop(L, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t luax_checkfieldarray(lua_State* L, int index, const DataField* array, char* data) {
|
static void luax_checkstruct(lua_State* L, int index, const DataField* structure, char* data) {
|
||||||
int components = typeComponents[array->type];
|
luax_fieldcheck(L, lua_istable(L, index), index, structure, false);
|
||||||
|
if (index < 0) index += lua_gettop(L) + 1;
|
||||||
uint32_t length = luax_len(L, index);
|
uint32_t length = luax_len(L, index);
|
||||||
|
uint32_t f = 0;
|
||||||
|
|
||||||
if (components == 1) {
|
// Number keys
|
||||||
uint32_t count = MIN(length, array->length);
|
for (uint32_t i = 1; i <= length && f < structure->fieldCount; f++) {
|
||||||
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
lua_rawgeti(L, index, i);
|
||||||
lua_rawgeti(L, index, i + 1);
|
const DataField* field = &structure->fields[f];
|
||||||
luax_checkfieldn(L, -1, array->type, data);
|
if (field->length == 0 && field->fieldCount == 0 && lua_type(L, -1) == LUA_TNUMBER) {
|
||||||
lua_pop(L, 1);
|
int n = typeComponents[field->type];
|
||||||
}
|
for (int c = 1; c < n; c++) lua_rawgeti(L, index, i + c);
|
||||||
return count;
|
luax_checkfieldn(L, -n, field, data + field->offset);
|
||||||
}
|
|
||||||
|
|
||||||
lua_rawgeti(L, index, 1);
|
|
||||||
int innerType = lua_type(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
uint32_t count;
|
|
||||||
|
|
||||||
switch (innerType) {
|
|
||||||
case LUA_TNUMBER:
|
|
||||||
if (index < 0) index += lua_gettop(L) + 1;
|
|
||||||
count = MIN(array->length, length / components);
|
|
||||||
lovrCheck(length % components == 0, "Table length for key '%s' must be divisible by %d", array->name, components);
|
|
||||||
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
|
||||||
for (int j = 1; j <= components; j++) {
|
|
||||||
lua_rawgeti(L, index, i * components + j);
|
|
||||||
}
|
|
||||||
luax_checkfieldn(L, -components, array->type, data);
|
|
||||||
lua_pop(L, components);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LUA_TUSERDATA:
|
|
||||||
case LUA_TLIGHTUSERDATA:
|
|
||||||
count = MIN(array->length, length);
|
|
||||||
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
|
||||||
lua_rawgeti(L, index, i + 1);
|
|
||||||
luax_checkfieldv(L, -1, array->type, data);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LUA_TTABLE:
|
|
||||||
count = MIN(array->length, length);
|
|
||||||
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
|
||||||
lua_rawgeti(L, index, i + 1);
|
|
||||||
luax_checkfieldt(L, -1, array->type, data);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LUA_TNIL:
|
|
||||||
count = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void luax_checkdataflat(lua_State* L, int index, int subindex, uint32_t count, const DataField* format, char* data) {
|
|
||||||
for (uint32_t i = 0; i < count; i++, data += format->stride) {
|
|
||||||
for (uint32_t f = 0; f < format->fieldCount; f++) {
|
|
||||||
int n = 1;
|
|
||||||
lua_rawgeti(L, index, subindex++);
|
|
||||||
const DataField* field = &format->fields[f];
|
|
||||||
if (lua_isuserdata(L, -1)) {
|
|
||||||
luax_checkfieldv(L, -1, field->type, data + field->offset);
|
|
||||||
} else {
|
|
||||||
n = typeComponents[field->type];
|
|
||||||
for (int c = 1; c < n; c++) {
|
|
||||||
lua_rawgeti(L, index, subindex++);
|
|
||||||
}
|
|
||||||
luax_checkfieldn(L, -n, field->type, data + field->offset);
|
|
||||||
}
|
|
||||||
lua_pop(L, n);
|
lua_pop(L, n);
|
||||||
}
|
i += n;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void luax_checkdatatuples(lua_State* L, int index, int start, uint32_t count, const DataField* format, char* data) {
|
|
||||||
for (uint32_t i = 0; i < count; i++, data += format->stride) {
|
|
||||||
lua_rawgeti(L, index, start + i);
|
|
||||||
lovrCheck(lua_type(L, -1) == LUA_TTABLE, "Expected table of tables");
|
|
||||||
|
|
||||||
for (uint32_t f = 0, subindex = 1; f < format->fieldCount; f++) {
|
|
||||||
int n = 1;
|
|
||||||
lua_rawgeti(L, -1, subindex);
|
|
||||||
const DataField* field = &format->fields[f];
|
|
||||||
if (lua_isuserdata(L, -1)) {
|
|
||||||
luax_checkfieldv(L, -1, field->type, data + field->offset);
|
|
||||||
} else {
|
} else {
|
||||||
while (n < (int) typeComponents[field->type]) {
|
luax_checkbufferdata(L, -1, field, data + field->offset);
|
||||||
lua_rawgeti(L, -n - 1, subindex + n);
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
luax_checkfieldn(L, -n, field->type, data + field->offset);
|
|
||||||
}
|
|
||||||
subindex += n;
|
|
||||||
lua_pop(L, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void luax_checkdatakeys(lua_State* L, int index, int start, uint32_t count, const DataField* array, char* data) {
|
// String keys
|
||||||
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
while (f < structure->fieldCount && structure->fields[f].name) {
|
||||||
lua_rawgeti(L, index, start + i);
|
const DataField* field = &structure->fields[f++];
|
||||||
lovrCheck(lua_istable(L, -1), "Expected table of tables");
|
|
||||||
luax_checkstruct(L, -1, array->fields, array->fieldCount, data);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void luax_checkstruct(lua_State* L, int index, const DataField* fields, uint32_t fieldCount, char* data) {
|
|
||||||
for (uint32_t f = 0; f < fieldCount; f++) {
|
|
||||||
const DataField* field = &fields[f];
|
|
||||||
int n = field->fieldCount == 0 ? typeComponents[field->type] : 0;
|
|
||||||
lua_getfield(L, index, field->name);
|
lua_getfield(L, index, field->name);
|
||||||
|
|
||||||
if (lua_isnil(L, -1)) {
|
if (lua_isnil(L, -1)) {
|
||||||
memset(data + field->offset, 0, MAX(field->length, 1) * field->stride);
|
memset(data + field->offset, 0, MAX(field->length, 1) * field->stride);
|
||||||
lua_pop(L, 1);
|
} else {
|
||||||
continue;
|
luax_checkbufferdata(L, -1, field, data + field->offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void luax_checkarray(lua_State* L, int index, int start, uint32_t count, const DataField* array, char* data) {
|
||||||
|
luax_fieldcheck(L, lua_istable(L, index), index, array, true);
|
||||||
|
uint32_t length = luax_len(L, index);
|
||||||
|
count = MIN(count, (length - start + 1));
|
||||||
|
|
||||||
|
if (array->fieldCount > 0) {
|
||||||
|
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
||||||
|
lua_rawgeti(L, index, start + i);
|
||||||
|
luax_checkstruct(L, -1, array, data);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lua_rawgeti(L, index, start);
|
||||||
|
int type = lua_type(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
if (type == LUA_TNUMBER) {
|
||||||
|
if (index < 0) index += lua_gettop(L) + 1;
|
||||||
|
uint32_t n = typeComponents[array->type];
|
||||||
|
count = MIN(count, (length - start + 1) / n);
|
||||||
|
for (uint32_t i = 0; i < count; i += n, data += array->stride) {
|
||||||
|
for (uint32_t j = 0; j < n; j++) {
|
||||||
|
lua_rawgeti(L, index, start + i + j);
|
||||||
|
}
|
||||||
|
luax_checkfieldn(L, -n, array, data);
|
||||||
|
lua_pop(L, n);
|
||||||
|
}
|
||||||
|
} else if (type == LUA_TUSERDATA || type == LUA_TLIGHTUSERDATA) {
|
||||||
|
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
||||||
|
lua_rawgeti(L, index, start + i);
|
||||||
|
luax_checkfieldv(L, -1, array, data);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
} else if (type == LUA_TTABLE) {
|
||||||
|
for (uint32_t i = 0; i < count; i++, data += array->stride) {
|
||||||
|
lua_rawgeti(L, index, start + i);
|
||||||
|
luax_checkfieldt(L, -1, array, data);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lua_rawgeti(L, index, start);
|
||||||
|
luax_fieldcheck(L, type == LUA_TNIL, -1, array, false);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void luax_checkbufferdata(lua_State* L, int index, const DataField* field, char* data) {
|
||||||
|
int type = lua_type(L, index);
|
||||||
|
|
||||||
if (field->length > 0) {
|
if (field->length > 0) {
|
||||||
lovrCheck(lua_istable(L, -1), "Expected table for key '%s'", field->name);
|
luax_checkarray(L, index, 1, field->length, field, data);
|
||||||
uint32_t count;
|
|
||||||
|
|
||||||
if (field->fieldCount > 0) {
|
|
||||||
uint32_t tableLength = luax_len(L, -1);
|
|
||||||
count = MIN(field->length, tableLength);
|
|
||||||
luax_checkdatakeys(L, -1, 1, count, field, data + field->offset);
|
|
||||||
} else {
|
|
||||||
count = luax_checkfieldarray(L, -1, field, data + field->offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < field->length) {
|
|
||||||
memset(data + field->offset + count * field->stride, 0, (field->length - count) * field->stride);
|
|
||||||
}
|
|
||||||
} else if (field->fieldCount > 0) {
|
} else if (field->fieldCount > 0) {
|
||||||
lovrCheck(lua_istable(L, -1), "Expected table for key '%s'", field->name);
|
luax_checkstruct(L, index, field, data);
|
||||||
luax_checkstruct(L, -1, field->fields, field->fieldCount, data + field->offset);
|
} else if (typeComponents[field->type] == 1) {
|
||||||
} else if (n == 1) {
|
luax_checkfieldn(L, index, field, data);
|
||||||
lovrCheck(lua_type(L, -1) == LUA_TNUMBER, "Expected number for key '%s'", field->name);
|
} else if (type == LUA_TUSERDATA || type == LUA_TLIGHTUSERDATA) {
|
||||||
luax_checkfieldn(L, -1, field->type, data + field->offset);
|
luax_checkfieldv(L, index, field, data);
|
||||||
} else if (lua_isuserdata(L, -1)) {
|
} else if (type == LUA_TTABLE) {
|
||||||
luax_checkfieldv(L, -1, field->type, data + field->offset);
|
luax_checkfieldt(L, index, field, data);
|
||||||
} else if (lua_istable(L, -1)) {
|
|
||||||
lovrCheck(luax_len(L, -1) == n, "Table length for key '%s' must be %d", field->name, n);
|
|
||||||
luax_checkfieldt(L, -1, field->type, data + field->offset);
|
|
||||||
} else {
|
} else {
|
||||||
lovrThrow("Expected table or vector for key '%s'", field->name);
|
luax_fielderror(L, index, field, false);
|
||||||
}
|
|
||||||
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int luax_pushcomponents(lua_State* L, DataType type, char* data) {
|
static int luax_pushfieldn(lua_State* L, DataType type, char* data) {
|
||||||
DataPointer p = { .raw = data };
|
DataPointer p = { .raw = data };
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TYPE_I8x4: for (int i = 0; i < 4; i++) lua_pushinteger(L, p.i8[i]); return 4;
|
case TYPE_I8x4: for (int i = 0; i < 4; i++) lua_pushinteger(L, p.i8[i]); return 4;
|
||||||
|
@ -361,104 +361,58 @@ static int luax_pushcomponents(lua_State* L, DataType type, char* data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int luax_pushstruct(lua_State* L, const DataField* fields, uint32_t count, char* data) {
|
int luax_pushbufferdata(lua_State* L, const DataField* format, uint32_t count, char* data) {
|
||||||
lua_createtable(L, 0, count);
|
if (format->length > 0 && count > 0) {
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
lua_createtable(L, count, 0);
|
||||||
const DataField* field = &fields[i];
|
if (format->fieldCount > 0) {
|
||||||
if (field->length > 0) {
|
for (uint32_t i = 0; i < count; i++, data += format->stride) {
|
||||||
if (field->fieldCount > 0) {
|
luax_pushbufferdata(L, format, 0, data);
|
||||||
lua_createtable(L, field->length, 0);
|
lua_rawseti(L, -2, i + 1);
|
||||||
for (uint32_t j = 0; j < field->length; j++) {
|
|
||||||
luax_pushstruct(L, field->fields, field->fieldCount, data + field->offset + j * field->stride);
|
|
||||||
lua_rawseti(L, -2, j + 1);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DataType type = field->type;
|
int n = typeComponents[format->type];
|
||||||
uint32_t n = typeComponents[field->type];
|
for (uint32_t i = 0; i < count; i++, data += format->stride) {
|
||||||
lua_createtable(L, (int) (field->length * n), 0);
|
luax_pushfieldn(L, format->type, data);
|
||||||
for (uint32_t j = 0, k = 1; j < field->length; j++, k += n) {
|
for (int c = 0; c < n; c++) {
|
||||||
luax_pushcomponents(L, type, data + field->offset + j * field->stride);
|
lua_rawseti(L, -1 - n + c, i * n + c + 1);
|
||||||
for (uint32_t c = 0; c < n; c++) {
|
|
||||||
lua_rawseti(L, -1 - n + c, k + n - 1 - c);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 1;
|
||||||
|
} else if (format->fieldCount > 0) {
|
||||||
|
lua_createtable(L, 0, format->fieldCount);
|
||||||
|
for (uint32_t f = 0; f < format->fieldCount; f++) {
|
||||||
|
const DataField* field = &format->fields[f];
|
||||||
|
if (field->length > 0) {
|
||||||
|
luax_pushbufferdata(L, field, field->length, data + field->offset);
|
||||||
} else if (field->fieldCount > 0) {
|
} else if (field->fieldCount > 0) {
|
||||||
luax_pushstruct(L, field->fields, field->fieldCount, data + field->offset);
|
luax_pushbufferdata(L, field, 0, data + field->offset);
|
||||||
} else {
|
} else {
|
||||||
uint32_t n = typeComponents[field->type];
|
uint32_t n = typeComponents[field->type];
|
||||||
if (n > 1) {
|
if (n > 1) {
|
||||||
lua_createtable(L, n, 0);
|
lua_createtable(L, n, 0);
|
||||||
luax_pushcomponents(L, field->type, data + field->offset);
|
luax_pushfieldn(L, field->type, data + field->offset);
|
||||||
for (uint32_t c = 0; c < n; c++) {
|
for (uint32_t c = 0; c < n; c++) {
|
||||||
lua_rawseti(L, -1 - n + c, n - c);
|
lua_rawseti(L, -1 - n + c, n - c);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
luax_pushcomponents(L, field->type, data + field->offset);
|
luax_pushfieldn(L, field->type, data + field->offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (field->name) {
|
||||||
lua_setfield(L, -2, field->name);
|
lua_setfield(L, -2, field->name);
|
||||||
|
} else {
|
||||||
|
lua_rawseti(L, -2, f + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
int luax_pushbufferdata(lua_State* L, const DataField* format, uint32_t count, char* data) {
|
|
||||||
lua_createtable(L, count, 0);
|
|
||||||
|
|
||||||
bool nested = false;
|
|
||||||
for (uint32_t i = 0; i < format->fieldCount; i++) {
|
|
||||||
if (format->fields[i].fields || format->fields[i].length > 0) {
|
|
||||||
nested = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format->fieldCount > 1 || typeComponents[format->fields[0].type] > 1 || nested) {
|
|
||||||
if (nested) {
|
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
|
||||||
luax_pushstruct(L, format->fields, format->fieldCount, data);
|
|
||||||
lua_rawseti(L, -2, i + 1);
|
|
||||||
data += format->stride;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for (uint32_t i = 0; i < count; i++, data += format->stride) {
|
return luax_pushfieldn(L, format->type, data);
|
||||||
lua_newtable(L);
|
|
||||||
int j = 1;
|
|
||||||
for (uint32_t f = 0; f < format->fieldCount; f++) {
|
|
||||||
const DataField* field = &format->fields[f];
|
|
||||||
int n = luax_pushcomponents(L, field->type, data + field->offset);
|
|
||||||
for (int c = 0; c < n; c++) {
|
|
||||||
lua_rawseti(L, -1 - n + c, j + n - 1 - c);
|
|
||||||
}
|
|
||||||
j += n;
|
|
||||||
}
|
|
||||||
lua_rawseti(L, -2, i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (uint32_t i = 0; i < count; i++, data += format->stride) {
|
|
||||||
luax_pushcomponents(L, format->fields[0].type, data + format->fields[0].offset);
|
|
||||||
lua_rawseti(L, -2, i + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
int luax_gettablestride(lua_State* L, int type) {
|
||||||
}
|
return typeComponents[type];
|
||||||
|
|
||||||
uint32_t luax_gettablestride(lua_State* L, int index, int subindex, DataField* fields, uint32_t count) {
|
|
||||||
int stride = 0;
|
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
|
||||||
lovrCheck(!fields[i].fields && fields[i].length == 0, "This Buffer's format requires data to be given as a table of tables");
|
|
||||||
lua_rawgeti(L, index, subindex + stride);
|
|
||||||
switch (lua_type(L, -1)) {
|
|
||||||
case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA: stride++; break;
|
|
||||||
case LUA_TNUMBER: stride += typeComponents[fields[i].type]; break;
|
|
||||||
case LUA_TNIL: lovrThrow("Table does not have enough elements for a single element");
|
|
||||||
default: lovrThrow("Expected table of numbers and/or vectors");
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
return (uint32_t) stride;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_lovrBufferGetSize(lua_State* L) {
|
static int l_lovrBufferGetSize(lua_State* L) {
|
||||||
|
@ -519,7 +473,11 @@ static int l_lovrBufferGetFormat(lua_State* L) {
|
||||||
Buffer* buffer = luax_checktype(L, 1, Buffer);
|
Buffer* buffer = luax_checktype(L, 1, Buffer);
|
||||||
const DataField* format = lovrBufferGetInfo(buffer)->format;
|
const DataField* format = lovrBufferGetInfo(buffer)->format;
|
||||||
if (format) {
|
if (format) {
|
||||||
|
if (format->fieldCount > 0) {
|
||||||
luax_pushbufferformat(L, format->fields, format->fieldCount);
|
luax_pushbufferformat(L, format->fields, format->fieldCount);
|
||||||
|
} else {
|
||||||
|
luax_pushbufferformat(L, format, 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
}
|
}
|
||||||
|
@ -540,78 +498,42 @@ static int l_lovrBufferGetData(lua_State* L) {
|
||||||
Buffer* buffer = luax_checktype(L, 1, Buffer);
|
Buffer* buffer = luax_checktype(L, 1, Buffer);
|
||||||
const DataField* format = lovrBufferGetInfo(buffer)->format;
|
const DataField* format = lovrBufferGetInfo(buffer)->format;
|
||||||
lovrCheck(format, "Buffer:getData requires the Buffer to have a format");
|
lovrCheck(format, "Buffer:getData requires the Buffer to have a format");
|
||||||
|
if (format->length > 0) {
|
||||||
uint32_t index = luax_optu32(L, 2, 1) - 1;
|
uint32_t index = luax_optu32(L, 2, 1) - 1;
|
||||||
lovrCheck(index < format->length, "Buffer:getData index exceeds the Buffer's length");
|
lovrCheck(index < format->length, "Buffer:getData index exceeds the Buffer's length");
|
||||||
uint32_t count = luax_optu32(L, 3, format->length - index);
|
uint32_t count = luax_optu32(L, 3, format->length - index);
|
||||||
void* data = lovrBufferGetData(buffer, index * format->stride, count * format->stride);
|
void* data = lovrBufferGetData(buffer, index * format->stride, count * format->stride);
|
||||||
return luax_pushbufferdata(L, format, count, data);
|
return luax_pushbufferdata(L, format, count, data);
|
||||||
|
} else {
|
||||||
|
void* data = lovrBufferGetData(buffer, 0, format->stride);
|
||||||
|
return luax_pushbufferdata(L, format, 0, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_lovrBufferSetData(lua_State* L) {
|
static int l_lovrBufferSetData(lua_State* L) {
|
||||||
Buffer* buffer = luax_checktype(L, 1, Buffer);
|
Buffer* buffer = luax_checktype(L, 1, Buffer);
|
||||||
const BufferInfo* info = lovrBufferGetInfo(buffer);
|
const BufferInfo* info = lovrBufferGetInfo(buffer);
|
||||||
const DataField* format = info->format;
|
const DataField* format = info->format;
|
||||||
bool hasNames = format->fields[0].name;
|
|
||||||
|
|
||||||
if (format && format->length == 1) { // When Buffer's length is 1, you can pass a single item
|
|
||||||
if (lua_istable(L, 2) && luax_len(L, 2) == 0 && hasNames) {
|
|
||||||
luax_checkstruct(L, 2, format->fields, format->fieldCount, lovrBufferSetData(buffer, 0, ~0u));
|
|
||||||
return 0;
|
|
||||||
} else if (typeComponents[format->fields[0].type] == 1 && lua_type(L, 2) == LUA_TNUMBER) {
|
|
||||||
luax_checkfieldn(L, 2, format->fields[0].type, lovrBufferSetData(buffer, 0, ~0u));
|
|
||||||
return 0;
|
|
||||||
} else if (typeComponents[format->fields[0].type] > 1 && luax_tovector(L, 2, NULL)) {
|
|
||||||
luax_checkfieldv(L, 2, format->fields[0].type, lovrBufferSetData(buffer, 0, ~0u));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lua_istable(L, 2)) {
|
|
||||||
lovrCheck(format, "Buffer must be created with format information to copy a table to it");
|
|
||||||
|
|
||||||
|
if (format) {
|
||||||
|
if (format->length > 0) {
|
||||||
|
luax_fieldcheck(L, lua_istable(L, 2), 2, format, -1);
|
||||||
uint32_t length = luax_len(L, 2);
|
uint32_t length = luax_len(L, 2);
|
||||||
uint32_t dstIndex = luax_optu32(L, 3, 1) - 1;
|
uint32_t dstIndex = luax_optu32(L, 3, 1) - 1;
|
||||||
uint32_t srcIndex = luax_optu32(L, 4, 1) - 1;
|
uint32_t srcIndex = luax_optu32(L, 4, 1) - 1;
|
||||||
|
|
||||||
// Fast path for scalar formats
|
lua_rawgeti(L, 2, srcIndex + 1);
|
||||||
if (format->fieldCount == 1 && typeComponents[format->fields[0].type] == 1) {
|
uint32_t tstride = format->fieldCount == 0 && lua_type(L, -1) == LUA_TNUMBER ? typeComponents[format->type] : 1;
|
||||||
uint32_t limit = MIN(format->length - dstIndex, length - srcIndex);
|
|
||||||
uint32_t count = luax_optu32(L, 5, limit);
|
|
||||||
char* data = lovrBufferSetData(buffer, dstIndex * format->stride, count * format->stride);
|
|
||||||
for (uint32_t i = 0; i < count; i++, data += format->stride) {
|
|
||||||
lua_rawgeti(L, 2, srcIndex + i + 1);
|
|
||||||
luax_checkfieldn(L, -1, format->fields[0].type, data);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_rawgeti(L, 2, 1);
|
|
||||||
bool tableOfTables = info->complexFormat || lua_istable(L, -1);
|
|
||||||
bool tuples = tableOfTables && !info->complexFormat && (luax_len(L, -1) > 0 || !hasNames);
|
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
if (tableOfTables) {
|
uint32_t limit = MIN(format->length - dstIndex, (length - srcIndex) / tstride);
|
||||||
uint32_t limit = MIN(format->length - dstIndex, length - srcIndex);
|
|
||||||
uint32_t count = luax_optu32(L, 5, limit);
|
uint32_t count = luax_optu32(L, 5, limit);
|
||||||
|
|
||||||
lovrCheck(length - srcIndex >= count, "Table does not have enough elements");
|
|
||||||
char* data = lovrBufferSetData(buffer, dstIndex * format->stride, count * format->stride);
|
char* data = lovrBufferSetData(buffer, dstIndex * format->stride, count * format->stride);
|
||||||
|
luax_checkarray(L, 2, srcIndex + 1, count, format, data);
|
||||||
if (tuples) {
|
|
||||||
luax_checkdatatuples(L, 2, srcIndex + 1, count, format, data);
|
|
||||||
} else {
|
} else {
|
||||||
luax_checkdatakeys(L, 2, srcIndex + 1, count, format, data);
|
luaL_checkany(L, 2);
|
||||||
}
|
luax_checkbufferdata(L, 2, format, lovrBufferSetData(buffer, 0, format->stride));
|
||||||
} else {
|
|
||||||
uint32_t tableStride = luax_gettablestride(L, 2, srcIndex + 1, format->fields, format->fieldCount);
|
|
||||||
lovrCheck(length % tableStride == 0, "Table length is not aligned -- it either uses inconsistent types for each field or is missing some data");
|
|
||||||
uint32_t limit = MIN(format->length - dstIndex, (length - srcIndex) / tableStride);
|
|
||||||
uint32_t count = luax_optu32(L, 5, limit);
|
|
||||||
|
|
||||||
lovrCheck((length - srcIndex) / tableStride >= count, "Table does not have enough elements");
|
|
||||||
char* data = lovrBufferSetData(buffer, dstIndex * format->stride, count * format->stride);
|
|
||||||
luax_checkdataflat(L, 2, srcIndex + 1, count, format, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -647,7 +569,7 @@ static int l_lovrBufferSetData(lua_State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return luax_typeerror(L, 2, "table, Blob, or Buffer");
|
return luax_typeerror(L, 2, "Blob or Buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_lovrBufferMapData(lua_State* L) {
|
static int l_lovrBufferMapData(lua_State* L) {
|
||||||
|
|
|
@ -83,7 +83,7 @@ static int l_lovrMeshSetVertices(lua_State* L) {
|
||||||
uint32_t count = luax_optu32(L, 4, limit);
|
uint32_t count = luax_optu32(L, 4, limit);
|
||||||
lovrCheck(length <= limit, "Table does not have enough data to set %d items", count);
|
lovrCheck(length <= limit, "Table does not have enough data to set %d items", count);
|
||||||
void* data = lovrMeshSetVertices(mesh, index, count);
|
void* data = lovrMeshSetVertices(mesh, index, count);
|
||||||
luax_checkdatatuples(L, 2, 1, count, format, data);
|
luax_checkbufferdata(L, 2, format, data);
|
||||||
} else {
|
} else {
|
||||||
return luax_typeerror(L, 2, "table or Blob");
|
return luax_typeerror(L, 2, "table or Blob");
|
||||||
}
|
}
|
||||||
|
|
|
@ -686,42 +686,7 @@ static int l_lovrPassSend(lua_State* L) {
|
||||||
lua_pushinteger(L, value);
|
lua_pushinteger(L, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format->length > 0) {
|
luax_checkbufferdata(L, 3, format, data);
|
||||||
luaL_checktype(L, 3, LUA_TTABLE);
|
|
||||||
if (format->fieldCount > 1) {
|
|
||||||
lua_rawgeti(L, 3, 1);
|
|
||||||
lovrCheck(lua_type(L, -1) == LUA_TTABLE, "Expected table of tables");
|
|
||||||
bool dictionary = luax_len(L, -1) == 0;
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
// Nested structs/arrays don't support the "tuple" table format
|
|
||||||
for (uint32_t i = 0; i < format->fieldCount; i++) {
|
|
||||||
if (format->fields[i].fieldCount > 0 || format->fields[i].length > 0) {
|
|
||||||
dictionary = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dictionary) {
|
|
||||||
luax_checkdatakeys(L, 3, 1, format->length, format, data);
|
|
||||||
} else {
|
|
||||||
luax_checkdatatuples(L, 3, 1, format->length, format, data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
luax_checkfieldarray(L, 3, format, data);
|
|
||||||
}
|
|
||||||
} else if (format->fieldCount > 1) {
|
|
||||||
luaL_checktype(L, 3, LUA_TTABLE);
|
|
||||||
luax_checkstruct(L, 3, format->fields, format->fieldCount, data);
|
|
||||||
} else if (lua_type(L, 3) == LUA_TNUMBER) {
|
|
||||||
luax_checkfieldn(L, 3, format->type, data);
|
|
||||||
} else if (lua_isuserdata(L, 3)) {
|
|
||||||
luax_checkfieldv(L, 3, format->type, data);
|
|
||||||
} else if (lua_istable(L, 3)) {
|
|
||||||
luax_checkfieldt(L, 3, format->type, data);
|
|
||||||
} else {
|
|
||||||
return luax_typeerror(L, 3, "number, vector, or table");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,10 +96,10 @@ static int l_lovrShaderGetBufferFormat(lua_State* L) {
|
||||||
lua_pushinteger(L, format->stride);
|
lua_pushinteger(L, format->stride);
|
||||||
lua_setfield(L, -2, "stride");
|
lua_setfield(L, -2, "stride");
|
||||||
|
|
||||||
if (format->length == ~0u) {
|
if (format->length == 0 || format->length == ~0u) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
} else {
|
} else {
|
||||||
lua_pushinteger(L, MAX(format->length, 1));
|
lua_pushinteger(L, format->length);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 2;
|
return 2;
|
||||||
|
|
|
@ -74,6 +74,7 @@ struct Buffer {
|
||||||
Sync sync;
|
Sync sync;
|
||||||
gpu_buffer* gpu;
|
gpu_buffer* gpu;
|
||||||
BufferBlock* block;
|
BufferBlock* block;
|
||||||
|
bool complexFormat;
|
||||||
BufferInfo info;
|
BufferInfo info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1851,6 +1852,11 @@ uint32_t lovrGraphicsAlignFields(DataField* parent, DataLayout layout) {
|
||||||
uint32_t extent = 0;
|
uint32_t extent = 0;
|
||||||
uint32_t align = 1;
|
uint32_t align = 1;
|
||||||
|
|
||||||
|
if (parent->fieldCount == 0) {
|
||||||
|
align = layout == LAYOUT_PACKED ? table[parent->type].scalarAlign : table[parent->type].baseAlign;
|
||||||
|
extent = table[parent->type].size;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < parent->fieldCount; i++) {
|
for (uint32_t i = 0; i < parent->fieldCount; i++) {
|
||||||
DataField* field = &parent->fields[i];
|
DataField* field = &parent->fields[i];
|
||||||
uint32_t length = MAX(field->length, 1);
|
uint32_t length = MAX(field->length, 1);
|
||||||
|
@ -1901,12 +1907,11 @@ Buffer* lovrBufferCreate(const BufferInfo* info, void** data) {
|
||||||
buffer->info.fieldCount = fieldCount;
|
buffer->info.fieldCount = fieldCount;
|
||||||
|
|
||||||
if (info->format) {
|
if (info->format) {
|
||||||
lovrCheck(info->format->length > 0, "Buffer length can not be zero");
|
|
||||||
char* names = (char*) buffer + sizeof(Buffer);
|
char* names = (char*) buffer + sizeof(Buffer);
|
||||||
DataField* format = buffer->info.format = (DataField*) (names + charCount);
|
DataField* format = buffer->info.format = (DataField*) (names + charCount);
|
||||||
memcpy(format, info->format, fieldCount * sizeof(DataField));
|
memcpy(format, info->format, fieldCount * sizeof(DataField));
|
||||||
|
|
||||||
// Copy names, hash names, fixup children pointers
|
// Copy names, hash names, fixup children pointers, set parent pointers
|
||||||
for (uint32_t i = 0; i < fieldCount; i++) {
|
for (uint32_t i = 0; i < fieldCount; i++) {
|
||||||
if (format[i].name) {
|
if (format[i].name) {
|
||||||
size_t length = strlen(format[i].name);
|
size_t length = strlen(format[i].name);
|
||||||
|
@ -1927,6 +1932,15 @@ Buffer* lovrBufferCreate(const BufferInfo* info, void** data) {
|
||||||
format->fields = format + 1;
|
format->fields = format + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set parent pointers
|
||||||
|
for (uint32_t i = 0; i < fieldCount; i++) {
|
||||||
|
if (format[i].fields) {
|
||||||
|
for (uint32_t j = 0; j < format[i].fieldCount; j++) {
|
||||||
|
format[i].fields[j].parent = &format[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Size is optional, and can be computed from format
|
// Size is optional, and can be computed from format
|
||||||
if (buffer->info.size == 0) {
|
if (buffer->info.size == 0) {
|
||||||
buffer->info.size = format->stride * MAX(format->length, 1);
|
buffer->info.size = format->stride * MAX(format->length, 1);
|
||||||
|
@ -1935,7 +1949,7 @@ Buffer* lovrBufferCreate(const BufferInfo* info, void** data) {
|
||||||
// Formats with array/struct fields have extra restrictions, cache it
|
// Formats with array/struct fields have extra restrictions, cache it
|
||||||
for (uint32_t i = 0; i < format->fieldCount; i++) {
|
for (uint32_t i = 0; i < format->fieldCount; i++) {
|
||||||
if (format->fields[i].fieldCount > 0 || format->fields[i].length > 0) {
|
if (format->fields[i].fieldCount > 0 || format->fields[i].length > 0) {
|
||||||
buffer->info.complexFormat = true;
|
buffer->complexFormat = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3035,19 +3049,9 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
||||||
if (buffer && resource->bufferFields) {
|
if (buffer && resource->bufferFields) {
|
||||||
spv_field* field = &resource->bufferFields[0];
|
spv_field* field = &resource->bufferFields[0];
|
||||||
|
|
||||||
// The following conversions take place, for convenience and to better match Buffer formats:
|
// Struct containing single item gets unwrapped
|
||||||
// - Struct containing either single struct or single array of structs gets unwrapped
|
if (field->fieldCount == 1) {
|
||||||
// - Struct containing single array of non-structs gets converted to array of single-field structs
|
|
||||||
if (field->fieldCount == 1 && field->totalFieldCount > 1) {
|
|
||||||
field = &field->fields[0];
|
field = &field->fields[0];
|
||||||
} else if (field->totalFieldCount == 1 && field->fields[0].arrayLength > 0) {
|
|
||||||
spv_field* child = &field->fields[0];
|
|
||||||
field->arrayLength = child->arrayLength;
|
|
||||||
field->arrayStride = child->arrayStride;
|
|
||||||
field->elementSize = child->elementSize;
|
|
||||||
field->type = child->type;
|
|
||||||
child->arrayLength = 0;
|
|
||||||
child->arrayStride = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shader->resources[index].fieldCount = field->totalFieldCount + 1;
|
shader->resources[index].fieldCount = field->totalFieldCount + 1;
|
||||||
|
@ -3934,7 +3938,7 @@ Mesh* lovrMeshCreate(const MeshInfo* info, void** vertices) {
|
||||||
|
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
lovrCheck(buffer->info.format, "Mesh vertex buffer must have format information");
|
lovrCheck(buffer->info.format, "Mesh vertex buffer must have format information");
|
||||||
lovrCheck(!buffer->info.complexFormat, "Mesh vertex buffer must use a format without nested types or arrays");
|
lovrCheck(!buffer->complexFormat, "Mesh vertex buffer must use a format without nested types or arrays");
|
||||||
lovrCheck(info->storage == MESH_GPU, "Mesh storage must be 'gpu' when created from a Buffer");
|
lovrCheck(info->storage == MESH_GPU, "Mesh storage must be 'gpu' when created from a Buffer");
|
||||||
lovrRetain(buffer);
|
lovrRetain(buffer);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3949,8 +3953,8 @@ Mesh* lovrMeshCreate(const MeshInfo* info, void** vertices) {
|
||||||
lovrCheck(format->stride <= state.limits.vertexBufferStride, "Mesh vertex buffer stride exceeds the vertexBufferStride limit of this GPU");
|
lovrCheck(format->stride <= state.limits.vertexBufferStride, "Mesh vertex buffer stride exceeds the vertexBufferStride limit of this GPU");
|
||||||
lovrCheck(format->fieldCount <= state.limits.vertexAttributes, "Mesh attribute count exceeds the vertexAttributes limit of this GPU");
|
lovrCheck(format->fieldCount <= state.limits.vertexAttributes, "Mesh attribute count exceeds the vertexAttributes limit of this GPU");
|
||||||
|
|
||||||
for (uint32_t i = 0; i < format->fieldCount; i++) {
|
for (uint32_t i = 0; i < MAX(format->fieldCount, 1); i++) {
|
||||||
const DataField* attribute = &format->fields[i];
|
const DataField* attribute = format->fieldCount > 0 ? &format->fields[i] : format;
|
||||||
lovrCheck(attribute->offset < 256, "Max Mesh attribute offset is 255"); // Limited by u8 gpu_attribute offset
|
lovrCheck(attribute->offset < 256, "Max Mesh attribute offset is 255"); // Limited by u8 gpu_attribute offset
|
||||||
lovrCheck(attribute->type < TYPE_MAT2 || attribute->type > TYPE_MAT4, "Currently, Mesh attributes can not use matrix types");
|
lovrCheck(attribute->type < TYPE_MAT2 || attribute->type > TYPE_MAT4, "Currently, Mesh attributes can not use matrix types");
|
||||||
lovrCheck(attribute->type < TYPE_INDEX16 || attribute->type > TYPE_INDEX32, "Mesh attributes can not use index types");
|
lovrCheck(attribute->type < TYPE_INDEX16 || attribute->type > TYPE_INDEX32, "Mesh attributes can not use index types");
|
||||||
|
@ -4011,12 +4015,14 @@ void lovrMeshSetIndexBuffer(Mesh* mesh, Buffer* buffer) {
|
||||||
|
|
||||||
DataField* format = buffer->info.format;
|
DataField* format = buffer->info.format;
|
||||||
lovrCheck(format, "Mesh index buffer must have been created with a format");
|
lovrCheck(format, "Mesh index buffer must have been created with a format");
|
||||||
DataType type = format[1].type;
|
lovrCheck(format->length > 0, "Mesh index buffer length can not be zero");
|
||||||
if (format->fieldCount > 1 || (type != TYPE_U16 && type != TYPE_U32 && type != TYPE_INDEX16 && type != TYPE_INDEX32)) {
|
|
||||||
|
DataType type = format->type;
|
||||||
|
if (format->fieldCount > 0 || (type != TYPE_U16 && type != TYPE_U32 && type != TYPE_INDEX16 && type != TYPE_INDEX32)) {
|
||||||
lovrThrow("Mesh index buffer must use the u16, u32, index16, or index32 type");
|
lovrThrow("Mesh index buffer must use the u16, u32, index16, or index32 type");
|
||||||
} else {
|
} else {
|
||||||
uint32_t stride = (type == TYPE_U16 || type == TYPE_INDEX16) ? 2 : 4;
|
uint32_t stride = (type == TYPE_U16 || type == TYPE_INDEX16) ? 2 : 4;
|
||||||
lovrCheck(format->stride == stride && format[1].offset == 0, "Mesh index buffer must be tightly packed");
|
lovrCheck(format->stride == stride && format->offset == 0, "Mesh index buffer must be tightly packed");
|
||||||
}
|
}
|
||||||
|
|
||||||
lovrRelease(mesh->indexBuffer, lovrBufferDestroy);
|
lovrRelease(mesh->indexBuffer, lovrBufferDestroy);
|
||||||
|
@ -4057,7 +4063,7 @@ void* lovrMeshGetIndices(Mesh* mesh, uint32_t* count, DataType* type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
*count = mesh->indexCount;
|
*count = mesh->indexCount;
|
||||||
*type = mesh->indexBuffer->info.format[1].type;
|
*type = mesh->indexBuffer->info.format->type;
|
||||||
|
|
||||||
if (mesh->storage == MESH_CPU) {
|
if (mesh->storage == MESH_CPU) {
|
||||||
return mesh->indices;
|
return mesh->indices;
|
||||||
|
@ -4071,14 +4077,11 @@ void* lovrMeshSetIndices(Mesh* mesh, uint32_t count, DataType type) {
|
||||||
mesh->indexCount = count;
|
mesh->indexCount = count;
|
||||||
mesh->dirtyIndices = true;
|
mesh->dirtyIndices = true;
|
||||||
|
|
||||||
if (!mesh->indexBuffer || count > format->length || type != format[1].type) {
|
if (!mesh->indexBuffer || count > format->length || type != format->type) {
|
||||||
lovrRelease(mesh->indexBuffer, lovrBufferDestroy);
|
lovrRelease(mesh->indexBuffer, lovrBufferDestroy);
|
||||||
uint32_t stride = (type == TYPE_U16 || type == TYPE_INDEX16) ? 2 : 4;
|
uint32_t stride = (type == TYPE_U16 || type == TYPE_INDEX16) ? 2 : 4;
|
||||||
DataField format[2] = {
|
DataField format = { .length = count, .stride = stride, .type = type };
|
||||||
{ .length = count, .stride = stride, .fieldCount = 1 },
|
BufferInfo info = { .format = &format };
|
||||||
{ .type = type }
|
|
||||||
};
|
|
||||||
BufferInfo info = { .format = format };
|
|
||||||
if (mesh->storage == MESH_CPU) {
|
if (mesh->storage == MESH_CPU) {
|
||||||
mesh->indexBuffer = lovrBufferCreate(&info, NULL);
|
mesh->indexBuffer = lovrBufferCreate(&info, NULL);
|
||||||
mesh->indices = realloc(mesh->indices, count * stride);
|
mesh->indices = realloc(mesh->indices, count * stride);
|
||||||
|
@ -4100,8 +4103,8 @@ static float* lovrMeshGetPositions(Mesh* mesh) {
|
||||||
if (mesh->storage == MESH_GPU) return NULL;
|
if (mesh->storage == MESH_GPU) return NULL;
|
||||||
const DataField* format = lovrMeshGetVertexFormat(mesh);
|
const DataField* format = lovrMeshGetVertexFormat(mesh);
|
||||||
uint32_t positionHash = (uint32_t) hash64("VertexPosition", strlen("VertexPosition"));
|
uint32_t positionHash = (uint32_t) hash64("VertexPosition", strlen("VertexPosition"));
|
||||||
for (uint32_t i = 0; i < format->fieldCount; i++) {
|
for (uint32_t i = 0; i < MAX(format->fieldCount, 1); i++) {
|
||||||
const DataField* attribute = &format->fields[i];
|
const DataField* attribute = format->fieldCount > 0 ? &format->fields[i] : format;
|
||||||
if (attribute->type != TYPE_F32x3) continue;
|
if (attribute->type != TYPE_F32x3) continue;
|
||||||
if ((attribute->hash == LOCATION_POSITION || attribute->hash == positionHash)) {
|
if ((attribute->hash == LOCATION_POSITION || attribute->hash == positionHash)) {
|
||||||
return (float*) ((char*) mesh->vertices + attribute->offset);
|
return (float*) ((char*) mesh->vertices + attribute->offset);
|
||||||
|
@ -4128,7 +4131,7 @@ void lovrMeshGetTriangles(Mesh* mesh, float** vertices, uint32_t** indices, uint
|
||||||
if (mesh->indexCount > 0) {
|
if (mesh->indexCount > 0) {
|
||||||
*indexCount = mesh->indexCount;
|
*indexCount = mesh->indexCount;
|
||||||
*indices = lovrMalloc(*indexCount * sizeof(uint32_t));
|
*indices = lovrMalloc(*indexCount * sizeof(uint32_t));
|
||||||
if (mesh->indexBuffer->info.format[1].type == TYPE_U16 || mesh->indexBuffer->info.format[1].type == TYPE_INDEX16) {
|
if (mesh->indexBuffer->info.format->type == TYPE_U16 || mesh->indexBuffer->info.format->type == TYPE_INDEX16) {
|
||||||
for (uint32_t i = 0; i < mesh->indexCount; i++) {
|
for (uint32_t i = 0; i < mesh->indexCount; i++) {
|
||||||
*indices[i] = (uint32_t) ((uint16_t*) mesh->indices)[i];
|
*indices[i] = (uint32_t) ((uint16_t*) mesh->indices)[i];
|
||||||
}
|
}
|
||||||
|
@ -4378,13 +4381,13 @@ Model* lovrModelCreate(const ModelInfo* info) {
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataType indexType = data->indexType == U32 ? TYPE_INDEX32 : TYPE_INDEX16;
|
||||||
uint32_t indexSize = data->indexType == U32 ? 4 : 2;
|
uint32_t indexSize = data->indexType == U32 ? 4 : 2;
|
||||||
|
|
||||||
if (data->indexCount > 0) {
|
if (data->indexCount > 0) {
|
||||||
model->indexBuffer = lovrBufferCreate(&(BufferInfo) {
|
model->indexBuffer = lovrBufferCreate(&(BufferInfo) {
|
||||||
.format = (DataField[]) {
|
.format = (DataField[]) {
|
||||||
{ .length = data->indexCount, .stride = indexSize, .fieldCount = 1 },
|
{ .length = data->indexCount, .stride = indexSize, .type = indexType }
|
||||||
{ .type = data->indexType == U32 ? TYPE_INDEX32 : TYPE_INDEX16 }
|
|
||||||
}
|
}
|
||||||
}, (void**) &indexData);
|
}, (void**) &indexData);
|
||||||
}
|
}
|
||||||
|
@ -5916,8 +5919,8 @@ static void lovrPassResolvePipeline(Pass* pass, DrawInfo* info, Draw* draw, Draw
|
||||||
ShaderAttribute* attribute = &shader->attributes[i];
|
ShaderAttribute* attribute = &shader->attributes[i];
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
for (uint32_t j = 0; j < format->fieldCount; j++) {
|
for (uint32_t j = 0; j < MAX(format->fieldCount, 1); j++) {
|
||||||
DataField* field = &format->fields[j];
|
const DataField* field = format->fieldCount > 0 ? &format->fields[j] : format;
|
||||||
if (field->hash == attribute->hash || field->hash == attribute->location) {
|
if (field->hash == attribute->hash || field->hash == attribute->location) {
|
||||||
lovrCheck(field->type < TYPE_MAT2, "Currently vertex attributes can not use matrix or index types");
|
lovrCheck(field->type < TYPE_MAT2, "Currently vertex attributes can not use matrix or index types");
|
||||||
pipeline->info.vertex.attributes[i] = (gpu_attribute) {
|
pipeline->info.vertex.attributes[i] = (gpu_attribute) {
|
||||||
|
@ -7106,7 +7109,7 @@ void lovrPassDrawTexture(Pass* pass, Texture* texture, float* transform) {
|
||||||
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances, uint32_t baseVertex) {
|
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances, uint32_t baseVertex) {
|
||||||
lovrCheck(!indices || indices->info.format, "Buffer must have been created with a format to use it as a%s buffer", "n index");
|
lovrCheck(!indices || indices->info.format, "Buffer must have been created with a format to use it as a%s buffer", "n index");
|
||||||
lovrCheck(!vertices || vertices->info.format, "Buffer must have been created with a format to use it as a%s buffer", " vertex");
|
lovrCheck(!vertices || vertices->info.format, "Buffer must have been created with a format to use it as a%s buffer", " vertex");
|
||||||
lovrCheck(!vertices || !vertices->info.complexFormat, "Vertex buffers must use a simple format without nested types or arrays");
|
lovrCheck(!vertices || !vertices->complexFormat, "Vertex buffers must use a simple format without nested types or arrays");
|
||||||
|
|
||||||
if (count == ~0u) {
|
if (count == ~0u) {
|
||||||
if (indices || vertices) {
|
if (indices || vertices) {
|
||||||
|
|
|
@ -158,6 +158,7 @@ typedef struct DataField {
|
||||||
uint32_t stride;
|
uint32_t stride;
|
||||||
uint32_t fieldCount;
|
uint32_t fieldCount;
|
||||||
struct DataField* fields;
|
struct DataField* fields;
|
||||||
|
struct DataField* parent;
|
||||||
} DataField;
|
} DataField;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -172,7 +173,6 @@ typedef struct {
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t fieldCount;
|
uint32_t fieldCount;
|
||||||
DataField* format;
|
DataField* format;
|
||||||
bool complexFormat;
|
|
||||||
const char* label;
|
const char* label;
|
||||||
uintptr_t handle;
|
uintptr_t handle;
|
||||||
} BufferInfo;
|
} BufferInfo;
|
||||||
|
|
Loading…
Reference in New Issue