mirror of https://github.com/bjornbytes/lovr.git
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:
parent
6134abe866
commit
3267bad4fa
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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__)
|
||||
|
|
Loading…
Reference in New Issue