Add ShaderFlags;

newShader and newComputeShader accept an optional options table
that can contain a table of flags.  Flags are turned into #defines
in the shader source and can be used as specialization constants in
the future.

Currently they can only be bools and ints.  This might change to add
float and/or remove bools.
This commit is contained in:
bjorn 2019-05-14 03:48:01 -07:00
parent 6134abe866
commit 3267bad4fa
4 changed files with 130 additions and 14 deletions

View File

@ -1322,12 +1322,60 @@ static void luax_readshadersource(lua_State* L, int index) {
free(contents);
}
#define MAX_SHADER_FLAGS 32
static void luax_parseshaderflags(lua_State* L, int index, ShaderFlag flags[MAX_SHADER_FLAGS], uint32_t* count) {
if (lua_isnil(L, -1)) {
*count = 0;
return;
}
lovrAssert(lua_istable(L, -1), "Shader flags must be a table");
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
ShaderFlag* flag = &flags[++*count];
lovrAssert(*count < MAX_SHADER_FLAGS, "Too many shader flags (max is %d)", MAX_SHADER_FLAGS);
if (lua_type(L, -2) == LUA_TSTRING) {
flag->name = lua_tostring(L, -2);
} else if (lua_isnumber(L, -2)) {
flag->index = lua_tointeger(L, -2);
} else {
lovrThrow("Shader flag names must be strings or numbers");
}
switch (lua_type(L, -1)) {
case LUA_TBOOLEAN:
flag->type = FLAG_BOOL;
flag->value.b32 = lua_toboolean(L, -1);
break;
case LUA_TNUMBER:
flag->type = FLAG_INT;
flag->value.i32 = lua_tointeger(L, -1);
break;
default:
lovrThrow("Shader flag values must be booleans or integers");
}
lua_pop(L, 1);
}
}
static int l_lovrGraphicsNewShader(lua_State* L) {
luax_readshadersource(L, 1);
luax_readshadersource(L, 2);
const char* vertexSource = lua_tostring(L, 1);
const char* fragmentSource = lua_tostring(L, 2);
Shader* shader = lovrShaderCreateGraphics(vertexSource, fragmentSource);
ShaderFlag flags[MAX_SHADER_FLAGS];
uint32_t flagCount = 0;
if (lua_istable(L, 3)) {
lua_getfield(L, 3, "flags");
luax_parseshaderflags(L, -1, flags, &flagCount);
lua_pop(L, 1);
}
Shader* shader = lovrShaderCreateGraphics(vertexSource, fragmentSource, flags, flagCount);
luax_pushobject(L, shader);
lovrRelease(Shader, shader);
return 1;
@ -1336,7 +1384,16 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
static int l_lovrGraphicsNewComputeShader(lua_State* L) {
luax_readshadersource(L, 1);
const char* source = lua_tostring(L, 1);
Shader* shader = lovrShaderCreateCompute(source);
ShaderFlag flags[MAX_SHADER_FLAGS];
uint32_t flagCount = 0;
if (lua_istable(L, 2)) {
lua_getfield(L, 2, "flags");
luax_parseshaderflags(L, -1, flags, &flagCount);
lua_pop(L, 1);
}
Shader* shader = lovrShaderCreateCompute(source, flags, flagCount);
luax_pushobject(L, shader);
lovrRelease(Shader, shader);
return 1;

View File

@ -1861,7 +1861,45 @@ static void lovrShaderSetupUniforms(Shader* shader) {
}
}
Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const char* fragmentSource) {
static char* lovrShaderGetFlagCode(ShaderFlag* flags, uint32_t flagCount) {
if (flagCount == 0) {
return NULL;
}
// Figure out how much space we need
size_t length = 0;
for (uint32_t i = 0; i < flagCount; i++) {
if (flags[i].name) {
length += strlen("#define FLAG_");
length += strlen(flags[i].name) + 1;
if (flags[i].type == FLAG_BOOL) {
length += flags[i].value.b32 ? strlen("true") : strlen("false");
} else {
length += snprintf(NULL, 0, "%d", flags[i].value.i32);
}
length += strlen("\n");
}
}
// Generate the string
char* code = malloc(length + 1);
code[length] = '\0';
char* s = code;
for (uint32_t i = 0; i < flagCount; i++) {
if (flags[i].name) {
s += sprintf(s, "#define FLAG_%s ", flags[i].name);
if (flags[i].type == FLAG_BOOL) {
s += sprintf(s, "%s\n", flags[i].value.b32 ? "true" : "false");
} else {
s += sprintf(s, "%d\n", flags[i].value.i32);
}
}
}
return code;
}
Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const char* fragmentSource, ShaderFlag* flags, uint32_t flagCount) {
#if defined(LOVR_WEBGL) || defined(LOVR_GLES)
const char* vertexHeader = "#version 300 es\nprecision mediump float;\nprecision mediump int;\n";
const char* fragmentHeader = vertexHeader;
@ -1878,16 +1916,20 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c
"#extension GL_ARB_fragment_layer_viewport : require\n" "#define SINGLEPASS 1\n" :
"#define SINGLEPASS 0\n";
char* flagSource = lovrShaderGetFlagCode(flags, flagCount);
// Vertex
vertexSource = vertexSource == NULL ? lovrDefaultVertexShader : vertexSource;
const char* vertexSources[] = { vertexHeader, vertexSinglepass, lovrShaderVertexPrefix, vertexSource, lovrShaderVertexSuffix };
const char* vertexSources[] = { vertexHeader, vertexSinglepass, flagSource ? flagSource : "", lovrShaderVertexPrefix, vertexSource, lovrShaderVertexSuffix };
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSources, sizeof(vertexSources) / sizeof(vertexSources[0]));
// Fragment
fragmentSource = fragmentSource == NULL ? lovrDefaultFragmentShader : fragmentSource;
const char* fragmentSources[] = { fragmentHeader, fragmentSinglepass, lovrShaderFragmentPrefix, fragmentSource, lovrShaderFragmentSuffix };
const char* fragmentSources[] = { fragmentHeader, fragmentSinglepass, flagSource ? flagSource : "", lovrShaderFragmentPrefix, fragmentSource, lovrShaderFragmentSuffix };
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSources, sizeof(fragmentSources) / sizeof(fragmentSources[0]));
free(flagSource);
// Link
uint32_t program = glCreateProgram();
glAttachShader(program, vertexShader);
@ -1932,13 +1974,15 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c
return shader;
}
Shader* lovrShaderInitCompute(Shader* shader, const char* source) {
Shader* lovrShaderInitCompute(Shader* shader, const char* source, ShaderFlag* flags, uint32_t flagCount) {
#ifdef LOVR_WEBGL
lovrThrow("Compute shaders are not supported on this system");
#else
lovrAssert(GLAD_GL_ARB_compute_shader, "Compute shaders are not supported on this system");
const char* sources[] = { lovrShaderComputePrefix, source, lovrShaderComputeSuffix };
char* flagSource = lovrShaderGetFlagCode(flags, flagCount);
const char* sources[] = { lovrShaderComputePrefix, flagSource ? flagSource : "", source, lovrShaderComputeSuffix };
GLuint computeShader = compileShader(GL_COMPUTE_SHADER, sources, sizeof(sources) / sizeof(sources[0]));
free(flagSource);
GLuint program = glCreateProgram();
glAttachShader(program, computeShader);
linkProgram(program);

View File

@ -59,11 +59,11 @@ static const char* getUniformTypeName(const Uniform* uniform) {
Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type) {
switch (type) {
case SHADER_DEFAULT: return lovrShaderInitGraphics(shader, NULL, NULL);
case SHADER_CUBE: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrCubeFragmentShader);
case SHADER_PANO: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrPanoFragmentShader);
case SHADER_FONT: return lovrShaderInitGraphics(shader, NULL, lovrFontFragmentShader);
case SHADER_FILL: return lovrShaderInitGraphics(shader, lovrFillVertexShader, NULL);
case SHADER_DEFAULT: return lovrShaderInitGraphics(shader, NULL, NULL, NULL, 0);
case SHADER_CUBE: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrCubeFragmentShader, NULL, 0);
case SHADER_PANO: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrPanoFragmentShader, NULL, 0);
case SHADER_FONT: return lovrShaderInitGraphics(shader, NULL, lovrFontFragmentShader, NULL, 0);
case SHADER_FILL: return lovrShaderInitGraphics(shader, lovrFillVertexShader, NULL, NULL, 0);
default: lovrThrow("Unknown default shader type"); return NULL;
}
}

View File

@ -36,6 +36,21 @@ typedef enum {
SHADER_COMPUTE
} ShaderType;
typedef enum {
FLAG_BOOL,
FLAG_INT
} ShaderFlagType;
typedef struct {
uint32_t index;
const char* name;
ShaderFlagType type;
union {
bool b32;
int32_t i32;
} value;
} ShaderFlag;
typedef enum {
SHADER_DEFAULT,
SHADER_CUBE,
@ -108,8 +123,8 @@ typedef struct Shader {
// Shader
Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const char* fragmentSource);
Shader* lovrShaderInitCompute(Shader* shader, const char* source);
Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const char* fragmentSource, ShaderFlag* flags, uint32_t flagCount);
Shader* lovrShaderInitCompute(Shader* shader, const char* source, ShaderFlag* flags, uint32_t flagCount);
Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type);
#define lovrShaderCreateGraphics(...) lovrShaderInitGraphics(lovrAlloc(Shader), __VA_ARGS__)
#define lovrShaderCreateCompute(...) lovrShaderInitCompute(lovrAlloc(Shader), __VA_ARGS__)