]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
Fix UI corruption caused by poor handling of missing VBO with firstvertex > 0.
[xonotic/darkplaces.git] / gl_rmain.c
index ba6c4432e9a788914ad23a1ad12d11dda6e5fed3..b9cde18ced5ef40ef57affa5487dcb6a970bb688 100644 (file)
@@ -42,7 +42,7 @@ __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
 mempool_t *r_main_mempool;
 rtexturepool_t *r_main_texturepool;
 
-static int r_textureframe = 0; ///< used only by R_GetCurrentTexture
+int r_textureframe = 0; ///< used only by R_GetCurrentTexture, incremented per view and per UI render
 
 static qboolean r_loadnormalmap;
 static qboolean r_loadgloss;
@@ -1153,6 +1153,9 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
        geomstrings_list[geomstrings_count++] = sourcestring;
        fragstrings_list[fragstrings_count++] = sourcestring;
 
+       // we don't currently use geometry shaders for anything, so just empty the list
+       geomstrings_count = 0;
+
        // compile the shader program
        if (vertstrings_count + geomstrings_count + fragstrings_count)
                p->program = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, geomstrings_count, geomstrings_list, fragstrings_count, fragstrings_list);
@@ -1364,7 +1367,7 @@ static void R_SetupShader_SetPermutationGLSL(unsigned int mode, dpuint64 permuta
                {
                        if (!r_glsl_permutation->compiled)
                        {
-                               Con_DPrintf("Compiling shader mode %u permutation %u\n", mode, permutation);
+                               Con_DPrintf("Compiling shader mode %u permutation %llx\n", mode, permutation);
                                R_GLSL_CompilePermutation(perm, mode, permutation);
                        }
                        if (!r_glsl_permutation->program)
@@ -1407,7 +1410,7 @@ void R_GLSL_Restart_f(void)
        unsigned int i, limit;
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                {
                        r_glsl_permutation_t *p;
@@ -1484,7 +1487,7 @@ void R_SetupShader_Generic(rtexture_t *t, qboolean usegamma, qboolean notrippy,
                GL_AlphaToCoverage(false);
        switch (vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                R_SetupShader_SetPermutationGLSL(SHADERMODE_GENERIC, permutation);
                if (r_glsl_permutation->tex_Texture_First >= 0)
@@ -1514,7 +1517,7 @@ void R_SetupShader_DepthOrShadow(qboolean notrippy, qboolean depthrgb, qboolean
                GL_AlphaToCoverage(false);
        switch (vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                R_SetupShader_SetPermutationGLSL(SHADERMODE_DEPTH_OR_SHADOW, permutation);
 #ifndef USE_GLES2 /* FIXME: GLES3 only */
@@ -1860,19 +1863,10 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                permutation |= SHADERPERMUTATION_FOGALPHAHACK;
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_ARRAY_LIGHTMAP : 0) | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
-               R_Mesh_VertexPointer(     3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
-               R_Mesh_ColorPointer(      4, GL_FLOAT, sizeof(float[4]), rsurface.batchlightmapcolor4f, rsurface.batchlightmapcolor4f_vertexbuffer, rsurface.batchlightmapcolor4f_bufferoffset);
-               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
-               R_Mesh_TexCoordPointer(1, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchsvector3f, rsurface.batchsvector3f_vertexbuffer, rsurface.batchsvector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(2, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchtvector3f, rsurface.batchtvector3f_vertexbuffer, rsurface.batchtvector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(3, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchnormal3f, rsurface.batchnormal3f_vertexbuffer, rsurface.batchnormal3f_bufferoffset);
-               R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
-               R_Mesh_TexCoordPointer(5, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
-               R_Mesh_TexCoordPointer(6, 4, GL_UNSIGNED_BYTE | 0x80000000, sizeof(unsigned char[4]), rsurface.batchskeletalindex4ub, rsurface.batchskeletalindex4ub_vertexbuffer, rsurface.batchskeletalindex4ub_bufferoffset);
-               R_Mesh_TexCoordPointer(7, 4, GL_UNSIGNED_BYTE, sizeof(unsigned char[4]), rsurface.batchskeletalweight4ub, rsurface.batchskeletalweight4ub_vertexbuffer, rsurface.batchskeletalweight4ub_bufferoffset);
+               RSurf_UploadBuffersForBatch();
                // this has to be after RSurf_PrepareVerticesForBatch
                if (rsurface.batchskeletaltransform3x4buffer)
                        permutation |= SHADERPERMUTATION_SKELETAL;
@@ -2076,7 +2070,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
        Matrix4x4_ToArrayFloatGL(&viewtolight, viewtolight16f);
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                R_SetupShader_SetPermutationGLSL(mode, permutation);
                if (r_glsl_permutation->loc_LightPosition             >= 0) qglUniform3f(       r_glsl_permutation->loc_LightPosition            , viewlightorigin[0], viewlightorigin[1], viewlightorigin[2]);
@@ -2185,6 +2179,7 @@ skinframe_t *R_SkinFrame_FindNextByName( skinframe_t *last, const char *name ) {
 skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewidth, int compareheight, int comparecrc, qboolean add)
 {
        skinframe_t *item;
+       int compareflags = textureflags & TEXF_IMPORTANTBITS;
        int hashindex;
        char basename[MAX_QPATH];
 
@@ -2192,7 +2187,11 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
 
        hashindex = CRC_Block((unsigned char *)basename, strlen(basename)) & (SKINFRAME_HASH - 1);
        for (item = r_skinframe.hash[hashindex];item;item = item->next)
-               if (!strcmp(item->basename, basename) && (comparecrc < 0 || (item->textureflags == textureflags && item->comparewidth == comparewidth && item->compareheight == compareheight && item->comparecrc == comparecrc)))
+               if (!strcmp(item->basename, basename) &&
+                       item->textureflags == compareflags &&
+                       item->comparewidth == comparewidth &&
+                       item->compareheight == compareheight &&
+                       item->comparecrc == comparecrc)
                        break;
 
        if (!item)
@@ -2202,7 +2201,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                item = (skinframe_t *)Mem_ExpandableArray_AllocRecord(&r_skinframe.array);
                memset(item, 0, sizeof(*item));
                strlcpy(item->basename, basename, sizeof(item->basename));
-               item->textureflags = textureflags & ~TEXF_FORCE_RELOAD;
+               item->textureflags = compareflags;
                item->comparewidth = comparewidth;
                item->compareheight = compareheight;
                item->comparecrc = comparecrc;
@@ -2210,11 +2209,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                r_skinframe.hash[hashindex] = item;
        }
        else if (textureflags & TEXF_FORCE_RELOAD)
-       {
-               if (!add)
-                       return NULL;
                R_SkinFrame_PurgeSkinFrame(item);
-       }
 
        R_SkinFrame_MarkUsed(item);
        return item;
@@ -2263,12 +2258,11 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                return NULL;
 
        // return an existing skinframe if already loaded
-       // if loading of the first image fails, don't make a new skinframe as it
-       // would cause all future lookups of this to be missing
-       skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, -1, false);
+       skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, 0, false);
        if (skinframe && skinframe->base)
                return skinframe;
 
+       // if the skinframe doesn't exist this will create it
        return R_SkinFrame_LoadExternal_SkinFrame(skinframe, name, textureflags, complain, fallbacknotexture);
 }
 
@@ -2487,8 +2481,7 @@ skinframe_t *R_SkinFrame_LoadExternal_SkinFrame(skinframe_t *skinframe, const ch
        return skinframe;
 }
 
-// this is only used by .spr32 sprites, HL .spr files, HL .bsp files
-skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, const unsigned char *skindata, int width, int height, qboolean sRGB)
+skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, const unsigned char *skindata, int width, int height, int comparewidth, int compareheight, int comparecrc, qboolean sRGB)
 {
        int i;
        skinframe_t *skinframe;
@@ -2498,7 +2491,7 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co
                return NULL;
 
        // if already loaded just return it, otherwise make a new skinframe
-       skinframe = R_SkinFrame_Find(name, textureflags, width, height, (!(textureflags & TEXF_FORCE_RELOAD) && skindata) ? CRC_Block(skindata, width*height*4) : -1, true);
+       skinframe = R_SkinFrame_Find(name, textureflags, comparewidth, compareheight, comparecrc, true);
        if (skinframe->base)
                return skinframe;
        textureflags &= ~TEXF_FORCE_RELOAD;
@@ -2568,7 +2561,7 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
                return NULL;
 
        // if already loaded just return it, otherwise make a new skinframe
-       skinframe = R_SkinFrame_Find(name, textureflags, width, height, (!(textureflags & TEXF_FORCE_RELOAD) && skindata) ? CRC_Block(skindata, width*height) : -1, true);
+       skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height) : 0, true);
        if (skinframe->base)
                return skinframe;
        //textureflags &= ~TEXF_FORCE_RELOAD;
@@ -2807,7 +2800,7 @@ skinframe_t *R_SkinFrame_LoadNoTexture(void)
                }
        }
 
-       return R_SkinFrame_LoadInternalBGRA("notexture", TEXF_FORCENEAREST, pix[0][0], 16, 16, false);
+       return R_SkinFrame_LoadInternalBGRA("notexture", TEXF_FORCENEAREST, pix[0][0], 16, 16, 0, 0, 0, false);
 }
 
 skinframe_t *R_SkinFrame_LoadInternalUsingTexture(const char *name, int textureflags, rtexture_t *tex, int width, int height, qboolean sRGB)
@@ -2816,7 +2809,7 @@ skinframe_t *R_SkinFrame_LoadInternalUsingTexture(const char *name, int texturef
        if (cls.state == ca_dedicated)
                return NULL;
        // if already loaded just return it, otherwise make a new skinframe
-       skinframe = R_SkinFrame_Find(name, textureflags, width, height, (textureflags & TEXF_FORCE_RELOAD) ? -1 : 0, true);
+       skinframe = R_SkinFrame_Find(name, textureflags, width, height, 0, true);
        if (skinframe->base)
                return skinframe;
        textureflags &= ~TEXF_FORCE_RELOAD;
@@ -3028,13 +3021,13 @@ static void gl_main_start(void)
        r_uniformbufferalignment = 32;
 
        r_loaddds = r_texture_dds_load.integer != 0;
-       r_savedds = vid.support.arb_texture_compression && vid.support.ext_texture_compression_s3tc && r_texture_dds_save.integer;
+       r_savedds = vid.support.ext_texture_compression_s3tc && r_texture_dds_save.integer;
 
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
-               Cvar_SetValueQuick(&r_textureunits, vid.texunits);
+               Cvar_SetValueQuick(&r_textureunits, MAX_TEXTUREUNITS);
                Cvar_SetValueQuick(&gl_combine, 1);
                Cvar_SetValueQuick(&r_glsl, 1);
                r_loadnormalmap = true;
@@ -3120,7 +3113,7 @@ static void gl_main_shutdown(void)
 
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
 #if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
                if (r_maxqueries)
@@ -4510,7 +4503,7 @@ void R_EntityMatrix(const matrix4x4_t *matrix)
                CHECKGLERROR
                switch(vid.renderpath)
                {
-               case RENDERPATH_GL20:
+               case RENDERPATH_GL32:
                case RENDERPATH_GLES2:
                        if (r_glsl_permutation && r_glsl_permutation->loc_ModelViewProjectionMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewProjectionMatrix, 1, false, gl_modelviewprojection16f);
                        if (r_glsl_permutation && r_glsl_permutation->loc_ModelViewMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewMatrix, 1, false, gl_modelview16f);
@@ -4543,7 +4536,7 @@ void R_ResetViewRendering2D_Common(int viewfbo, rtexture_t *viewdepthtexture, rt
        GL_PolygonOffset(0, 0);
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
                break;
@@ -4575,7 +4568,7 @@ void R_ResetViewRendering3D(int viewfbo, rtexture_t *viewdepthtexture, rtexture_
        GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
                break;
@@ -5114,7 +5107,7 @@ static void R_Bloom_StartFrame(void)
 
        switch (vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
                r_fb.usedepthtextures = r_usedepthtextures.integer != 0;
                if (r_viewfbo.integer == 2) textype = TEXTYPE_COLORBUFFER16F;
                if (r_viewfbo.integer == 3) textype = TEXTYPE_COLORBUFFER32F;
@@ -5246,6 +5239,7 @@ static void R_Bloom_MakeTexture(void)
                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight;
        }
+       CHECKGLERROR
 
        range = r_bloom_blur.integer * r_fb.bloomwidth / 320;
        brighten = r_bloom_brighten.value;
@@ -5261,8 +5255,11 @@ static void R_Bloom_MakeTexture(void)
                // blend on at multiple vertical offsets to achieve a vertical blur
                // TODO: do offset blends using GLSL
                // TODO instead of changing the texcoords, change the target positions to prevent artifacts at edges
+               CHECKGLERROR
                GL_BlendFunc(GL_ONE, GL_ZERO);
+               CHECKGLERROR
                R_SetupShader_Generic(prev->colortexture[0], false, true, false);
+               CHECKGLERROR
                for (x = -range;x <= range;x++)
                {
                        if (!dir){xoffset = 0;yoffset = x;}
@@ -5288,11 +5285,16 @@ static void R_Bloom_MakeTexture(void)
                                r *= (1 - x*x/(float)((range+1)*(range+1)));
                        if (r <= 0)
                                continue;
+                       CHECKGLERROR
                        GL_Color(r, r, r, 1);
+                       CHECKGLERROR
                        R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.offsettexcoord2f);
+                       CHECKGLERROR
                        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight;
+                       CHECKGLERROR
                        GL_BlendFunc(GL_ONE, GL_ONE);
+                       CHECKGLERROR
                }
        }
 
@@ -5410,7 +5412,7 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v
        R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_fb.rt_screen->texcoord2f, bloomtexture ? r_fb.rt_bloom->texcoord2f : NULL);
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                permutation =
                        (r_fb.bloomwidth ? SHADERPERMUTATION_BLOOM : 0)
@@ -5567,7 +5569,7 @@ void R_UpdateVariables(void)
        r_gpuskeletal = false;
        switch(vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
                r_gpuskeletal = r_glsl_skeletal.integer && !r_showsurfaces.integer;
        case RENDERPATH_GLES2:
                if(!vid_gammatables_trivial)
@@ -7453,6 +7455,46 @@ float RSurf_FogVertex(const float *v)
        return r_refdef.fogmasktable[min(fogmasktableindex, FOGMASKTABLEWIDTH - 1)];
 }
 
+void RSurf_UploadBuffersForBatch(void)
+{
+       // upload buffer data for generated vertex data (dynamicvertex case) or index data (copytriangles case) and models that lack it to begin with (e.g. DrawQ_FlushUI)
+       // note that if rsurface.batchvertex3f_vertexbuffer is NULL, dynamicvertex is forced as we don't account for the proper base vertex here.
+       if (rsurface.batchvertex3f && !rsurface.batchvertex3f_vertexbuffer)
+               rsurface.batchvertex3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f, R_BUFFERDATA_VERTEX, &rsurface.batchvertex3f_bufferoffset);
+       if (rsurface.batchsvector3f && !rsurface.batchsvector3f_vertexbuffer)
+               rsurface.batchsvector3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchsvector3f, R_BUFFERDATA_VERTEX, &rsurface.batchsvector3f_bufferoffset);
+       if (rsurface.batchtvector3f && !rsurface.batchtvector3f_vertexbuffer)
+               rsurface.batchtvector3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchtvector3f, R_BUFFERDATA_VERTEX, &rsurface.batchtvector3f_bufferoffset);
+       if (rsurface.batchnormal3f && !rsurface.batchnormal3f_vertexbuffer)
+               rsurface.batchnormal3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f, R_BUFFERDATA_VERTEX, &rsurface.batchnormal3f_bufferoffset);
+       if (rsurface.batchlightmapcolor4f && !rsurface.batchlightmapcolor4f_vertexbuffer)
+               rsurface.batchlightmapcolor4f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[4]), rsurface.batchlightmapcolor4f, R_BUFFERDATA_VERTEX, &rsurface.batchlightmapcolor4f_bufferoffset);
+       if (rsurface.batchtexcoordtexture2f && !rsurface.batchtexcoordtexture2f_vertexbuffer)
+               rsurface.batchtexcoordtexture2f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[2]), rsurface.batchtexcoordtexture2f, R_BUFFERDATA_VERTEX, &rsurface.batchtexcoordtexture2f_bufferoffset);
+       if (rsurface.batchtexcoordlightmap2f && !rsurface.batchtexcoordlightmap2f_vertexbuffer)
+               rsurface.batchtexcoordlightmap2f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[2]), rsurface.batchtexcoordlightmap2f, R_BUFFERDATA_VERTEX, &rsurface.batchtexcoordlightmap2f_bufferoffset);
+       if (rsurface.batchskeletalindex4ub && !rsurface.batchskeletalindex4ub_vertexbuffer)
+               rsurface.batchskeletalindex4ub_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(unsigned char[4]), rsurface.batchskeletalindex4ub, R_BUFFERDATA_VERTEX, &rsurface.batchskeletalindex4ub_bufferoffset);
+       if (rsurface.batchskeletalweight4ub && !rsurface.batchskeletalweight4ub_vertexbuffer)
+               rsurface.batchskeletalweight4ub_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(unsigned char[4]), rsurface.batchskeletalweight4ub, R_BUFFERDATA_VERTEX, &rsurface.batchskeletalweight4ub_bufferoffset);
+
+       if (rsurface.batchelement3s && !rsurface.batchelement3s_indexbuffer)
+               rsurface.batchelement3s_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(short[3]), rsurface.batchelement3s, R_BUFFERDATA_INDEX16, &rsurface.batchelement3s_bufferoffset);
+       else if (rsurface.batchelement3i && !rsurface.batchelement3i_indexbuffer)
+               rsurface.batchelement3i_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(int[3]), rsurface.batchelement3i, R_BUFFERDATA_INDEX32, &rsurface.batchelement3i_bufferoffset);
+
+       R_Mesh_VertexPointer(     3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
+       R_Mesh_ColorPointer(      4, GL_FLOAT, sizeof(float[4]), rsurface.batchlightmapcolor4f, rsurface.batchlightmapcolor4f_vertexbuffer, rsurface.batchlightmapcolor4f_bufferoffset);
+       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+       R_Mesh_TexCoordPointer(1, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchsvector3f, rsurface.batchsvector3f_vertexbuffer, rsurface.batchsvector3f_bufferoffset);
+       R_Mesh_TexCoordPointer(2, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchtvector3f, rsurface.batchtvector3f_vertexbuffer, rsurface.batchtvector3f_bufferoffset);
+       R_Mesh_TexCoordPointer(3, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchnormal3f, rsurface.batchnormal3f_vertexbuffer, rsurface.batchnormal3f_bufferoffset);
+       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
+       R_Mesh_TexCoordPointer(5, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+       R_Mesh_TexCoordPointer(6, 4, GL_UNSIGNED_BYTE | 0x80000000, sizeof(unsigned char[4]), rsurface.batchskeletalindex4ub, rsurface.batchskeletalindex4ub_vertexbuffer, rsurface.batchskeletalindex4ub_bufferoffset);
+       R_Mesh_TexCoordPointer(7, 4, GL_UNSIGNED_BYTE, sizeof(unsigned char[4]), rsurface.batchskeletalweight4ub, rsurface.batchskeletalweight4ub_vertexbuffer, rsurface.batchskeletalweight4ub_bufferoffset);
+}
+
 static void RSurf_RenumberElements(const int *inelement3i, int *outelement3i, int numelements, int adjust)
 {
        int i;
@@ -7529,6 +7571,14 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
        // check if any dynamic vertex processing must occur
        dynamicvertex = false;
 
+       // we must use vertexbuffers for rendering, we can upload vertex buffers
+       // easily enough but if the basevertex is non-zero it becomes more
+       // difficult, so force dynamicvertex path in that case - it's suboptimal
+       // but the most optimal case is to have the geometry sources provide their
+       // own anyway.
+       if (!rsurface.modelvertex3f_vertexbuffer && firstvertex != 0)
+               dynamicvertex = true;
+
        // a cvar to force the dynamic vertex path to be taken, for debugging
        if (r_batch_debugdynamicvertexpath.integer)
        {
@@ -7816,11 +7866,6 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                                for (i = 0;i < numtriangles*3;i++)
                                        rsurface.batchelement3s[i] = rsurface.batchelement3i[i];
                        }
-                       // upload buffer data for the copytriangles batch
-                       if (rsurface.batchelement3s)
-                               rsurface.batchelement3s_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(short[3]), rsurface.batchelement3s, R_BUFFERDATA_INDEX16, &rsurface.batchelement3s_bufferoffset);
-                       else if (rsurface.batchelement3i)
-                               rsurface.batchelement3i_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(int[3]), rsurface.batchelement3i, R_BUFFERDATA_INDEX32, &rsurface.batchelement3i_bufferoffset);
                }
                else
                {
@@ -8545,30 +8590,6 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                        }
                }
        }
-
-       // upload buffer data for the dynamic batch
-       if (rsurface.batchvertex3f)
-               rsurface.batchvertex3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f, R_BUFFERDATA_VERTEX, &rsurface.batchvertex3f_bufferoffset);
-       if (rsurface.batchsvector3f)
-               rsurface.batchsvector3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchsvector3f, R_BUFFERDATA_VERTEX, &rsurface.batchsvector3f_bufferoffset);
-       if (rsurface.batchtvector3f)
-               rsurface.batchtvector3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchtvector3f, R_BUFFERDATA_VERTEX, &rsurface.batchtvector3f_bufferoffset);
-       if (rsurface.batchnormal3f)
-               rsurface.batchnormal3f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f, R_BUFFERDATA_VERTEX, &rsurface.batchnormal3f_bufferoffset);
-       if (rsurface.batchlightmapcolor4f)
-               rsurface.batchlightmapcolor4f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[4]), rsurface.batchlightmapcolor4f, R_BUFFERDATA_VERTEX, &rsurface.batchlightmapcolor4f_bufferoffset);
-       if (rsurface.batchtexcoordtexture2f)
-               rsurface.batchtexcoordtexture2f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[2]), rsurface.batchtexcoordtexture2f, R_BUFFERDATA_VERTEX, &rsurface.batchtexcoordtexture2f_bufferoffset);
-       if (rsurface.batchtexcoordlightmap2f)
-               rsurface.batchtexcoordlightmap2f_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(float[2]), rsurface.batchtexcoordlightmap2f, R_BUFFERDATA_VERTEX, &rsurface.batchtexcoordlightmap2f_bufferoffset);
-       if (rsurface.batchskeletalindex4ub)
-               rsurface.batchskeletalindex4ub_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(unsigned char[4]), rsurface.batchskeletalindex4ub, R_BUFFERDATA_VERTEX, &rsurface.batchskeletalindex4ub_bufferoffset);
-       if (rsurface.batchskeletalweight4ub)
-               rsurface.batchskeletalweight4ub_vertexbuffer = R_BufferData_Store(rsurface.batchnumvertices * sizeof(unsigned char[4]), rsurface.batchskeletalweight4ub, R_BUFFERDATA_VERTEX, &rsurface.batchskeletalweight4ub_bufferoffset);
-       if (rsurface.batchelement3s)
-               rsurface.batchelement3s_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(short[3]), rsurface.batchelement3s, R_BUFFERDATA_INDEX16, &rsurface.batchelement3s_bufferoffset);
-       else if (rsurface.batchelement3i)
-               rsurface.batchelement3i_indexbuffer = R_BufferData_Store(rsurface.batchnumtriangles * sizeof(int[3]), rsurface.batchelement3i, R_BUFFERDATA_INDEX32, &rsurface.batchelement3i_bufferoffset);
 }
 
 void RSurf_DrawBatch(void)
@@ -8888,7 +8909,7 @@ static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface
        }
        switch (vid.renderpath)
        {
-       case RENDERPATH_GL20:
+       case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
                break;
@@ -9039,10 +9060,12 @@ static void R_DrawTextureSurfaceList_DepthOnly(int texturenumsurfaces, const msu
        RSurf_DrawBatch();
 }
 
-static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, qboolean prepass)
+static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, qboolean prepass, qboolean ui)
 {
        CHECKGLERROR
-       if (depthonly)
+       if (ui)
+               R_DrawModelTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
+       else if (depthonly)
                R_DrawTextureSurfaceList_DepthOnly(texturenumsurfaces, texturesurfacelist);
        else if (prepass)
        {
@@ -9071,7 +9094,7 @@ static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurf
        CHECKGLERROR
 }
 
-static void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly, qboolean prepass)
+static void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly, qboolean prepass, qboolean ui)
 {
        int i, j;
        texture_t *texture;
@@ -9112,7 +9135,7 @@ static void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const
                                ;
                }
                // render the range of surfaces
-               R_ProcessModelTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, prepass);
+               R_ProcessModelTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, prepass, ui);
        }
        R_FrameData_ReturnToMark();
 }
@@ -10060,7 +10083,7 @@ static void R_DrawDebugModel(void)
 
 int r_maxsurfacelist = 0;
 const msurface_t **r_surfacelist = NULL;
-void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean debug, qboolean prepass)
+void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean debug, qboolean prepass, qboolean ui)
 {
        int i, j, endj, flagsmask;
        dp_model_t *model = ent->model;
@@ -10132,6 +10155,12 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                                r_surfacelist[numsurfacelist++] = surfaces + j;
                }
        }
+       else if (ui)
+       {
+               // for ui we have to preserve the order of surfaces
+               for (i = 0; i < model->nummodelsurfaces; i++)
+                       r_surfacelist[numsurfacelist++] = surfaces + model->firstmodelsurface + i;
+       }
        else
        {
                // add all surfaces
@@ -10158,7 +10187,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                }
        }
 
-       R_QueueModelSurfaceList(ent, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, prepass);
+       R_QueueModelSurfaceList(ent, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, prepass, ui);
 
        // add to stats if desired
        if (r_speeds.integer && !skysurfaces && !depthonly)
@@ -10205,7 +10234,7 @@ void R_DebugLine(vec3_t start, vec3_t end)
                offsetx = 0.5f * width * vid_conwidth.value / vid.width;
                offsety = 0;
        }
-       surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, "white", 0, 0, MATERIALFLAG_VERTEXCOLOR), true);
+       surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, "white", 0, 0, MATERIALFLAG_WALL | MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX), true);
        e0 = Mod_Mesh_IndexForVertex(mod, surf, x1 - offsetx, y1 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r1, g1, b1, alpha1);
        e1 = Mod_Mesh_IndexForVertex(mod, surf, x2 - offsetx, y2 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r2, g2, b2, alpha2);
        e2 = Mod_Mesh_IndexForVertex(mod, surf, x2 + offsetx, y2 + offsety, 10, 0, 0, -1, 0, 0, 0, 0, r2, g2, b2, alpha2);