Files
starruler-linux/source/game/render/gl_vertexBuffer.cpp
T
kalvindukes 9481d94c98 Fix for "front() called on empty vector" Errors on debug builds.
Now using the C++11 vector.data() to fetch the proper pointer instead of $vector.front()
- Which fails on empty vectors because of the standard compiler setting _ITERATOR_DEBUG_LEVEL=2 when debugging
2018-09-26 19:23:46 +02:00

317 lines
8.8 KiB
C++

#include "vertexBuffer.h"
#include "compat/gl.h"
#include "main/references.h"
#include "compat/misc.h"
#include <unordered_map>
const unsigned maxVerts = 2048, maxInds = 4096;
unsigned drawnSteps = 0, bufferFlushes = 0;
namespace render {
unsigned vbFloatLimit = 24, vbMaxSteps = 200;
unsigned vbFlushCounts[FC_COUNT] = {0,0,0,0};
extern const RenderMesh* lastRenderedMesh;
extern float* shaderUniforms;
VertexBufferTCV tcv;
threads::Mutex bufferLock;
extern bool useVertexArrays();
inline bool getRequestSize(unsigned& indexCount, unsigned& vertCount, unsigned polygons, unsigned polyType) {
switch(polyType) {
case PT_Lines:
indexCount = polygons * 2;
vertCount = polygons * 2;
return true;
case PT_LineStrip:
indexCount = polygons * 2;
vertCount = polygons + 1;
return true;
case PT_Triangles:
indexCount = polygons * 3;
vertCount = polygons * 3;
return false;
case PT_Quads:
indexCount = polygons * 6;
vertCount = polygons * 4;
return false;
NO_DEFAULT
}
}
void buildIndices(std::vector<unsigned short>& indices, unsigned indexCount, unsigned prevVerts, unsigned polygons, unsigned polyType) {
//Fill out indices according to the polygon type
auto prevInds = indices.size();
indices.resize(prevInds + indexCount);
unsigned short* pIndices = &indices.at(0);
switch(polyType) {
case PT_Lines:
for(unsigned i = 0; i < polygons * 2; ++i)
pIndices[prevInds + i] = prevVerts + i;
break;
case PT_LineStrip:
for(unsigned i = 0; i < polygons; ++i) {
pIndices[prevInds + (i*2)] = prevVerts + i;
pIndices[prevInds + (i*2) + 1] = prevVerts + i + 1;
}
break;
case PT_Triangles:
for(unsigned i = 0; i < polygons * 3; ++i)
pIndices[prevInds + i] = prevVerts + i;
break;
case PT_Quads:
for(unsigned i = 0; i < polygons; ++i) {
auto off = prevInds + (i*6);
auto vOff = prevVerts + (i*4);
pIndices[off] = vOff;
pIndices[off+1] = vOff+2;
pIndices[off+2] = vOff+1;
pIndices[off+3] = vOff;
pIndices[off+4] = vOff+3;
pIndices[off+5] = vOff+2;
}
break;
NO_DEFAULT
}
}
void renderVertexBuffers() {
tcv.draw();
}
VertexBufferTCV::VertexBufferTCV()
: last(0)
{
vertices.reserve(maxVerts);
indices.reserve(maxInds);
steps.reserve(vbMaxSteps);
shaderBufferSize = 40000;
shaderBuffer = new float[shaderBufferSize];
pNextShaderBuffer = shaderBuffer;
}
VertexBufferTCV::~VertexBufferTCV() {
for(auto i = vertBuffer.begin(), end = vertBuffer.end(); i != end; ++i)
glDeleteBuffers(1, &*i);
for(auto i = indBuffer.begin(), end = indBuffer.end(); i != end; ++i)
glDeleteBuffers(1, &*i);
delete[] shaderBuffer;
}
void VertexBufferTCV::flushRetainingLast(FlushCause reason) {
RenderStep step = *last;
float* prevBuffer = pNextShaderBuffer;
//We may have added a step that we decided we can't render, we just pop the empty step
if(step.vertEnd == 0)
steps.pop_back();
draw(reason);
step.vertStart = 0;
step.vertEnd = 0;
step.indexOffset = 0;
if(step.shaderCache) {
memcpy(pNextShaderBuffer, step.shaderCache, (prevBuffer - step.shaderCache) * sizeof(float));
auto count = prevBuffer - step.shaderCache;
step.shaderCache = pNextShaderBuffer;
pNextShaderBuffer += count;
}
steps.push_back(step);
last = &steps.back();
}
void VertexBufferTCV::duplicateLast() {
if(steps.size() != steps.capacity()) {
steps.push_back(*last);
last = &steps.back();
last->indexOffset = (unsigned short)indices.size();
last->vertStart = (unsigned short)vertices.size();
}
else {
flushRetainingLast(FC_StepLimit);
}
}
VertexTCV* VertexBufferTCV::request(unsigned polygons, unsigned polyType) {
unsigned indexCount = 0;
unsigned vertCount = 0;
bool isLines = getRequestSize(indexCount, vertCount, polygons, polyType);
unsigned prevVerts = (unsigned)vertices.size();
if(prevVerts + vertCount > maxVerts
|| indices.size() + indexCount > maxInds)
{
flushRetainingLast(FC_VertexLimit);
last->lines = isLines;
prevVerts = 0;
}
if(last->lines == isLines) {
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
if(last->vertEnd)
last->vertEnd += vertCount;
else
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
else if(last->vertEnd != 0) {
duplicateLast();
last->lines = isLines;
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
else {
last->lines = isLines;
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
}
void VertexBufferTCV::draw(FlushCause reason) {
if(vertices.empty())
return;
if(vertBuffer.empty()) {
GLuint buffs[24];
glGenBuffers(24, buffs);
for(unsigned i = 0; i < 12; ++i)
vertBuffer.push_back(buffs[i]);
for(unsigned i = 12; i < 24; ++i)
indBuffer.push_back(buffs[i]);
}
{
lastRenderedMesh = reinterpret_cast<const render::RenderMesh*>(this);
if(useVertexArrays())
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indBuffer.front());
glBindBuffer(GL_ARRAY_BUFFER, vertBuffer.front());
indBuffer.push_back(indBuffer.front());
indBuffer.pop_front();
vertBuffer.push_back(vertBuffer.front());
vertBuffer.pop_front();
if(!useVertexArrays()) {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTCV), (void*)offsetof(VertexTCV, uv));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexTCV), (void*)offsetof(VertexTCV, col));
glVertexPointer(3, GL_FLOAT, sizeof(VertexTCV), (void*)offsetof(VertexTCV, pos));
}
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short),
&indices[0], GL_STREAM_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(VertexTCV), &vertices[0], GL_STREAM_DRAW);
drawnSteps += (unsigned)steps.size();
bufferFlushes += 1;
vbFlushCounts[reason] += 1;
RenderStep* pSteps = steps.data();
for(unsigned i = 0, cnt = (unsigned)steps.size(); i < cnt; ++i) {
auto& step = pSteps[i];
shaderUniforms = step.shaderCache;
devices.render->switchToRenderState(*step.material);
unsigned count = ((i+1) < cnt ? pSteps[i+1].indexOffset : (unsigned short)indices.size()) - step.indexOffset;
glDrawRangeElements(step.lines ? GL_LINES : GL_TRIANGLES, step.vertStart, step.vertEnd,
count, GL_UNSIGNED_SHORT, (void*)(step.indexOffset * sizeof(unsigned short)));
}
shaderUniforms = 0;
vertices.clear();
indices.clear();
steps.clear();
last = 0;
pNextShaderBuffer = shaderBuffer;
}
VertexBufferTCV* VertexBufferTCV::fetch(const RenderState* mat) {
auto& vbuff = tcv;
if(vbuff.last == 0 || vbuff.last->material != mat) {
if(vbuff.steps.size() == vbMaxSteps)
vbuff.draw(FC_StepLimit);
RenderStep step;
step.material = mat;
step.lines = false;
step.indexOffset = (unsigned short)tcv.indices.size();
step.vertStart = tcv.steps.empty() ? 0 : tcv.steps.back().vertEnd + 1;
step.vertEnd = 0;
if(mat->shader && !mat->shader->constant) {
step.shaderCache = tcv.pNextShaderBuffer;
mat->shader->saveDynamicVars(tcv.pNextShaderBuffer);
tcv.pNextShaderBuffer += mat->shader->dynamicFloats;
}
else {
step.shaderCache = 0;
}
vbuff.steps.push_back(step);
vbuff.last = &vbuff.steps.back();
}
else if(mat->shader && !mat->shader->constant) {
auto* buffer = vbuff.pNextShaderBuffer;
unsigned floats = mat->shader->dynamicFloats;
if(buffer + floats >= vbuff.shaderBuffer + vbuff.shaderBufferSize) {
//Flush the buffer and re-fetch the step
vbuff.draw(FC_ShaderLimit);
return fetch(mat);
}
else {
mat->shader->saveDynamicVars(buffer);
auto* lastStep = vbuff.last;
//When a shader uses a small enough amount of data, check to see if it's duplicated
//If it is, we can batch it into one step
if(floats > vbFloatLimit || memcmp(lastStep->shaderCache, buffer, floats * sizeof(float)) != 0) {
tcv.steps.push_back(*lastStep);
lastStep = &tcv.steps.back();
tcv.last = lastStep;
lastStep->shaderCache = buffer;
lastStep->indexOffset = (unsigned short)tcv.indices.size();
lastStep->vertStart = lastStep->vertEnd + 1;
lastStep->vertEnd = 0;
vbuff.pNextShaderBuffer += floats;
}
}
}
return &vbuff;
}
};