summaryrefslogtreecommitdiff
path: root/src/renderergl2/tr_vbo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderergl2/tr_vbo.cpp')
-rw-r--r--src/renderergl2/tr_vbo.cpp945
1 files changed, 945 insertions, 0 deletions
diff --git a/src/renderergl2/tr_vbo.cpp b/src/renderergl2/tr_vbo.cpp
new file mode 100644
index 0000000..58836cd
--- /dev/null
+++ b/src/renderergl2/tr_vbo.cpp
@@ -0,0 +1,945 @@
+/*
+===========================================================================
+Copyright (C) 2007-2009 Robert Beckebans <trebor_7@users.sourceforge.net>
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+// tr_vbo.c
+#include "tr_local.h"
+
+
+void R_VaoPackTangent(int16_t *out, vec4_t v)
+{
+ out[0] = v[0] * 32767.0f + (v[0] > 0.0f ? 0.5f : -0.5f);
+ out[1] = v[1] * 32767.0f + (v[1] > 0.0f ? 0.5f : -0.5f);
+ out[2] = v[2] * 32767.0f + (v[2] > 0.0f ? 0.5f : -0.5f);
+ out[3] = v[3] * 32767.0f + (v[3] > 0.0f ? 0.5f : -0.5f);
+}
+
+void R_VaoPackNormal(int16_t *out, vec3_t v)
+{
+ out[0] = v[0] * 32767.0f + (v[0] > 0.0f ? 0.5f : -0.5f);
+ out[1] = v[1] * 32767.0f + (v[1] > 0.0f ? 0.5f : -0.5f);
+ out[2] = v[2] * 32767.0f + (v[2] > 0.0f ? 0.5f : -0.5f);
+ out[3] = 0;
+}
+
+void R_VaoPackColor(uint16_t *out, vec4_t c)
+{
+ out[0] = c[0] * 65535.0f + 0.5f;
+ out[1] = c[1] * 65535.0f + 0.5f;
+ out[2] = c[2] * 65535.0f + 0.5f;
+ out[3] = c[3] * 65535.0f + 0.5f;
+}
+
+void R_VaoUnpackTangent(vec4_t v, int16_t *pack)
+{
+ v[0] = pack[0] / 32767.0f;
+ v[1] = pack[1] / 32767.0f;
+ v[2] = pack[2] / 32767.0f;
+ v[3] = pack[3] / 32767.0f;
+}
+
+void R_VaoUnpackNormal(vec3_t v, int16_t *pack)
+{
+ v[0] = pack[0] / 32767.0f;
+ v[1] = pack[1] / 32767.0f;
+ v[2] = pack[2] / 32767.0f;
+}
+
+void Vao_SetVertexPointers(vao_t *vao)
+{
+ int attribIndex;
+
+ // set vertex pointers
+ for (attribIndex = 0; attribIndex < ATTR_INDEX_COUNT; attribIndex++)
+ {
+ uint32_t attribBit = 1 << attribIndex;
+ vaoAttrib_t *vAtb = &vao->attribs[attribIndex];
+
+ if (vAtb->enabled)
+ {
+ qglVertexAttribPointer(attribIndex, vAtb->count, vAtb->type, vAtb->normalized, vAtb->stride, BUFFER_OFFSET(vAtb->offset));
+ if (glRefConfig.vertexArrayObject || !(glState.vertexAttribsEnabled & attribBit))
+ qglEnableVertexAttribArray(attribIndex);
+
+ if (!glRefConfig.vertexArrayObject || vao == tess.vao)
+ glState.vertexAttribsEnabled |= attribBit;
+ }
+ else
+ {
+ // don't disable vertex attribs when using vertex array objects
+ // Vao_SetVertexPointers is only called during init when using VAOs, and vertex attribs start disabled anyway
+ if (!glRefConfig.vertexArrayObject && (glState.vertexAttribsEnabled & attribBit))
+ qglDisableVertexAttribArray(attribIndex);
+
+ if (!glRefConfig.vertexArrayObject || vao == tess.vao)
+ glState.vertexAttribsEnabled &= ~attribBit;
+ }
+ }
+}
+
+/*
+============
+R_CreateVao
+============
+*/
+vao_t *R_CreateVao(const char *name, byte *vertexes, int vertexesSize, byte *indexes, int indexesSize, vaoUsage_t usage)
+{
+ vao_t *vao;
+ int glUsage;
+
+ switch (usage)
+ {
+ case VAO_USAGE_STATIC:
+ glUsage = GL_STATIC_DRAW;
+ break;
+
+ case VAO_USAGE_DYNAMIC:
+ glUsage = GL_DYNAMIC_DRAW;
+ break;
+
+ default:
+ Com_Error(ERR_FATAL, "bad vaoUsage_t given: %i", usage);
+ return NULL;
+ }
+
+ if(strlen(name) >= MAX_QPATH)
+ {
+ ri.Error(ERR_DROP, "R_CreateVao: \"%s\" is too long", name);
+ }
+
+ if ( tr.numVaos == MAX_VAOS ) {
+ ri.Error( ERR_DROP, "R_CreateVao: MAX_VAOS hit");
+ }
+
+ R_IssuePendingRenderCommands();
+
+ vao = tr.vaos[tr.numVaos] = (vao_t*)ri.Hunk_Alloc(sizeof(*vao), h_low);
+ tr.numVaos++;
+
+ memset(vao, 0, sizeof(*vao));
+
+ Q_strncpyz(vao->name, name, sizeof(vao->name));
+
+
+ if (glRefConfig.vertexArrayObject)
+ {
+ qglGenVertexArrays(1, &vao->vao);
+ qglBindVertexArray(vao->vao);
+ }
+
+
+ vao->vertexesSize = vertexesSize;
+
+ qglGenBuffers(1, &vao->vertexesVBO);
+
+ qglBindBuffer(GL_ARRAY_BUFFER, vao->vertexesVBO);
+ qglBufferData(GL_ARRAY_BUFFER, vertexesSize, vertexes, glUsage);
+
+
+ vao->indexesSize = indexesSize;
+
+ qglGenBuffers(1, &vao->indexesIBO);
+
+ qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vao->indexesIBO);
+ qglBufferData(GL_ELEMENT_ARRAY_BUFFER, indexesSize, indexes, glUsage);
+
+
+ glState.currentVao = vao;
+
+ GL_CheckErrors();
+
+ return vao;
+}
+
+/*
+============
+R_CreateVao2
+============
+*/
+vao_t *R_CreateVao2(const char *name, int numVertexes, srfVert_t *verts, int numIndexes, glIndex_t *indexes)
+{
+ vao_t *vao;
+ int i;
+
+ byte *data;
+ int dataSize;
+ int dataOfs;
+
+ int glUsage = GL_STATIC_DRAW;
+
+ if(!numVertexes || !numIndexes)
+ return NULL;
+
+ if(strlen(name) >= MAX_QPATH)
+ {
+ ri.Error(ERR_DROP, "R_CreateVao2: \"%s\" is too long", name);
+ }
+
+ if ( tr.numVaos == MAX_VAOS ) {
+ ri.Error( ERR_DROP, "R_CreateVao2: MAX_VAOS hit");
+ }
+
+ R_IssuePendingRenderCommands();
+
+ vao = tr.vaos[tr.numVaos] = (vao_t*)ri.Hunk_Alloc(sizeof(*vao), h_low);
+ tr.numVaos++;
+
+ memset(vao, 0, sizeof(*vao));
+
+ Q_strncpyz(vao->name, name, sizeof(vao->name));
+
+ // since these vertex attributes are never altered, interleave them
+ vao->attribs[ATTR_INDEX_POSITION ].enabled = 1;
+ vao->attribs[ATTR_INDEX_NORMAL ].enabled = 1;
+ vao->attribs[ATTR_INDEX_TANGENT ].enabled = 1;
+ vao->attribs[ATTR_INDEX_TEXCOORD ].enabled = 1;
+ vao->attribs[ATTR_INDEX_LIGHTCOORD ].enabled = 1;
+ vao->attribs[ATTR_INDEX_COLOR ].enabled = 1;
+ vao->attribs[ATTR_INDEX_LIGHTDIRECTION].enabled = 1;
+
+ vao->attribs[ATTR_INDEX_POSITION ].count = 3;
+ vao->attribs[ATTR_INDEX_NORMAL ].count = 4;
+ vao->attribs[ATTR_INDEX_TANGENT ].count = 4;
+ vao->attribs[ATTR_INDEX_TEXCOORD ].count = 2;
+ vao->attribs[ATTR_INDEX_LIGHTCOORD ].count = 2;
+ vao->attribs[ATTR_INDEX_COLOR ].count = 4;
+ vao->attribs[ATTR_INDEX_LIGHTDIRECTION].count = 4;
+
+ vao->attribs[ATTR_INDEX_POSITION ].type = GL_FLOAT;
+ vao->attribs[ATTR_INDEX_NORMAL ].type = GL_SHORT;
+ vao->attribs[ATTR_INDEX_TANGENT ].type = GL_SHORT;
+ vao->attribs[ATTR_INDEX_TEXCOORD ].type = GL_FLOAT;
+ vao->attribs[ATTR_INDEX_LIGHTCOORD ].type = GL_FLOAT;
+ vao->attribs[ATTR_INDEX_COLOR ].type = GL_UNSIGNED_SHORT;
+ vao->attribs[ATTR_INDEX_LIGHTDIRECTION].type = GL_SHORT;
+
+ vao->attribs[ATTR_INDEX_POSITION ].normalized = GL_FALSE;
+ vao->attribs[ATTR_INDEX_NORMAL ].normalized = GL_TRUE;
+ vao->attribs[ATTR_INDEX_TANGENT ].normalized = GL_TRUE;
+ vao->attribs[ATTR_INDEX_TEXCOORD ].normalized = GL_FALSE;
+ vao->attribs[ATTR_INDEX_LIGHTCOORD ].normalized = GL_FALSE;
+ vao->attribs[ATTR_INDEX_COLOR ].normalized = GL_TRUE;
+ vao->attribs[ATTR_INDEX_LIGHTDIRECTION].normalized = GL_TRUE;
+
+ vao->attribs[ATTR_INDEX_POSITION ].offset = 0; dataSize = sizeof(verts[0].xyz);
+ vao->attribs[ATTR_INDEX_NORMAL ].offset = dataSize; dataSize += sizeof(verts[0].normal);
+ vao->attribs[ATTR_INDEX_TANGENT ].offset = dataSize; dataSize += sizeof(verts[0].tangent);
+ vao->attribs[ATTR_INDEX_TEXCOORD ].offset = dataSize; dataSize += sizeof(verts[0].st);
+ vao->attribs[ATTR_INDEX_LIGHTCOORD ].offset = dataSize; dataSize += sizeof(verts[0].lightmap);
+ vao->attribs[ATTR_INDEX_COLOR ].offset = dataSize; dataSize += sizeof(verts[0].color);
+ vao->attribs[ATTR_INDEX_LIGHTDIRECTION].offset = dataSize; dataSize += sizeof(verts[0].lightdir);
+
+ vao->attribs[ATTR_INDEX_POSITION ].stride = dataSize;
+ vao->attribs[ATTR_INDEX_NORMAL ].stride = dataSize;
+ vao->attribs[ATTR_INDEX_TANGENT ].stride = dataSize;
+ vao->attribs[ATTR_INDEX_TEXCOORD ].stride = dataSize;
+ vao->attribs[ATTR_INDEX_LIGHTCOORD ].stride = dataSize;
+ vao->attribs[ATTR_INDEX_COLOR ].stride = dataSize;
+ vao->attribs[ATTR_INDEX_LIGHTDIRECTION].stride = dataSize;
+
+
+ if (glRefConfig.vertexArrayObject)
+ {
+ qglGenVertexArrays(1, &vao->vao);
+ qglBindVertexArray(vao->vao);
+ }
+
+
+ // create VBO
+ dataSize *= numVertexes;
+ data = (byte*)ri.Hunk_AllocateTempMemory(dataSize);
+ dataOfs = 0;
+
+ for (i = 0; i < numVertexes; i++)
+ {
+ // xyz
+ memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz));
+ dataOfs += sizeof(verts[i].xyz);
+
+ // normal
+ memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal));
+ dataOfs += sizeof(verts[i].normal);
+
+ // tangent
+ memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent));
+ dataOfs += sizeof(verts[i].tangent);
+
+ // texcoords
+ memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st));
+ dataOfs += sizeof(verts[i].st);
+
+ // lightmap texcoords
+ memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap));
+ dataOfs += sizeof(verts[i].lightmap);
+
+ // colors
+ memcpy(data + dataOfs, &verts[i].color, sizeof(verts[i].color));
+ dataOfs += sizeof(verts[i].color);
+
+ // light directions
+ memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir));
+ dataOfs += sizeof(verts[i].lightdir);
+ }
+
+ vao->vertexesSize = dataSize;
+
+ qglGenBuffers(1, &vao->vertexesVBO);
+
+ qglBindBuffer(GL_ARRAY_BUFFER, vao->vertexesVBO);
+ qglBufferData(GL_ARRAY_BUFFER, vao->vertexesSize, data, glUsage);
+
+
+ // create IBO
+ vao->indexesSize = numIndexes * sizeof(glIndex_t);
+
+ qglGenBuffers(1, &vao->indexesIBO);
+
+ qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vao->indexesIBO);
+ qglBufferData(GL_ELEMENT_ARRAY_BUFFER, vao->indexesSize, indexes, glUsage);
+
+
+ Vao_SetVertexPointers(vao);
+
+
+ glState.currentVao = vao;
+
+ GL_CheckErrors();
+
+ ri.Hunk_FreeTempMemory(data);
+
+ return vao;
+}
+
+
+/*
+============
+R_BindVao
+============
+*/
+void R_BindVao(vao_t * vao)
+{
+ if(!vao)
+ {
+ //R_BindNullVao();
+ ri.Error(ERR_DROP, "R_BindVao: NULL vao");
+ return;
+ }
+
+ if(r_logFile->integer)
+ {
+ // don't just call LogComment, or we will get a call to va() every frame!
+ GLimp_LogComment((char*)va("--- R_BindVao( %s ) ---\n", vao->name));
+ }
+
+ if(glState.currentVao != vao)
+ {
+ glState.currentVao = vao;
+
+ glState.vertexAttribsInterpolation = 0;
+ glState.vertexAnimation = false;
+ backEnd.pc.c_vaoBinds++;
+
+ if (glRefConfig.vertexArrayObject)
+ {
+ qglBindVertexArray(vao->vao);
+
+ // Intel Graphics doesn't save GL_ELEMENT_ARRAY_BUFFER binding with VAO binding.
+ if (glRefConfig.intelGraphics || vao == tess.vao)
+ qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vao->indexesIBO);
+
+ // tess VAO always has buffers bound
+ if (vao == tess.vao)
+ qglBindBuffer(GL_ARRAY_BUFFER, vao->vertexesVBO);
+ }
+ else
+ {
+ qglBindBuffer(GL_ARRAY_BUFFER, vao->vertexesVBO);
+ qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vao->indexesIBO);
+
+ // tess VAO doesn't have vertex pointers set until data is uploaded
+ if (vao != tess.vao)
+ Vao_SetVertexPointers(vao);
+ }
+ }
+}
+
+/*
+============
+R_BindNullVao
+============
+*/
+void R_BindNullVao(void)
+{
+ GLimp_LogComment("--- R_BindNullVao ---\n");
+
+ if(glState.currentVao)
+ {
+ if (glRefConfig.vertexArrayObject)
+ {
+ qglBindVertexArray(0);
+
+ // why you no save GL_ELEMENT_ARRAY_BUFFER binding, Intel?
+ if (1) qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ else
+ {
+ qglBindBuffer(GL_ARRAY_BUFFER, 0);
+ qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ glState.currentVao = NULL;
+ }
+
+ GL_CheckErrors();
+}
+
+
+/*
+============
+R_InitVaos
+============
+*/
+void R_InitVaos(void)
+{
+ int vertexesSize, indexesSize;
+ int offset;
+
+ ri.Printf(PRINT_ALL, "------- R_InitVaos -------\n");
+
+ tr.numVaos = 0;
+
+ vertexesSize = sizeof(tess.xyz[0]);
+ vertexesSize += sizeof(tess.normal[0]);
+ vertexesSize += sizeof(tess.tangent[0]);
+ vertexesSize += sizeof(tess.color[0]);
+ vertexesSize += sizeof(tess.texCoords[0]);
+ vertexesSize += sizeof(tess.lightCoords[0]);
+ vertexesSize += sizeof(tess.lightdir[0]);
+ vertexesSize *= SHADER_MAX_VERTEXES;
+
+ indexesSize = sizeof(tess.indexes[0]) * SHADER_MAX_INDEXES;
+
+ tess.vao = R_CreateVao("tessVertexArray_VAO", NULL, vertexesSize, NULL, indexesSize, VAO_USAGE_DYNAMIC);
+
+ offset = 0;
+
+ tess.vao->attribs[ATTR_INDEX_POSITION ].enabled = 1;
+ tess.vao->attribs[ATTR_INDEX_NORMAL ].enabled = 1;
+ tess.vao->attribs[ATTR_INDEX_TANGENT ].enabled = 1;
+ tess.vao->attribs[ATTR_INDEX_TEXCOORD ].enabled = 1;
+ tess.vao->attribs[ATTR_INDEX_LIGHTCOORD ].enabled = 1;
+ tess.vao->attribs[ATTR_INDEX_COLOR ].enabled = 1;
+ tess.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].enabled = 1;
+
+ tess.vao->attribs[ATTR_INDEX_POSITION ].count = 3;
+ tess.vao->attribs[ATTR_INDEX_NORMAL ].count = 4;
+ tess.vao->attribs[ATTR_INDEX_TANGENT ].count = 4;
+ tess.vao->attribs[ATTR_INDEX_TEXCOORD ].count = 2;
+ tess.vao->attribs[ATTR_INDEX_LIGHTCOORD ].count = 2;
+ tess.vao->attribs[ATTR_INDEX_COLOR ].count = 4;
+ tess.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].count = 4;
+
+ tess.vao->attribs[ATTR_INDEX_POSITION ].type = GL_FLOAT;
+ tess.vao->attribs[ATTR_INDEX_NORMAL ].type = GL_SHORT;
+ tess.vao->attribs[ATTR_INDEX_TANGENT ].type = GL_SHORT;
+ tess.vao->attribs[ATTR_INDEX_TEXCOORD ].type = GL_FLOAT;
+ tess.vao->attribs[ATTR_INDEX_LIGHTCOORD ].type = GL_FLOAT;
+ tess.vao->attribs[ATTR_INDEX_COLOR ].type = GL_UNSIGNED_SHORT;
+ tess.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].type = GL_SHORT;
+
+ tess.vao->attribs[ATTR_INDEX_POSITION ].normalized = GL_FALSE;
+ tess.vao->attribs[ATTR_INDEX_NORMAL ].normalized = GL_TRUE;
+ tess.vao->attribs[ATTR_INDEX_TANGENT ].normalized = GL_TRUE;
+ tess.vao->attribs[ATTR_INDEX_TEXCOORD ].normalized = GL_FALSE;
+ tess.vao->attribs[ATTR_INDEX_LIGHTCOORD ].normalized = GL_FALSE;
+ tess.vao->attribs[ATTR_INDEX_COLOR ].normalized = GL_TRUE;
+ tess.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].normalized = GL_TRUE;
+
+ tess.vao->attribs[ATTR_INDEX_POSITION ].offset = offset; offset += sizeof(tess.xyz[0]) * SHADER_MAX_VERTEXES;
+ tess.vao->attribs[ATTR_INDEX_NORMAL ].offset = offset; offset += sizeof(tess.normal[0]) * SHADER_MAX_VERTEXES;
+ tess.vao->attribs[ATTR_INDEX_TANGENT ].offset = offset; offset += sizeof(tess.tangent[0]) * SHADER_MAX_VERTEXES;
+ tess.vao->attribs[ATTR_INDEX_TEXCOORD ].offset = offset; offset += sizeof(tess.texCoords[0]) * SHADER_MAX_VERTEXES;
+ tess.vao->attribs[ATTR_INDEX_LIGHTCOORD ].offset = offset; offset += sizeof(tess.lightCoords[0]) * SHADER_MAX_VERTEXES;
+ tess.vao->attribs[ATTR_INDEX_COLOR ].offset = offset; offset += sizeof(tess.color[0]) * SHADER_MAX_VERTEXES;
+ tess.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].offset = offset;
+
+ tess.vao->attribs[ATTR_INDEX_POSITION ].stride = sizeof(tess.xyz[0]);
+ tess.vao->attribs[ATTR_INDEX_NORMAL ].stride = sizeof(tess.normal[0]);
+ tess.vao->attribs[ATTR_INDEX_TANGENT ].stride = sizeof(tess.tangent[0]);
+ tess.vao->attribs[ATTR_INDEX_TEXCOORD ].stride = sizeof(tess.texCoords[0]);
+ tess.vao->attribs[ATTR_INDEX_LIGHTCOORD ].stride = sizeof(tess.lightCoords[0]);
+ tess.vao->attribs[ATTR_INDEX_COLOR ].stride = sizeof(tess.color[0]);
+ tess.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].stride = sizeof(tess.lightdir[0]);
+
+ tess.attribPointers[ATTR_INDEX_POSITION] = tess.xyz;
+ tess.attribPointers[ATTR_INDEX_NORMAL] = tess.normal;
+ tess.attribPointers[ATTR_INDEX_TANGENT] = tess.tangent;
+ tess.attribPointers[ATTR_INDEX_TEXCOORD] = tess.texCoords;
+ tess.attribPointers[ATTR_INDEX_LIGHTCOORD] = tess.lightCoords;
+ tess.attribPointers[ATTR_INDEX_COLOR] = tess.color;
+ tess.attribPointers[ATTR_INDEX_LIGHTDIRECTION] = tess.lightdir;
+
+ Vao_SetVertexPointers(tess.vao);
+
+ R_BindNullVao();
+
+ VaoCache_Init();
+
+ GL_CheckErrors();
+}
+
+/*
+============
+R_ShutdownVaos
+============
+*/
+void R_ShutdownVaos(void)
+{
+ int i;
+ vao_t *vao;
+
+ ri.Printf(PRINT_ALL, "------- R_ShutdownVaos -------\n");
+
+ R_BindNullVao();
+
+ for(i = 0; i < tr.numVaos; i++)
+ {
+ vao = tr.vaos[i];
+
+ if(vao->vao)
+ qglDeleteVertexArrays(1, &vao->vao);
+
+ if(vao->vertexesVBO)
+ {
+ qglDeleteBuffers(1, &vao->vertexesVBO);
+ }
+
+ if(vao->indexesIBO)
+ {
+ qglDeleteBuffers(1, &vao->indexesIBO);
+ }
+ }
+
+ tr.numVaos = 0;
+}
+
+/*
+============
+R_VaoList_f
+============
+*/
+void R_VaoList_f(void)
+{
+ int i;
+ vao_t *vao;
+ int vertexesSize = 0;
+ int indexesSize = 0;
+
+ ri.Printf(PRINT_ALL, " size name\n");
+ ri.Printf(PRINT_ALL, "----------------------------------------------------------\n");
+
+ for(i = 0; i < tr.numVaos; i++)
+ {
+ vao = tr.vaos[i];
+
+ ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", vao->vertexesSize / (1024 * 1024),
+ (vao->vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024), vao->name);
+
+ vertexesSize += vao->vertexesSize;
+ }
+
+ for(i = 0; i < tr.numVaos; i++)
+ {
+ vao = tr.vaos[i];
+
+ ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", vao->indexesSize / (1024 * 1024),
+ (vao->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), vao->name);
+
+ indexesSize += vao->indexesSize;
+ }
+
+ ri.Printf(PRINT_ALL, " %i total VAOs\n", tr.numVaos);
+ ri.Printf(PRINT_ALL, " %d.%02d MB total vertices memory\n", vertexesSize / (1024 * 1024),
+ (vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024));
+ ri.Printf(PRINT_ALL, " %d.%02d MB total triangle indices memory\n", indexesSize / (1024 * 1024),
+ (indexesSize % (1024 * 1024)) * 100 / (1024 * 1024));
+}
+
+
+/*
+==============
+RB_UpdateTessVao
+
+Adapted from Tess_UpdateVBOs from xreal
+
+Update the default VAO to replace the client side vertex arrays
+==============
+*/
+void RB_UpdateTessVao(unsigned int attribBits)
+{
+ GLimp_LogComment("--- RB_UpdateTessVao ---\n");
+
+ backEnd.pc.c_dynamicVaoDraws++;
+
+ // update the default VAO
+ if(tess.numVertexes > 0 && tess.numVertexes <= SHADER_MAX_VERTEXES && tess.numIndexes > 0 && tess.numIndexes <= SHADER_MAX_INDEXES)
+ {
+ int attribIndex;
+ int attribUpload;
+
+ R_BindVao(tess.vao);
+
+ // orphan old vertex buffer so we don't stall on it
+ qglBufferData(GL_ARRAY_BUFFER, tess.vao->vertexesSize, NULL, GL_DYNAMIC_DRAW);
+
+ // if nothing to set, set everything
+ if(!(attribBits & ATTR_BITS))
+ attribBits = ATTR_BITS;
+
+ attribUpload = attribBits;
+
+ for (attribIndex = 0; attribIndex < ATTR_INDEX_COUNT; attribIndex++)
+ {
+ uint32_t attribBit = 1 << attribIndex;
+ vaoAttrib_t *vAtb = &tess.vao->attribs[attribIndex];
+
+ if (attribUpload & attribBit)
+ {
+ // note: tess has a VBO where stride == size
+ qglBufferSubData(GL_ARRAY_BUFFER, vAtb->offset, tess.numVertexes * vAtb->stride, tess.attribPointers[attribIndex]);
+ }
+
+ if (attribBits & attribBit)
+ {
+ if (!glRefConfig.vertexArrayObject)
+ qglVertexAttribPointer(attribIndex, vAtb->count, vAtb->type, vAtb->normalized, vAtb->stride, BUFFER_OFFSET(vAtb->offset));
+
+ if (!(glState.vertexAttribsEnabled & attribBit))
+ {
+ qglEnableVertexAttribArray(attribIndex);
+ glState.vertexAttribsEnabled |= attribBit;
+ }
+ }
+ else
+ {
+ if ((glState.vertexAttribsEnabled & attribBit))
+ {
+ qglDisableVertexAttribArray(attribIndex);
+ glState.vertexAttribsEnabled &= ~attribBit;
+ }
+ }
+ }
+
+ // orphan old index buffer so we don't stall on it
+ qglBufferData(GL_ELEMENT_ARRAY_BUFFER, tess.vao->indexesSize, NULL, GL_DYNAMIC_DRAW);
+
+ qglBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, tess.numIndexes * sizeof(tess.indexes[0]), tess.indexes);
+ }
+}
+
+typedef struct bufferCacheEntry_s
+{
+ void *data;
+ int size;
+ int bufferOffset;
+}
+bufferCacheEntry_t;
+
+typedef struct queuedSurface_s
+{
+ srfVert_t *vertexes;
+ int numVerts;
+ glIndex_t *indexes;
+ int numIndexes;
+}
+queuedSurface_t;
+
+#define VAOCACHE_MAX_BUFFERED_SURFACES (1 << 16)
+#define VAOCACHE_VERTEX_BUFFER_SIZE 12 * 1024 * 1024
+#define VAOCACHE_INDEX_BUFFER_SIZE 12 * 1024 * 1024
+
+#define VAOCACHE_MAX_QUEUED_VERTEXES (1 << 16)
+#define VAOCACHE_MAX_QUEUED_INDEXES (VAOCACHE_MAX_QUEUED_VERTEXES * 6 / 4)
+
+#define VAOCACHE_MAX_QUEUED_SURFACES 4096
+
+static struct
+{
+ vao_t *vao;
+ bufferCacheEntry_t indexEntries[VAOCACHE_MAX_BUFFERED_SURFACES];
+ int indexChainLengths[VAOCACHE_MAX_BUFFERED_SURFACES];
+ int numIndexEntries;
+ int vertexOffset;
+ int indexOffset;
+
+ srfVert_t vertexes[VAOCACHE_MAX_QUEUED_VERTEXES];
+ int vertexCommitSize;
+
+ glIndex_t indexes[VAOCACHE_MAX_QUEUED_INDEXES];
+ int indexCommitSize;
+
+ queuedSurface_t surfaceQueue[VAOCACHE_MAX_QUEUED_SURFACES];
+ int numSurfacesQueued;
+}
+vc = { 0 };
+
+void VaoCache_Commit(void)
+{
+ bufferCacheEntry_t *entry1;
+ queuedSurface_t *surf, *end = vc.surfaceQueue + vc.numSurfacesQueued;
+
+ R_BindVao(vc.vao);
+
+ // search entire cache for exact chain of indexes
+ // FIXME: use faster search
+ for (entry1 = vc.indexEntries; entry1 < vc.indexEntries + vc.numIndexEntries;)
+ {
+ int chainLength = vc.indexChainLengths[entry1 - vc.indexEntries];
+
+ if (chainLength == vc.numSurfacesQueued)
+ {
+ bufferCacheEntry_t *entry2 = entry1;
+ for (surf = vc.surfaceQueue; surf < end; surf++, entry2++)
+ {
+ if (surf->indexes != entry2->data || (surf->numIndexes * sizeof(glIndex_t)) != entry2->size)
+ break;
+ }
+
+ if (surf == end)
+ break;
+ }
+
+ entry1 += chainLength;
+ }
+
+ // if found, use that
+ if (entry1 < vc.indexEntries + vc.numIndexEntries)
+ {
+ tess.firstIndex = entry1->bufferOffset / sizeof(glIndex_t);
+ //ri.Printf(PRINT_ALL, "firstIndex %d numIndexes %d\n", tess.firstIndex, tess.numIndexes);
+ //ri.Printf(PRINT_ALL, "vc.numIndexEntries %d\n", vc.numIndexEntries);
+ }
+ // if not, rebuffer all the indexes but reuse any existing vertexes
+ else
+ {
+ srfVert_t *dstVertex = vc.vertexes;
+ glIndex_t *dstIndex = vc.indexes;
+ int *indexChainLength = vc.indexChainLengths + vc.numIndexEntries;
+
+ tess.firstIndex = vc.indexOffset / sizeof(glIndex_t);
+ vc.vertexCommitSize = 0;
+ vc.indexCommitSize = 0;
+ for (surf = vc.surfaceQueue; surf < end; surf++)
+ {
+ srfVert_t *srcVertex = surf->vertexes;
+ glIndex_t *srcIndex = surf->indexes;
+ int vertexesSize = surf->numVerts * sizeof(srfVert_t);
+ int indexesSize = surf->numIndexes * sizeof(glIndex_t);
+ int i, indexOffset = (vc.vertexOffset + vc.vertexCommitSize) / sizeof(srfVert_t);
+
+ for (i = 0; i < surf->numVerts; i++)
+ *dstVertex++ = *srcVertex++;
+
+ vc.vertexCommitSize += vertexesSize;
+
+ entry1 = vc.indexEntries + vc.numIndexEntries;
+ entry1->data = surf->indexes;
+ entry1->size = indexesSize;
+ entry1->bufferOffset = vc.indexOffset + vc.indexCommitSize;
+ vc.numIndexEntries++;
+
+ *indexChainLength++ = ((surf == vc.surfaceQueue) ? vc.numSurfacesQueued : 0);
+
+ for (i = 0; i < surf->numIndexes; i++)
+ *dstIndex++ = *srcIndex++ + indexOffset;
+
+ vc.indexCommitSize += indexesSize;
+ }
+
+ //ri.Printf(PRINT_ALL, "committing %d to %d, %d to %d\n", vc.vertexCommitSize, vc.vertexOffset, vc.indexCommitSize, vc.indexOffset);
+
+ if (vc.vertexCommitSize)
+ {
+ qglBindBuffer(GL_ARRAY_BUFFER, vc.vao->vertexesVBO);
+ qglBufferSubData(GL_ARRAY_BUFFER, vc.vertexOffset, vc.vertexCommitSize, vc.vertexes);
+ vc.vertexOffset += vc.vertexCommitSize;
+ }
+
+ if (vc.indexCommitSize)
+ {
+ qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vc.vao->indexesIBO);
+ qglBufferSubData(GL_ELEMENT_ARRAY_BUFFER, vc.indexOffset, vc.indexCommitSize, vc.indexes);
+ vc.indexOffset += vc.indexCommitSize;
+ }
+ }
+}
+
+void VaoCache_Init(void)
+{
+ srfVert_t vert;
+ int dataSize;
+
+ vc.vao = R_CreateVao("VaoCache", NULL, VAOCACHE_VERTEX_BUFFER_SIZE, NULL, VAOCACHE_INDEX_BUFFER_SIZE, VAO_USAGE_DYNAMIC);
+
+ vc.vao->attribs[ATTR_INDEX_POSITION].enabled = 1;
+ vc.vao->attribs[ATTR_INDEX_TEXCOORD].enabled = 1;
+ vc.vao->attribs[ATTR_INDEX_LIGHTCOORD].enabled = 1;
+ vc.vao->attribs[ATTR_INDEX_NORMAL].enabled = 1;
+ vc.vao->attribs[ATTR_INDEX_TANGENT].enabled = 1;
+ vc.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].enabled = 1;
+ vc.vao->attribs[ATTR_INDEX_COLOR].enabled = 1;
+
+ vc.vao->attribs[ATTR_INDEX_POSITION].count = 3;
+ vc.vao->attribs[ATTR_INDEX_TEXCOORD].count = 2;
+ vc.vao->attribs[ATTR_INDEX_LIGHTCOORD].count = 2;
+ vc.vao->attribs[ATTR_INDEX_NORMAL].count = 4;
+ vc.vao->attribs[ATTR_INDEX_TANGENT].count = 4;
+ vc.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].count = 4;
+ vc.vao->attribs[ATTR_INDEX_COLOR].count = 4;
+
+ vc.vao->attribs[ATTR_INDEX_POSITION].type = GL_FLOAT;
+ vc.vao->attribs[ATTR_INDEX_TEXCOORD].type = GL_FLOAT;
+ vc.vao->attribs[ATTR_INDEX_LIGHTCOORD].type = GL_FLOAT;
+ vc.vao->attribs[ATTR_INDEX_NORMAL].type = GL_SHORT;
+ vc.vao->attribs[ATTR_INDEX_TANGENT].type = GL_SHORT;
+ vc.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].type = GL_SHORT;
+ vc.vao->attribs[ATTR_INDEX_COLOR].type = GL_UNSIGNED_SHORT;
+
+ vc.vao->attribs[ATTR_INDEX_POSITION].normalized = GL_FALSE;
+ vc.vao->attribs[ATTR_INDEX_TEXCOORD].normalized = GL_FALSE;
+ vc.vao->attribs[ATTR_INDEX_LIGHTCOORD].normalized = GL_FALSE;
+ vc.vao->attribs[ATTR_INDEX_NORMAL].normalized = GL_TRUE;
+ vc.vao->attribs[ATTR_INDEX_TANGENT].normalized = GL_TRUE;
+ vc.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].normalized = GL_TRUE;
+ vc.vao->attribs[ATTR_INDEX_COLOR].normalized = GL_TRUE;
+
+ vc.vao->attribs[ATTR_INDEX_POSITION].offset = 0; dataSize = sizeof(vert.xyz);
+ vc.vao->attribs[ATTR_INDEX_TEXCOORD].offset = dataSize; dataSize += sizeof(vert.st);
+ vc.vao->attribs[ATTR_INDEX_LIGHTCOORD].offset = dataSize; dataSize += sizeof(vert.lightmap);
+ vc.vao->attribs[ATTR_INDEX_NORMAL].offset = dataSize; dataSize += sizeof(vert.normal);
+ vc.vao->attribs[ATTR_INDEX_TANGENT].offset = dataSize; dataSize += sizeof(vert.tangent);
+ vc.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].offset = dataSize; dataSize += sizeof(vert.lightdir);
+ vc.vao->attribs[ATTR_INDEX_COLOR].offset = dataSize; dataSize += sizeof(vert.color);
+
+ vc.vao->attribs[ATTR_INDEX_POSITION].stride = dataSize;
+ vc.vao->attribs[ATTR_INDEX_TEXCOORD].stride = dataSize;
+ vc.vao->attribs[ATTR_INDEX_LIGHTCOORD].stride = dataSize;
+ vc.vao->attribs[ATTR_INDEX_NORMAL].stride = dataSize;
+ vc.vao->attribs[ATTR_INDEX_TANGENT].stride = dataSize;
+ vc.vao->attribs[ATTR_INDEX_LIGHTDIRECTION].stride = dataSize;
+ vc.vao->attribs[ATTR_INDEX_COLOR].stride = dataSize;
+
+ Vao_SetVertexPointers(vc.vao);
+
+ vc.numIndexEntries = 0;
+ vc.vertexOffset = 0;
+ vc.indexOffset = 0;
+ vc.vertexCommitSize = 0;
+ vc.indexCommitSize = 0;
+ vc.numSurfacesQueued = 0;
+}
+
+void VaoCache_BindVao(void)
+{
+ R_BindVao(vc.vao);
+}
+
+void VaoCache_CheckAdd(bool *endSurface, bool *recycleVertexBuffer, bool *recycleIndexBuffer, int numVerts, int numIndexes)
+{
+ int vertexesSize = sizeof(srfVert_t) * numVerts;
+ int indexesSize = sizeof(glIndex_t) * numIndexes;
+
+ if (vc.vao->vertexesSize < vc.vertexOffset + vc.vertexCommitSize + vertexesSize)
+ {
+ //ri.Printf(PRINT_ALL, "out of space in vertex cache: %d < %d + %d + %d\n", vc.vao->vertexesSize, vc.vertexOffset, vc.vertexCommitSize, vertexesSize);
+ *recycleVertexBuffer = true;
+ *recycleIndexBuffer = true;
+ *endSurface = true;
+ }
+
+ if (vc.vao->indexesSize < vc.indexOffset + vc.indexCommitSize + indexesSize)
+ {
+ //ri.Printf(PRINT_ALL, "out of space in index cache\n");
+ *recycleIndexBuffer = true;
+ *endSurface = true;
+ }
+
+ if (vc.numIndexEntries + vc.numSurfacesQueued >= VAOCACHE_MAX_BUFFERED_SURFACES)
+ {
+ //ri.Printf(PRINT_ALL, "out of surfaces in index cache\n");
+ *recycleIndexBuffer = true;
+ *endSurface = true;
+ }
+
+ if (vc.numSurfacesQueued == VAOCACHE_MAX_QUEUED_SURFACES)
+ {
+ //ri.Printf(PRINT_ALL, "out of queued surfaces\n");
+ *endSurface = true;
+ }
+
+ if (VAOCACHE_MAX_QUEUED_VERTEXES * sizeof(srfVert_t) < vc.vertexCommitSize + vertexesSize)
+ {
+ //ri.Printf(PRINT_ALL, "out of queued vertexes\n");
+ *endSurface = true;
+ }
+
+ if (VAOCACHE_MAX_QUEUED_INDEXES * sizeof(glIndex_t) < vc.indexCommitSize + indexesSize)
+ {
+ //ri.Printf(PRINT_ALL, "out of queued indexes\n");
+ *endSurface = true;
+ }
+}
+
+void VaoCache_RecycleVertexBuffer(void)
+{
+ qglBindBuffer(GL_ARRAY_BUFFER, vc.vao->vertexesVBO);
+ qglBufferData(GL_ARRAY_BUFFER, vc.vao->vertexesSize, NULL, GL_DYNAMIC_DRAW);
+ vc.vertexOffset = 0;
+}
+
+void VaoCache_RecycleIndexBuffer(void)
+{
+ qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vc.vao->indexesIBO);
+ qglBufferData(GL_ELEMENT_ARRAY_BUFFER, vc.vao->indexesSize, NULL, GL_DYNAMIC_DRAW);
+ vc.indexOffset = 0;
+ vc.numIndexEntries = 0;
+}
+
+void VaoCache_InitNewSurfaceSet(void)
+{
+ vc.vertexCommitSize = 0;
+ vc.indexCommitSize = 0;
+ vc.numSurfacesQueued = 0;
+}
+
+void VaoCache_AddSurface(srfVert_t *verts, int numVerts, glIndex_t *indexes, int numIndexes)
+{
+ int vertexesSize = sizeof(srfVert_t) * numVerts;
+ int indexesSize = sizeof(glIndex_t) * numIndexes;
+ queuedSurface_t *queueEntry = vc.surfaceQueue + vc.numSurfacesQueued;
+ queueEntry->vertexes = verts;
+ queueEntry->numVerts = numVerts;
+ queueEntry->indexes = indexes;
+ queueEntry->numIndexes = numIndexes;
+ vc.numSurfacesQueued++;
+
+ vc.vertexCommitSize += vertexesSize;
+ vc.indexCommitSize += indexesSize;
+}