GL3: Use dlightbits/lightflags in shader

This makes the fragment shader faster by skipping lights that haven't
marked this surface in GL3_MarkLights()

This seems to improve performance at least slightly everywhere, but
it really helps *a lot* on integrated intel GPUs like the one on their
Sandy Bridge, Ivy Bride and Haswell CPUs (those are the ones we tested).
This commit is contained in:
Daniel Gibson 2017-04-01 16:17:22 +02:00
parent 5656345d1f
commit 3503f91234
7 changed files with 79 additions and 39 deletions

View File

@ -70,6 +70,12 @@ GL3_MarkLights(dlight_t *light, int bit, mnode_t *node)
for (i = 0; i < node->numsurfaces; i++, surf++) for (i = 0; i < node->numsurfaces; i++, surf++)
{ {
if (surf->dlightframe != r_dlightframecount)
{
surf->dlightbits = 0;
surf->dlightframe = r_dlightframecount;
}
dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist; dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist;
if (dist >= 0) if (dist >= 0)
@ -86,12 +92,6 @@ GL3_MarkLights(dlight_t *light, int bit, mnode_t *node)
continue; continue;
} }
if (surf->dlightframe != r_dlightframecount)
{
surf->dlightbits = 0;
surf->dlightframe = r_dlightframecount;
}
surf->dlightbits |= bit; surf->dlightbits |= bit;
} }

View File

@ -196,6 +196,7 @@ GL3_LM_BuildPolygonFromSurface(msurface_t *fa)
vert->lmTexCoord[1] = t; vert->lmTexCoord[1] = t;
VectorCopy(normal, vert->normal); VectorCopy(normal, vert->normal);
vert->lightFlags = 0;
} }
poly->numverts = lnumverts; poly->numverts = lnumverts;

View File

@ -110,6 +110,7 @@ CreateShaderProgram(int numShaders, const GLuint* shaders)
glBindAttribLocation(shaderProgram, GL3_ATTRIB_LMTEXCOORD, "lmTexCoord"); glBindAttribLocation(shaderProgram, GL3_ATTRIB_LMTEXCOORD, "lmTexCoord");
glBindAttribLocation(shaderProgram, GL3_ATTRIB_COLOR, "vertColor"); glBindAttribLocation(shaderProgram, GL3_ATTRIB_COLOR, "vertColor");
glBindAttribLocation(shaderProgram, GL3_ATTRIB_NORMAL, "normal"); glBindAttribLocation(shaderProgram, GL3_ATTRIB_NORMAL, "normal");
glBindAttribLocation(shaderProgram, GL3_ATTRIB_LIGHTFLAGS, "lightFlags");
// the following line is not necessary/implicit (as there's only one output) // the following line is not necessary/implicit (as there's only one output)
// glBindFragDataLocation(shaderProgram, 0, "outColor"); XXX would this even be here? // glBindFragDataLocation(shaderProgram, 0, "outColor"); XXX would this even be here?
@ -262,6 +263,7 @@ static const char* vertexCommon3D = MULTILINE_STRING(#version 150\n
in vec2 lmTexCoord; // GL3_ATTRIB_LMTEXCOORD in vec2 lmTexCoord; // GL3_ATTRIB_LMTEXCOORD
in vec4 vertColor; // GL3_ATTRIB_COLOR in vec4 vertColor; // GL3_ATTRIB_COLOR
in vec3 normal; // GL3_ATTRIB_NORMAL in vec3 normal; // GL3_ATTRIB_NORMAL
in uint lightFlags; // GL3_ATTRIB_LIGHTFLAGS
out vec2 passTexCoord; out vec2 passTexCoord;
@ -337,6 +339,7 @@ static const char* vertexSrc3Dlm = MULTILINE_STRING(
out vec2 passLMcoord; out vec2 passLMcoord;
out vec3 passWorldCoord; out vec3 passWorldCoord;
out vec3 passNormal; out vec3 passNormal;
flat out uint passLightFlags;
void main() void main()
{ {
@ -344,6 +347,7 @@ static const char* vertexSrc3Dlm = MULTILINE_STRING(
passLMcoord = lmTexCoord; passLMcoord = lmTexCoord;
passWorldCoord = position; // TODO: multiply with model matrix for brush-based entities passWorldCoord = position; // TODO: multiply with model matrix for brush-based entities
passNormal = normalize(normal); // TODO: multiply with model matrix and normalize passNormal = normalize(normal); // TODO: multiply with model matrix and normalize
passLightFlags = lightFlags;
gl_Position = transProj * transModelView * vec4(position, 1.0); gl_Position = transProj * transModelView * vec4(position, 1.0);
} }
@ -356,6 +360,7 @@ static const char* vertexSrc3DlmFlow = MULTILINE_STRING(
out vec2 passLMcoord; out vec2 passLMcoord;
out vec3 passWorldCoord; out vec3 passWorldCoord;
out vec3 passNormal; out vec3 passNormal;
flat out uint passLightFlags;
void main() void main()
{ {
@ -363,6 +368,7 @@ static const char* vertexSrc3DlmFlow = MULTILINE_STRING(
passLMcoord = lmTexCoord; passLMcoord = lmTexCoord;
passWorldCoord = position; // TODO: multiply with model matrix for brush-based entities passWorldCoord = position; // TODO: multiply with model matrix for brush-based entities
passNormal = normalize(normal); // TODO: multiply with model matrix and normalize passNormal = normalize(normal); // TODO: multiply with model matrix and normalize
passLightFlags = lightFlags;
gl_Position = transProj * transModelView * vec4(position, 1.0); gl_Position = transProj * transModelView * vec4(position, 1.0);
} }
@ -417,6 +423,7 @@ static const char* fragmentSrc3Dlm = MULTILINE_STRING(
in vec2 passLMcoord; in vec2 passLMcoord;
in vec3 passWorldCoord; in vec3 passWorldCoord;
in vec3 passNormal; in vec3 passNormal;
flat in uint passLightFlags;
void main() void main()
{ {
@ -431,36 +438,37 @@ static const char* fragmentSrc3Dlm = MULTILINE_STRING(
lmTex += texture(lightmap2, passLMcoord) * lmScales[2]; lmTex += texture(lightmap2, passLMcoord) * lmScales[2];
lmTex += texture(lightmap3, passLMcoord) * lmScales[3]; lmTex += texture(lightmap3, passLMcoord) * lmScales[3];
float planeDist = -dot(passNormal, passWorldCoord); // signed dist to origin, hopefully like msurface_t->plane->dist if(passLightFlags != 0u)
// TODO: or is hardcoding 32 better?
for(uint i=uint(0); i<numDynLights; ++i)
{ {
// I made the following up, it's probably not too cool.. // TODO: or is hardcoding 32 better?
// it basically checks if the light is on the right side of the surface //for(uint i=0u; i<numDynLights; ++i)
// and, if it is, sets intensity according to distance between light and pixel on surface for(uint i=0u; i < 32u; ++i)
{
// I made the following up, it's probably not too cool..
// it basically checks if the light is on the right side of the surface
// and, if it is, sets intensity according to distance between light and pixel on surface
// signed distance between light and plane // dyn light number i does not affect this plane, just skip it
float fdist = dot(passNormal, dynLights[i].lightOrigin) + planeDist; if((passLightFlags & (1u << i)) == 0u) continue;
if(fdist < 0) continue; // light is on wrong side of the plane float intens = dynLights[i].lightColor.a;
float intens = dynLights[i].lightColor.a; vec3 lightToPos = dynLights[i].lightOrigin - passWorldCoord;
float distLightToPos = length(lightToPos);
float fact = max(0, intens - distLightToPos - 64); // FIXME: really -64 for DLIGHT_CUTOFF?
vec3 lightToPos = dynLights[i].lightOrigin - passWorldCoord; // also factor in angle between light and point on surface
float distLightToPos = length(lightToPos); fact *= dot(passNormal, lightToPos/distLightToPos);
float fact = max(0, intens - distLightToPos - 64); // FIXME: really - 64 for DLIGHT_CUTOFF?
fact *= dot(passNormal, lightToPos/distLightToPos); // also factor in angle between light and surfaces lmTex.rgb += dynLights[i].lightColor.rgb * fact * (1.0/256.0);
}
lmTex.rgb += dynLights[i].lightColor.rgb * fact * (1.0/256.0);
} }
lmTex.rgb *= overbrightbits; lmTex.rgb *= overbrightbits;
outColor = lmTex*texel; outColor = lmTex*texel;
outColor.rgb = pow(outColor.rgb, vec3(gamma)); // apply gamma correction to result outColor.rgb = pow(outColor.rgb, vec3(gamma)); // apply gamma correction to result
outColor.a = 1; // lightmaps aren't used with translucent surfaces outColor.a = 1; // lightmaps aren't used with translucent surfaces
//texel.a*alpha; // I think alpha shouldn't be modified by gamma and intensity
} }
); );
@ -845,13 +853,7 @@ initShader3D(gl3ShaderInfo_t* shaderInfo, const char* vertSrc, const char* fragS
glUniformBlockBinding(prog, blockIndex, GL3_BINDINGPOINT_UNILIGHTS); glUniformBlockBinding(prog, blockIndex, GL3_BINDINGPOINT_UNILIGHTS);
} }
else // else: as uniLights is only used in the LM shaders, it's ok if it's missing
{
// TODO: warn about this? only the lightmapped shaders have/need this..
R_Printf(PRINT_ALL, "WARNING: Couldn't find uniform block index 'uniLights'\n");
//goto err_cleanup;
}
// make sure texture is GL_TEXTURE0 // make sure texture is GL_TEXTURE0
GLint texLoc = glGetUniformLocation(prog, "tex"); GLint texLoc = glGetUniformLocation(prog, "tex");

View File

@ -43,9 +43,8 @@ extern int numgl3textures;
void GL3_SurfInit(void) void GL3_SurfInit(void)
{ {
// init the VAO and VBO for the standard vertexdata: 7 floats // init the VAO and VBO for the standard vertexdata: 10 floats and 1 uint
// (X, Y, Z), (S, T), (LMS, LMT), (normX, normY, normZ) - last two groups for lightmap/dynlights // (X, Y, Z), (S, T), (LMS, LMT), (normX, normY, normZ) - last two groups for lightmap/dynlights
// TODO: remove LMS, LMT? only used for lightmapped surfaces, but those need normal as well for dyn lights
glGenVertexArrays(1, &gl3state.vao3D); glGenVertexArrays(1, &gl3state.vao3D);
GL3_BindVAO(gl3state.vao3D); GL3_BindVAO(gl3state.vao3D);
@ -65,6 +64,10 @@ void GL3_SurfInit(void)
glEnableVertexAttribArray(GL3_ATTRIB_NORMAL); glEnableVertexAttribArray(GL3_ATTRIB_NORMAL);
qglVertexAttribPointer(GL3_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(gl3_3D_vtx_t), offsetof(gl3_3D_vtx_t, normal)); qglVertexAttribPointer(GL3_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(gl3_3D_vtx_t), offsetof(gl3_3D_vtx_t, normal));
glEnableVertexAttribArray(GL3_ATTRIB_LIGHTFLAGS);
qglVertexAttribIPointer(GL3_ATTRIB_LIGHTFLAGS, 1, GL_UNSIGNED_INT, sizeof(gl3_3D_vtx_t), offsetof(gl3_3D_vtx_t, lightFlags));
// init VAO and VBO for model vertexdata: 9 floats // init VAO and VBO for model vertexdata: 9 floats
// (X,Y,Z), (S,T), (R,G,B,A) // (X,Y,Z), (S,T), (R,G,B,A)
@ -169,9 +172,32 @@ TextureAnimation(mtexinfo_t *tex)
return tex->image; return tex->image;
} }
void
GL3_DrawGLPoly(glpoly_t *p) static void
SetLightFlags(msurface_t *surf)
{ {
unsigned int lightFlags = 0;
if (surf->dlightframe == gl3_framecount)
{
lightFlags = surf->dlightbits;
}
gl3_3D_vtx_t* verts = surf->polys->vertices;
int numVerts = surf->polys->numverts;
for(int i=0; i<numVerts; ++i)
{
verts[i].lightFlags = lightFlags;
}
}
void
GL3_DrawGLPoly(msurface_t *fa)
{
glpoly_t *p = fa->polys;
SetLightFlags(fa);
GL3_BindVAO(gl3state.vao3D); GL3_BindVAO(gl3state.vao3D);
GL3_BindVBO(gl3state.vbo3D); GL3_BindVBO(gl3state.vbo3D);
glBufferData(GL_ARRAY_BUFFER, sizeof(gl3_3D_vtx_t)*p->numverts, p->vertices, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(gl3_3D_vtx_t)*p->numverts, p->vertices, GL_STREAM_DRAW);
@ -185,6 +211,8 @@ GL3_DrawGLFlowingPoly(msurface_t *fa)
glpoly_t *p; glpoly_t *p;
float scroll; float scroll;
SetLightFlags(fa);
p = fa->polys; p = fa->polys;
scroll = -64.0f * ((gl3_newrefdef.time / 40.0f) - (int)(gl3_newrefdef.time / 40.0f)); scroll = -64.0f * ((gl3_newrefdef.time / 40.0f) - (int)(gl3_newrefdef.time / 40.0f));
@ -372,7 +400,7 @@ RenderBrushPoly(msurface_t *fa)
{ {
GL3_UseProgram(gl3state.si3Dlm.shaderProgram); GL3_UseProgram(gl3state.si3Dlm.shaderProgram);
UpdateLMscales(lmScales, &gl3state.si3Dlm); UpdateLMscales(lmScales, &gl3state.si3Dlm);
GL3_DrawGLPoly(fa->polys); GL3_DrawGLPoly(fa);
} }
// Note: lightmap chains are gone, lightmaps are rendered together with normal texture in one pass // Note: lightmap chains are gone, lightmaps are rendered together with normal texture in one pass
@ -430,7 +458,7 @@ GL3_DrawAlphaSurfaces(void)
else else
{ {
GL3_UseProgram(gl3state.si3Dtrans.shaderProgram); GL3_UseProgram(gl3state.si3Dtrans.shaderProgram);
GL3_DrawGLPoly(s->polys); GL3_DrawGLPoly(s);
} }
} }
@ -558,7 +586,7 @@ RenderLightmappedPoly(msurface_t *surf)
{ {
GL3_UseProgram(gl3state.si3Dlm.shaderProgram); GL3_UseProgram(gl3state.si3Dlm.shaderProgram);
UpdateLMscales(lmScales, &gl3state.si3Dlm); UpdateLMscales(lmScales, &gl3state.si3Dlm);
GL3_DrawGLPoly(surf->polys); GL3_DrawGLPoly(surf);
} }
} }

View File

@ -174,6 +174,7 @@ R_SubdividePolygon(int numverts, float *verts, msurface_t *warpface)
poly->vertices[i + 1].texCoord[0] = s; poly->vertices[i + 1].texCoord[0] = s;
poly->vertices[i + 1].texCoord[1] = t; poly->vertices[i + 1].texCoord[1] = t;
VectorCopy(normal, poly->vertices[i + 1].normal); VectorCopy(normal, poly->vertices[i + 1].normal);
poly->vertices[i + 1].lightFlags = 0;
} }
VectorScale(total, (1.0 / numverts), poly->vertices[0].pos); VectorScale(total, (1.0 / numverts), poly->vertices[0].pos);

View File

@ -64,6 +64,12 @@ qglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normaliz
glVertexAttribPointer(index, size, type, normalized, stride, (const void*)offset); glVertexAttribPointer(index, size, type, normalized, stride, (const void*)offset);
} }
static inline void
qglVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset)
{
glVertexAttribIPointer(index, size, type, stride, (void*)offset);
}
// attribute locations for vertex shaders // attribute locations for vertex shaders
enum { enum {
GL3_ATTRIB_POSITION = 0, GL3_ATTRIB_POSITION = 0,
@ -71,6 +77,7 @@ enum {
GL3_ATTRIB_LMTEXCOORD = 2, // for lightmap GL3_ATTRIB_LMTEXCOORD = 2, // for lightmap
GL3_ATTRIB_COLOR = 3, // per-vertex color GL3_ATTRIB_COLOR = 3, // per-vertex color
GL3_ATTRIB_NORMAL = 4, // vertex normal GL3_ATTRIB_NORMAL = 4, // vertex normal
GL3_ATTRIB_LIGHTFLAGS = 5 // uint, each set bit means "dyn light i affects this surface"
}; };
// TODO: do we need the following configurable? // TODO: do we need the following configurable?
@ -444,7 +451,7 @@ extern void GL3_AddSkySurface(msurface_t *fa);
// gl3_surf.c // gl3_surf.c
extern void GL3_SurfInit(void); extern void GL3_SurfInit(void);
extern void GL3_SurfShutdown(void); extern void GL3_SurfShutdown(void);
extern void GL3_DrawGLPoly(glpoly_t *p); extern void GL3_DrawGLPoly(msurface_t *fa);
extern void GL3_DrawGLFlowingPoly(msurface_t *fa); extern void GL3_DrawGLFlowingPoly(msurface_t *fa);
extern void GL3_DrawTriangleOutlines(void); extern void GL3_DrawTriangleOutlines(void);
extern void GL3_DrawAlphaSurfaces(void); extern void GL3_DrawAlphaSurfaces(void);

View File

@ -53,6 +53,7 @@ typedef struct gl3_3D_vtx_s {
float texCoord[2]; float texCoord[2];
float lmTexCoord[2]; // lightmap texture coordinate (sometimes unused) float lmTexCoord[2]; // lightmap texture coordinate (sometimes unused)
vec3_t normal; vec3_t normal;
GLuint lightFlags; // bit i set means: dynlight i affects surface
} gl3_3D_vtx_t; } gl3_3D_vtx_t;
// used for vertex array elements when drawing models // used for vertex array elements when drawing models