Load world and brush models into a VBO, and draw it in batches in R_DrawTextureChains_Multitexture. Uses the same immediate mode code as before if VBOs are not available, or if "-novbo" used at the command line. I only touched R_DrawTextureChains_Multitexture because it's usually the main bottleneck aside from alias model rendering.

This seems to help fps a fair bit on maps with a lot of world polys like jam2_tronyn. Tried on a few computers with intel and nvidia gpus, windows, mac os, linux, and there's always at least some fps improvement. Best case was 70fps -> 96fps on jam2_tronyn, on OS X + nvidia 650gt.

Interested to hear how this works for amd gpu's, just do a timedemo with and without "-novbo".

Only downside is I had to disable the fast path in Vid_Toggle_f() because at least with SDL1, the vbo no longer works after a toggle. So as a result, fullscreen toggles with alt-enter are slightly slower.

git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1018 af15c1b1-3010-417e-b628-4374ebc0bcbd
This commit is contained in:
Eric Wasylishen 2014-09-11 04:55:16 +00:00
parent b2c3363718
commit e0680cb6d3
6 changed files with 267 additions and 14 deletions

View file

@ -155,6 +155,8 @@ typedef struct msurface_s
struct msurface_s *texturechain; struct msurface_s *texturechain;
mtexinfo_t *texinfo; mtexinfo_t *texinfo;
int vbo_firstvert; // index of this surface's first vert in the VBO
// lighting info // lighting info
int dlightframe; int dlightframe;

View file

@ -273,7 +273,8 @@ void R_NewMap (void)
R_ClearParticles (); R_ClearParticles ();
GL_BuildLightmaps (); GL_BuildLightmaps ();
GL_BuildVBOs ();
r_framecount = 0; //johnfitz -- paranoid? r_framecount = 0; //johnfitz -- paranoid?
r_visframecount = 0; //johnfitz -- paranoid? r_visframecount = 0; //johnfitz -- paranoid?

View file

@ -92,9 +92,15 @@ qboolean gl_swap_control = false; //johnfitz
qboolean gl_anisotropy_able = false; //johnfitz qboolean gl_anisotropy_able = false; //johnfitz
float gl_max_anisotropy; //johnfitz float gl_max_anisotropy; //johnfitz
qboolean gl_texture_NPOT = false; //ericw qboolean gl_texture_NPOT = false; //ericw
qboolean gl_vbo_able = false; //ericw
PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc = NULL; //johnfitz PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc = NULL; //johnfitz
PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc = NULL; //johnfitz PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc = NULL; //johnfitz
PFNGLCLIENTACTIVETEXTUREARBPROC GL_ClientActiveTextureFunc = NULL; //ericw
PFNGLBINDBUFFERARBPROC GL_BindBufferFunc = NULL; //ericw
PFNGLBUFFERDATAARBPROC GL_BufferDataFunc = NULL; //ericw
PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc = NULL; //ericw
PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc = NULL; //ericw
//==================================== //====================================
@ -634,6 +640,7 @@ static void VID_Restart (void)
GL_Init (); GL_Init ();
TexMgr_ReloadImages (); TexMgr_ReloadImages ();
GL_BuildVBOs ();
GL_SetupState (); GL_SetupState ();
//warpimages needs to be recalculated //warpimages needs to be recalculated
@ -788,6 +795,28 @@ static qboolean GL_ParseExtensionList (const char *list, const char *name)
static void GL_CheckExtensions (void) static void GL_CheckExtensions (void)
{ {
int swap_control; int swap_control;
//
// ARB_vertex_buffer_object
//
if (COM_CheckParm("-novbo"))
Con_Warning ("Vertex buffer objects disabled at command line\n");
else
{
GL_BindBufferFunc = (PFNGLBINDBUFFERARBPROC) SDL_GL_GetProcAddress("glBindBufferARB");
GL_BufferDataFunc = (PFNGLBUFFERDATAARBPROC) SDL_GL_GetProcAddress("glBufferDataARB");
GL_DeleteBuffersFunc = (PFNGLDELETEBUFFERSARBPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB");
GL_GenBuffersFunc = (PFNGLGENBUFFERSARBPROC) SDL_GL_GetProcAddress("glGenBuffersARB");
if (GL_BindBufferFunc && GL_BufferDataFunc && GL_DeleteBuffersFunc && GL_GenBuffersFunc)
{
Con_Printf("FOUND: ARB_vertex_buffer_object\n");
gl_vbo_able = true;
}
else
{
Con_Warning ("ARB_vertex_buffer_object not available\n");
}
}
// multitexture // multitexture
// //
@ -797,7 +826,8 @@ static void GL_CheckExtensions (void)
{ {
GL_MTexCoord2fFunc = (PFNGLMULTITEXCOORD2FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord2fARB"); GL_MTexCoord2fFunc = (PFNGLMULTITEXCOORD2FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord2fARB");
GL_SelectTextureFunc = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB"); GL_SelectTextureFunc = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
if (GL_MTexCoord2fFunc && GL_SelectTextureFunc) GL_ClientActiveTextureFunc = (PFNGLCLIENTACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glClientActiveTextureARB");
if (GL_MTexCoord2fFunc && GL_SelectTextureFunc && GL_ClientActiveTextureFunc)
{ {
Con_Printf("FOUND: ARB_multitexture\n"); Con_Printf("FOUND: ARB_multitexture\n");
gl_mtexable = true; gl_mtexable = true;
@ -1376,7 +1406,13 @@ void VID_Init (void)
// new proc by S.A., called by alt-return key binding. // new proc by S.A., called by alt-return key binding.
void VID_Toggle (void) void VID_Toggle (void)
{ {
static qboolean vid_toggle_works = true; // disabling the fast path because with SDL 1.2 it invalidates VBOs (using them
// causes a crash, sugesting that the fullscreen toggle created a new GL context,
// although texture objects remain valid for some reason).
//
// SDL2 does promise window resizes / fullscreen changes preserve the GL context,
// so we could use the fast path with SDL2. --ericw
static qboolean vid_toggle_works = false;
qboolean toggleWorked; qboolean toggleWorked;
S_ClearBuffer (); S_ClearBuffer ();

View file

@ -154,6 +154,7 @@ extern qboolean mtexenabled;
extern qboolean gl_mtexable; extern qboolean gl_mtexable;
extern PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc; extern PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc;
extern PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc; extern PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc;
extern PFNGLCLIENTACTIVETEXTUREARBPROC GL_ClientActiveTextureFunc;
//johnfitz -- anisotropic filtering //johnfitz -- anisotropic filtering
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
@ -161,6 +162,14 @@ extern PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc;
extern float gl_max_anisotropy; extern float gl_max_anisotropy;
extern qboolean gl_anisotropy_able; extern qboolean gl_anisotropy_able;
//ericw -- VBO
extern PFNGLBINDBUFFERARBPROC GL_BindBufferFunc;
extern PFNGLBUFFERDATAARBPROC GL_BufferDataFunc;
extern PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc;
extern PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc;
extern qboolean gl_vbo_able;
//ericw
//ericw -- NPOT texture support //ericw -- NPOT texture support
extern qboolean gl_texture_NPOT; extern qboolean gl_texture_NPOT;
@ -265,6 +274,7 @@ void R_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain
void R_RenderDlights (void); void R_RenderDlights (void);
void GL_BuildLightmaps (void); void GL_BuildLightmaps (void);
void GL_BuildVBOs (void);
void R_RebuildAllLightmaps (void); void R_RebuildAllLightmaps (void);
int R_LightPoint (vec3_t p); int R_LightPoint (vec3_t p);

View file

@ -950,6 +950,90 @@ void GL_BuildLightmaps (void)
//johnfitz //johnfitz
} }
/*
=============================================================
VBO support
=============================================================
*/
static GLuint gl_bmodel_vbo = 0;
/*
==================
GL_BuildVBOs
Deletes gl_bmodel_vbo if it already exists, then rebuilds it with all
surfaces from world + all brush models
==================
*/
void GL_BuildVBOs (void)
{
unsigned int numverts, varray_bytes, varray_index;
int i, j;
qmodel_t *m;
float *varray;
if (!(gl_vbo_able && gl_mtexable))
return;
// ask GL for a name for our VBO
GL_DeleteBuffersFunc (1, &gl_bmodel_vbo);
GL_GenBuffersFunc (1, &gl_bmodel_vbo);
// count all verts in all models
numverts = 0;
for (j=1 ; j<MAX_MODELS ; j++)
{
m = cl.model_precache[j];
if (!m || m->name[0] == '*' || m->type != mod_brush)
continue;
for (i=0 ; i<m->numsurfaces ; i++)
{
numverts += m->surfaces[i].numedges;
}
}
// build vertex array
varray_bytes = VERTEXSIZE * sizeof(float) * numverts;
varray = (float *) malloc (varray_bytes);
varray_index = 0;
for (j=1 ; j<MAX_MODELS ; j++)
{
m = cl.model_precache[j];
if (!m || m->name[0] == '*' || m->type != mod_brush)
continue;
for (i=0 ; i<m->numsurfaces ; i++)
{
msurface_t *s = &m->surfaces[i];
s->vbo_firstvert = varray_index;
memcpy (&varray[VERTEXSIZE * varray_index], s->polys->verts, VERTEXSIZE * sizeof(float) * s->numedges);
varray_index += s->numedges;
}
}
// upload to GPU
GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo);
GL_BufferDataFunc (GL_ARRAY_BUFFER, varray_bytes, varray, GL_STATIC_DRAW);
free (varray);
// setup vertex array. this will need to move if we use vertex arrays for other things
glVertexPointer (3, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0));
glEnableClientState (GL_VERTEX_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE0_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 3);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE1_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 5);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
}
/* /*
=============== ===============
R_AddDynamicLights R_AddDynamicLights

View file

@ -410,6 +410,121 @@ void R_DrawTextureChains_Glow (qmodel_t *model, entity_t *ent, texchain_t chain)
} }
} }
//==============================================================================
//
// VBO SUPPORT
//
//==============================================================================
/*
================
R_MultitexturedDrawGLPoly
Fallback immediate mode code to draw a multitexutred glpoly_t
================
*/
static void R_MultitexturedDrawGLPoly (glpoly_t *p)
{
float *v;
int j;
glBegin(GL_POLYGON);
v = p->verts[0];
for (j=0 ; j<p->numverts ; j++, v+= VERTEXSIZE)
{
GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]);
GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]);
glVertex3fv (v);
}
glEnd ();
}
static unsigned int R_NumTriangleIndicesForSurf (msurface_t *s)
{
return 3 * (s->numedges - 2);
}
/*
================
R_TriangleIndicesForSurf
Writes out the triangle indices needed to draw s as a triangle list.
The number of indices it will write is given by R_NumTriangleIndicesForSurf.
================
*/
static void R_TriangleIndicesForSurf (msurface_t *s, unsigned int *dest)
{
int i;
for (i=2; i<s->numedges; i++)
{
*dest++ = s->vbo_firstvert;
*dest++ = s->vbo_firstvert + i - 1;
*dest++ = s->vbo_firstvert + i;
}
}
#define MAX_BATCH_SIZE 4096
static unsigned int vbo_indices[MAX_BATCH_SIZE];
static unsigned int num_vbo_indices;
/*
================
R_ClearBatch
================
*/
static void R_ClearBatch ()
{
if (!(gl_vbo_able && gl_mtexable)) return;
num_vbo_indices = 0;
}
/*
================
R_FlushBatch
Draw the current batch if non-empty and clears it, ready for more R_BatchSurface calls.
================
*/
static void R_FlushBatch ()
{
if (!(gl_vbo_able && gl_mtexable)) return;
if (num_vbo_indices > 0)
{
glDrawElements (GL_TRIANGLES, num_vbo_indices, GL_UNSIGNED_INT, vbo_indices);
num_vbo_indices = 0;
}
}
/*
================
R_BatchSurface
Add the surface to the current batch, or just draw it immediately if we're not
using VBOs.
================
*/
static void R_BatchSurface (msurface_t *s)
{
int num_surf_indices;
if (!(gl_vbo_able && gl_mtexable))
{
R_MultitexturedDrawGLPoly (s->polys);
return;
}
num_surf_indices = R_NumTriangleIndicesForSurf (s);
if (num_vbo_indices + num_surf_indices > MAX_BATCH_SIZE)
R_FlushBatch();
R_TriangleIndicesForSurf (s, &vbo_indices[num_vbo_indices]);
num_vbo_indices += num_surf_indices;
}
/* /*
================ ================
R_DrawTextureChains_Multitexture -- johnfitz R_DrawTextureChains_Multitexture -- johnfitz
@ -417,11 +532,11 @@ R_DrawTextureChains_Multitexture -- johnfitz
*/ */
void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_t chain) void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_t chain)
{ {
int i, j; int i;
msurface_t *s; msurface_t *s;
texture_t *t; texture_t *t;
float *v;
qboolean bound; qboolean bound;
int lastlightmap;
for (i=0 ; i<model->numtextures ; i++) for (i=0 ; i<model->numtextures ; i++)
{ {
@ -430,7 +545,10 @@ void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_
if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTILED | SURF_NOTEXTURE)) if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTILED | SURF_NOTEXTURE))
continue; continue;
R_ClearBatch ();
bound = false; bound = false;
lastlightmap = 0; // avoid compiler warning
for (s = t->texturechains[chain]; s; s = s->texturechain) for (s = t->texturechains[chain]; s; s = s->texturechain)
if (!s->culled) if (!s->culled)
{ {
@ -443,20 +561,22 @@ void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_
GL_EnableMultitexture(); // selects TEXTURE1 GL_EnableMultitexture(); // selects TEXTURE1
bound = true; bound = true;
lastlightmap = s->lightmaptexturenum;
} }
R_RenderDynamicLightmaps (s); R_RenderDynamicLightmaps (s);
if (s->lightmaptexturenum != lastlightmap)
R_FlushBatch ();
GL_Bind (lightmap_textures[s->lightmaptexturenum]); GL_Bind (lightmap_textures[s->lightmaptexturenum]);
glBegin(GL_POLYGON); lastlightmap = s->lightmaptexturenum;
v = s->polys->verts[0]; R_BatchSurface (s);
for (j=0 ; j<s->polys->numverts ; j++, v+= VERTEXSIZE)
{
GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]);
GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]);
glVertex3fv (v);
}
glEnd ();
rs_brushpasses++; rs_brushpasses++;
} }
R_FlushBatch ();
GL_DisableMultitexture(); // selects TEXTURE0 GL_DisableMultitexture(); // selects TEXTURE0
if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE) if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE)