ShaderBlock:send; Helper function for reading uniforms;

This commit is contained in:
bjorn 2018-07-31 18:23:04 -07:00
parent 18ee139341
commit 2b093b5828
6 changed files with 219 additions and 149 deletions

View File

@ -56,6 +56,7 @@ extern const luaL_Reg lovrModelData[];
extern const luaL_Reg lovrRandomGenerator[];
extern const luaL_Reg lovrRasterizer[];
extern const luaL_Reg lovrShader[];
extern const luaL_Reg lovrShaderBlock[];
extern const luaL_Reg lovrShape[];
extern const luaL_Reg lovrSliderJoint[];
extern const luaL_Reg lovrSoundData[];
@ -114,3 +115,4 @@ Blob* luax_readblob(lua_State* L, int index, const char* debug);
Seed luax_checkrandomseed(lua_State* L, int index);
void luax_checkvariant(lua_State* L, int index, Variant* variant);
int luax_pushvariant(lua_State* L, Variant* variant);
int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug);

View File

@ -241,6 +241,7 @@ int l_lovrGraphicsInit(lua_State* L) {
luax_registertype(L, "Mesh", lovrMesh);
luax_registertype(L, "Model", lovrModel);
luax_registertype(L, "Shader", lovrShader);
luax_registertype(L, "ShaderBlock", lovrShaderBlock);
luax_registertype(L, "Texture", lovrTexture);
luax_extendtype(L, "Texture", "Canvas", lovrTexture, lovrCanvas);
lovrGraphicsInit();
@ -885,9 +886,9 @@ int l_lovrGraphicsNewShaderBlock(lua_State* L) {
lua_getfield(L, -1, "type");
const char* type = lua_tolstring(L, -1, &length);
uniform.components = 1;
if (strcmp(type, "float")) {
if (!strcmp(type, "float")) {
uniform.type = UNIFORM_FLOAT;
} else if (strcmp(type, "int")) {
} else if (!strcmp(type, "int")) {
uniform.type = UNIFORM_INT;
} else {
uniform.components = type[length - 1] - '0';

View File

@ -9,6 +9,132 @@ struct TempData {
static struct TempData tempData;
int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug) {
Blob* blob = luax_totype(L, index, Blob);
int components = uniform->components;
int count = uniform->count;
if (uniform->type == UNIFORM_MATRIX) {
components *= components;
}
// TODO samplers/textures
if (blob) {
size_t elements = count * components;
const char* s = elements == 1 ? "" : "s";
size_t capacity;
switch (uniform->type) {
case UNIFORM_FLOAT:
case UNIFORM_MATRIX:
capacity = blob->size / sizeof(float);
lovrAssert(capacity >= elements, "Blob can only hold %d float%s, at least %d needed", capacity, s, elements);
memcpy(dest, blob->data, elements * sizeof(float));
break;
case UNIFORM_INT:
capacity = blob->size / sizeof(int);
lovrAssert(capacity >= elements, "Blob can only hold %d int%s, at least %d needed", capacity, s, elements);
memcpy(dest, blob->data, elements * sizeof(int));
break;
default: lovrThrow(""); // TODO
}
return 0;
}
if (components == 1) {
bool isTable = lua_istable(L, index);
if (isTable) {
int length = lua_objlen(L, index);
lovrAssert(length == count, ""); // TODO
for (int i = 0; i < length; i++) {
lua_rawgeti(L, index, i + 1);
switch (uniform->type) {
case UNIFORM_FLOAT: *((float*) dest + i) = luaL_checknumber(L, -1); break;
case UNIFORM_INT: *((int*) dest + i) = luaL_checkinteger(L, -1); break;
default: lovrThrow(""); // TODO
}
lua_pop(L, 1);
}
} else {
for (int i = 0; i < count; i++) {
switch (uniform->type) {
case UNIFORM_FLOAT: *((float*) dest + i) = luaL_checknumber(L, index + i); break;
case UNIFORM_INT: *((int*) dest + i) = luaL_checkinteger(L, index + i); break;
default: lovrThrow(""); // TODO
}
}
}
} else {
luaL_checktype(L, index, LUA_TTABLE);
lua_rawgeti(L, index, 1);
bool wrappedTable = lua_istable(L, -1) || luax_totype(L, -1, Transform);
lua_pop(L, 1);
if (wrappedTable) {
int length = lua_objlen(L, index);
lovrAssert(length == count, ""); // TODO
for (int i = 0; i < length; i++) {
lua_rawgeti(L, index, i + 1);
if (uniform->type == UNIFORM_MATRIX && lua_isuserdata(L, -1)) {
Transform* transform = luax_checktype(L, -1, Transform);
memcpy((float*) dest + i * components, transform->matrix, 16 * sizeof(float));
continue;
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
switch (uniform->type) {
case UNIFORM_FLOAT:
case UNIFORM_MATRIX:
*((float*) dest + i * components + j) = luaL_checknumber(L, -1);
case UNIFORM_INT:
*((int*) dest + i * components + j) = luaL_checkinteger(L, -1);
break;
default:
lovrThrow(""); // TODO
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
} else {
for (int i = 0; i < count; i++) {
if (uniform->type == UNIFORM_MATRIX && lua_isuserdata(L, index + i)) {
Transform* transform = luax_checktype(L, index + i, Transform);
memcpy((float*) dest + i * components, transform->matrix, 16 * sizeof(float));
continue;
}
luaL_checktype(L, index + i, LUA_TTABLE);
for (int j = 0; j < components; j++) {
lua_rawgeti(L, index + i, j + 1);
switch (uniform->type) {
case UNIFORM_FLOAT:
case UNIFORM_MATRIX:
*((float*) dest + i * components + j) = luaL_checknumber(L, -1);
break;
case UNIFORM_INT:
*((float*) dest + i * components + j) = luaL_checknumber(L, -1);
break;
default: lovrThrow(""); // TODO
}
}
}
}
}
return 0;
}
int l_lovrShaderHasUniform(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
@ -19,146 +145,15 @@ int l_lovrShaderHasUniform(lua_State* L) {
int l_lovrShaderSend(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
lua_settop(L, 3);
const Uniform* uniform = lovrShaderGetUniform(shader, name);
lovrAssert(uniform, "Unknown shader variable '%s'", name);
int count;
int components;
int size;
UniformType type;
bool present = lovrShaderGetUniform(shader, name, &count, &components, &size, &type);
if (!present) {
return luaL_error(L, "Unknown shader variable '%s'", name);
}
if (tempData.size < size) {
tempData.size = size;
if (tempData.size < uniform->size) {
tempData.size = uniform->size;
tempData.data = realloc(tempData.data, tempData.size);
}
int* ints = (int*) tempData.data;
float* floats = (float*) tempData.data;
Texture** textures = (Texture**) tempData.data;
int n = 1;
if (components > 1 && lua_istable(L, 3)) {
lua_rawgeti(L, 3, 1);
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1);
} else {
lua_pop(L, 1);
}
n = lua_objlen(L, -1);
if (n != count) {
return luaL_error(L, "Expected %d element%s for array '%s', got %d", count, count == 1 ? "" : "s", name, n);
}
}
Blob* blob = luax_totype(L, 3, Blob);
switch (type) {
case UNIFORM_FLOAT:
if (blob) {
n = count;
floats = (float*) blob->data;
size_t count = n * components;
size_t capacity = blob->size / sizeof(float);
const char* s = capacity == 1 ? "" : "s";
lovrAssert(capacity >= count, "Blob can only hold %d float%s, at least %d needed", capacity, s, count);
} else if (components == 1) {
floats[0] = luaL_checknumber(L, 3);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) {
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, name, lua_objlen(L, -1));
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
floats[i * components + j] = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetFloat(shader, name, floats, n * components);
break;
case UNIFORM_INT:
if (blob) {
n = count;
ints = (int*) blob->data;
size_t count = n * components;
size_t capacity = blob->size / sizeof(int);
const char* s = capacity == 1 ? "" : "s";
lovrAssert(capacity >= count, "Blob can only hold %d int%s, at least %d needed", capacity, s, count);
} else if (components == 1) {
ints[0] = luaL_checkinteger(L, 3);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) {
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, name, lua_objlen(L, -1));
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
ints[i * components + j] = luaL_checkinteger(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetInt(shader, name, ints, n * components);
break;
case UNIFORM_MATRIX:
if (blob) {
n = count;
floats = (float*) blob->data;
size_t count = n * components * components;
size_t capacity = blob->size / sizeof(float);
const char* s = capacity == 1 ? "x" : "ces";
lovrAssert(capacity >= count, "Blob can only hold %d matri%s, at least %d needed", capacity, s, count);
} else if (components == 4 && lua_isuserdata(L, 3)) {
Transform* transform = luax_checktype(L, 3, Transform);
memcpy(floats, transform->matrix, 16 * sizeof(float));
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
for (int j = 0; j < components * components; j++) {
lua_rawgeti(L, -1, j + 1);
floats[i * components * components + j] = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetMatrix(shader, name, floats, n * components * components);
break;
case UNIFORM_SAMPLER:
if (components == 1) {
textures[0] = luax_checktype(L, 3, Texture);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
textures[i] = luax_checktype(L, -1, Texture);
lua_pop(L, 1);
}
}
lovrShaderSetTexture(shader, name, textures, n);
break;
default: break;
}
luax_checkuniform(L, 3, uniform, tempData.data, name);
return 0;
}

View File

@ -0,0 +1,27 @@
#include "api.h"
#include "graphics/shader.h"
#include "math/transform.h"
int l_lovrShaderBlockGetSize(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
lua_pushinteger(L, lovrShaderBlockGetSize(block));
return 1;
}
int l_lovrShaderBlockSend(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
const char* name = luaL_checkstring(L, 2);
const Uniform* uniform = lovrShaderBlockGetUniform(block, name);
lovrAssert(uniform, "Unknown uniform for ShaderBlock '%s'", name);
uint8_t* data = ((uint8_t*) lovrShaderBlockMap(block)) + uniform->offset;
luax_checkuniform(L, 3, uniform, data, name);
return 0;
}
const luaL_Reg lovrShaderBlock[] = {
{ "getSize", l_lovrShaderBlockGetSize },
{ "send", l_lovrShaderBlockSend },
{ NULL, NULL }
};

View File

@ -67,7 +67,11 @@ static struct {
struct ShaderBlock {
Ref ref;
vec_uniform_t uniforms;
map_int_t uniformMap;
uint32_t buffer;
size_t size;
void* data;
bool mapped;
};
typedef struct {
@ -1442,7 +1446,12 @@ void lovrShaderBind(Shader* shader) {
UniformBlock* block;
vec_foreach_ptr(&shader->blocks, block, i) {
lovrGpuBindUniformBuffer(block->source ? block->source->buffer : 0, block->binding);
if (block->source) {
lovrShaderBlockUnmap(block->source);
lovrGpuBindUniformBuffer(block->source->buffer, block->binding);
} else {
lovrGpuBindUniformBuffer(0, block->binding);
}
}
}
@ -1455,19 +1464,13 @@ bool lovrShaderHasUniform(Shader* shader, const char* name) {
return map_get(&shader->uniformMap, name) != NULL;
}
bool lovrShaderGetUniform(Shader* shader, const char* name, int* count, int* components, int* size, UniformType* type) {
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
int* index = map_get(&shader->uniformMap, name);
if (!index) {
return false;
}
Uniform* uniform = &shader->uniforms.data[*index];
*count = uniform->count;
*components = uniform->components;
*size = uniform->size;
*type = uniform->type;
return true;
return &shader->uniforms.data[*index];
}
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int count, int size, const char* debug) {
@ -1529,32 +1532,70 @@ ShaderBlock* lovrShaderBlockCreate(vec_uniform_t* uniforms) {
vec_init(&block->uniforms);
vec_extend(&block->uniforms, uniforms);
map_init(&block->uniformMap);
int i;
Uniform* uniform;
size_t size = 0;
vec_foreach_ptr(&block->uniforms, uniform, i) {
// Calculate size and offset
uniform->offset = size;
if (uniform->type == UNIFORM_MATRIX) {
size += uniform->components * uniform->components * 4;
} else {
size += uniform->count == 1 ? 4 : (uniform->count * 16);
}
// Write index to uniform lookup
map_set(&block->uniformMap, uniform->name, i);
}
glGenBuffers(1, &block->buffer);
lovrGpuBindUniformBuffer(block->buffer, 0);
glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_STATIC_DRAW);
block->size = size;
block->data = calloc(1, size);
return block;
}
void lovrShaderBlockDestroy(void* ref) {
UniformBlock* block = ref;
ShaderBlock* block = ref;
vec_deinit(&block->uniforms);
map_deinit(&block->uniformMap);
free(block->data);
free(block);
}
size_t lovrShaderBlockGetSize(ShaderBlock* block) {
return block->size;
}
const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) {
int* index = map_get(&block->uniformMap, name);
if (!index) return NULL;
return &block->uniforms.data[*index];
}
void* lovrShaderBlockMap(ShaderBlock* block) {
block->mapped = true;
return block->data;
}
void lovrShaderBlockUnmap(ShaderBlock* block) {
if (!block->mapped) {
return;
}
lovrGpuBindUniformBuffer(block->buffer, 0);
glBufferData(GL_UNIFORM_BUFFER, block->size, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_UNIFORM_BUFFER, 0, block->size, block->data);
block->mapped = false;
}
// Mesh
Mesh* lovrMeshCreate(uint32_t count, VertexFormat format, MeshDrawMode drawMode, MeshUsage usage) {

View File

@ -54,7 +54,7 @@ void lovrShaderDestroy(void* ref);
void lovrShaderBind(Shader* shader);
int lovrShaderGetAttributeId(Shader* shader, const char* name);
bool lovrShaderHasUniform(Shader* shader, const char* name);
bool lovrShaderGetUniform(Shader* shader, const char* name, int* count, int* components, int* size, UniformType* type);
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name);
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count);
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count);
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count);
@ -64,3 +64,7 @@ void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* block);
ShaderBlock* lovrShaderBlockCreate(vec_uniform_t* uniforms);
void lovrShaderBlockDestroy(void* ref);
size_t lovrShaderBlockGetSize(ShaderBlock* block);
const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name);
void* lovrShaderBlockMap(ShaderBlock* block);
void lovrShaderBlockUnmap(ShaderBlock* block);