diff --git a/quakespasm/Quake/gl_model.h b/quakespasm/Quake/gl_model.h index c55d2eb5..fbec09a0 100644 --- a/quakespasm/Quake/gl_model.h +++ b/quakespasm/Quake/gl_model.h @@ -155,6 +155,8 @@ typedef struct msurface_s struct msurface_s *texturechain; mtexinfo_t *texinfo; + + int vbo_firstvert; // index of this surface's first vert in the VBO // lighting info int dlightframe; diff --git a/quakespasm/Quake/gl_rmisc.c b/quakespasm/Quake/gl_rmisc.c index 84e8f491..aa901049 100644 --- a/quakespasm/Quake/gl_rmisc.c +++ b/quakespasm/Quake/gl_rmisc.c @@ -273,7 +273,8 @@ void R_NewMap (void) R_ClearParticles (); GL_BuildLightmaps (); - + GL_BuildVBOs (); + r_framecount = 0; //johnfitz -- paranoid? r_visframecount = 0; //johnfitz -- paranoid? diff --git a/quakespasm/Quake/gl_vidsdl.c b/quakespasm/Quake/gl_vidsdl.c index 78183f8e..9cc879d2 100644 --- a/quakespasm/Quake/gl_vidsdl.c +++ b/quakespasm/Quake/gl_vidsdl.c @@ -92,9 +92,15 @@ qboolean gl_swap_control = false; //johnfitz qboolean gl_anisotropy_able = false; //johnfitz float gl_max_anisotropy; //johnfitz qboolean gl_texture_NPOT = false; //ericw +qboolean gl_vbo_able = false; //ericw PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc = 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 (); TexMgr_ReloadImages (); + GL_BuildVBOs (); GL_SetupState (); //warpimages needs to be recalculated @@ -788,6 +795,28 @@ static qboolean GL_ParseExtensionList (const char *list, const char *name) static void GL_CheckExtensions (void) { 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 // @@ -797,7 +826,8 @@ static void GL_CheckExtensions (void) { GL_MTexCoord2fFunc = (PFNGLMULTITEXCOORD2FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord2fARB"); 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"); gl_mtexable = true; @@ -1376,7 +1406,13 @@ void VID_Init (void) // new proc by S.A., called by alt-return key binding. 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; S_ClearBuffer (); diff --git a/quakespasm/Quake/glquake.h b/quakespasm/Quake/glquake.h index edb40607..e4918009 100644 --- a/quakespasm/Quake/glquake.h +++ b/quakespasm/Quake/glquake.h @@ -154,6 +154,7 @@ extern qboolean mtexenabled; extern qboolean gl_mtexable; extern PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc; extern PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc; +extern PFNGLCLIENTACTIVETEXTUREARBPROC GL_ClientActiveTextureFunc; //johnfitz -- anisotropic filtering #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE @@ -161,6 +162,14 @@ extern PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc; extern float gl_max_anisotropy; 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 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 GL_BuildLightmaps (void); +void GL_BuildVBOs (void); void R_RebuildAllLightmaps (void); int R_LightPoint (vec3_t p); diff --git a/quakespasm/Quake/r_brush.c b/quakespasm/Quake/r_brush.c index 111a0cd2..8c03f98d 100644 --- a/quakespasm/Quake/r_brush.c +++ b/quakespasm/Quake/r_brush.c @@ -950,6 +950,90 @@ void GL_BuildLightmaps (void) //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 ; jname[0] == '*' || m->type != mod_brush) + continue; + + for (i=0 ; inumsurfaces ; 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 ; jname[0] == '*' || m->type != mod_brush) + continue; + + for (i=0 ; inumsurfaces ; 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 diff --git a/quakespasm/Quake/r_world.c b/quakespasm/Quake/r_world.c index c0830beb..0ec3426a 100644 --- a/quakespasm/Quake/r_world.c +++ b/quakespasm/Quake/r_world.c @@ -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 ; jnumverts ; 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; inumedges; 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 @@ -417,11 +532,11 @@ R_DrawTextureChains_Multitexture -- johnfitz */ void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_t chain) { - int i, j; + int i; msurface_t *s; texture_t *t; - float *v; qboolean bound; + int lastlightmap; for (i=0 ; inumtextures ; 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)) continue; + R_ClearBatch (); + bound = false; + lastlightmap = 0; // avoid compiler warning for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { @@ -443,20 +561,22 @@ void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_ GL_EnableMultitexture(); // selects TEXTURE1 bound = true; + lastlightmap = s->lightmaptexturenum; } R_RenderDynamicLightmaps (s); + + if (s->lightmaptexturenum != lastlightmap) + R_FlushBatch (); + GL_Bind (lightmap_textures[s->lightmaptexturenum]); - glBegin(GL_POLYGON); - v = s->polys->verts[0]; - for (j=0 ; jpolys->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 (); + lastlightmap = s->lightmaptexturenum; + R_BatchSurface (s); + rs_brushpasses++; } + + R_FlushBatch (); + GL_DisableMultitexture(); // selects TEXTURE0 if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE)