# Optimise terrain renderer.

Batch patch splats by texture match.
Use VBOs for patch indices.
Fixes #722.

This was SVN commit r9053.
This commit is contained in:
Ykkrosh 2011-03-13 19:06:33 +00:00
parent 2f28b07356
commit fc2c54c39f
7 changed files with 401 additions and 157 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2010 Wildfire Games /* Copyright (c) 2011 Wildfire Games
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@ -63,6 +63,10 @@ actually supported).
// GL_EXT_draw_range_elements / GL1.2: // GL_EXT_draw_range_elements / GL1.2:
FUNC2(void, glDrawRangeElementsEXT, glDrawRangeElements, "1.2", (GLenum, GLuint, GLuint, GLsizei, GLenum, GLvoid*)) FUNC2(void, glDrawRangeElementsEXT, glDrawRangeElements, "1.2", (GLenum, GLuint, GLuint, GLsizei, GLenum, GLvoid*))
// GL_EXT_multi_draw_arrays / GL1.4
FUNC2(void, glMultiDrawArraysEXT, glMultiDrawArrays, "1.4", (GLenum, GLint*, GLsizei*, GLsizei))
FUNC2(void, glMultiDrawElementsEXT, glMultiDrawElements, "1.4", (GLenum, GLsizei*, GLenum, GLvoid**, GLsizei))
// GL_ARB_multitexture / GL1.3: // GL_ARB_multitexture / GL1.3:
FUNC2(void, glMultiTexCoord2fARB, glMultiTexCoord2f, "1.3", (int, float, float)) FUNC2(void, glMultiTexCoord2fARB, glMultiTexCoord2f, "1.3", (int, float, float))
FUNC2(void, glMultiTexCoord3fARB, glMultiTexCoord3f, "1.3", (int, float, float, float)) FUNC2(void, glMultiTexCoord3fARB, glMultiTexCoord3f, "1.3", (int, float, float, float))

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2010 Wildfire Games /* Copyright (c) 2011 Wildfire Games
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@ -288,6 +288,24 @@ static void GL_CALL_CONV dummy_glDrawRangeElementsEXT(GLenum mode, GLuint, GLuin
glDrawElements(mode, count, type, indices); glDrawElements(mode, count, type, indices);
} }
static void GL_CALL_CONV dummy_glMultiDrawArraysEXT(GLenum mode, GLint* first, GLsizei* count, GLsizei primcount)
{
for (GLsizei i = 0; i < primcount; ++i)
{
if (count[i] > 0)
glDrawArrays(mode, first[i], count[i]);
}
}
static void GL_CALL_CONV dummy_glMultiDrawElementsEXT(GLenum mode, GLsizei* count, GLenum type, GLvoid** indices, GLsizei primcount)
{
for (GLsizei i = 0; i < primcount; ++i)
{
if (count[i] > 0)
glDrawElements(mode, count[i], type, indices[i]);
}
}
static void GL_CALL_CONV dummy_glActiveTextureARB(int) static void GL_CALL_CONV dummy_glActiveTextureARB(int)
{ {
} }
@ -301,6 +319,11 @@ static void GL_CALL_CONV dummy_glMultiTexCoord2fARB(int, float s, float t)
glTexCoord2f(s, t); glTexCoord2f(s, t);
} }
static void GL_CALL_CONV dummy_glMultiTexCoord3fARB(int, float s, float t, float r)
{
glTexCoord3f(s, t, r);
}
static void importExtensionFunctions() static void importExtensionFunctions()
{ {
// It should be safe to load the ARB function pointers even if the // It should be safe to load the ARB function pointers even if the
@ -323,15 +346,26 @@ static void importExtensionFunctions()
#undef FUNC23 #undef FUNC23
#undef FUNC #undef FUNC
// fall back to the dummy functions // fall back to the dummy functions when extensions (or equivalent core support) are missing
if(!pglDrawRangeElementsEXT)
if(!ogl_HaveExtension("GL_EXT_draw_range_elements"))
{
pglDrawRangeElementsEXT = &dummy_glDrawRangeElementsEXT; pglDrawRangeElementsEXT = &dummy_glDrawRangeElementsEXT;
if(!pglActiveTextureARB) }
if(!ogl_HaveExtension("GL_EXT_multi_draw_arrays"))
{
pglMultiDrawArraysEXT = &dummy_glMultiDrawArraysEXT;
pglMultiDrawElementsEXT = &dummy_glMultiDrawElementsEXT;
}
if(!ogl_HaveExtension("GL_ARB_multitexture"))
{
pglActiveTextureARB = &dummy_glActiveTextureARB; pglActiveTextureARB = &dummy_glActiveTextureARB;
if(!pglClientActiveTextureARB)
pglClientActiveTextureARB = &dummy_glClientActiveTextureARB; pglClientActiveTextureARB = &dummy_glClientActiveTextureARB;
if(!pglMultiTexCoord2fARB)
pglMultiTexCoord2fARB = &dummy_glMultiTexCoord2fARB; pglMultiTexCoord2fARB = &dummy_glMultiTexCoord2fARB;
pglMultiTexCoord3fARB = &dummy_glMultiTexCoord3fARB;
}
} }

View File

@ -54,7 +54,7 @@ const ssize_t BlendOffsets[9][2] = {
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
// CPatchRData constructor // CPatchRData constructor
CPatchRData::CPatchRData(CPatch* patch) : CPatchRData::CPatchRData(CPatch* patch) :
m_Patch(patch), m_VBBase(0), m_VBSides(0), m_VBBlends(0), m_Vertices(0) m_Patch(patch), m_VBBase(0), m_VBBaseIndices(0), m_VBSides(0), m_VBBlends(0), m_Vertices(0)
{ {
debug_assert(patch); debug_assert(patch);
Build(); Build();
@ -68,6 +68,7 @@ CPatchRData::~CPatchRData()
delete[] m_Vertices; delete[] m_Vertices;
// release vertex buffer chunks // release vertex buffer chunks
if (m_VBBase) g_VBMan.Release(m_VBBase); if (m_VBBase) g_VBMan.Release(m_VBBase);
if (m_VBBaseIndices) g_VBMan.Release(m_VBBaseIndices);
if (m_VBSides) g_VBMan.Release(m_VBSides); if (m_VBSides) g_VBMan.Release(m_VBSides);
if (m_VBBlends) g_VBMan.Release(m_VBBlends); if (m_VBBlends) g_VBMan.Release(m_VBBlends);
} }
@ -203,6 +204,8 @@ void CPatchRData::BuildBlends()
// Given the blend stack per tile, we want to batch together as many blends as possible. // Given the blend stack per tile, we want to batch together as many blends as possible.
// Group them into a series of layers (each of which has a single texture): // Group them into a series of layers (each of which has a single texture):
// (This is effectively a topological sort / linearisation of the partial order induced
// by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
std::vector<SBlendLayer> blendLayers; std::vector<SBlendLayer> blendLayers;
@ -383,11 +386,13 @@ void CPatchRData::BuildIndices()
// number of vertices in each direction in each patch // number of vertices in each direction in each patch
ssize_t vsize=PATCH_SIZE+1; ssize_t vsize=PATCH_SIZE+1;
// release existing indices and bins std::vector<unsigned short> indices;
m_Indices.clear(); indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
// release existing splats
m_Splats.clear(); m_Splats.clear();
// build grid of textures on this patch and boundaries of adjacent patches // build grid of textures on this patch
std::vector<CTerrainTextureEntry*> textures; std::vector<CTerrainTextureEntry*> textures;
CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE]; CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
for (ssize_t j=0;j<PATCH_SIZE;j++) { for (ssize_t j=0;j<PATCH_SIZE;j++) {
@ -404,25 +409,39 @@ void CPatchRData::BuildIndices()
m_Splats.resize(textures.size()); m_Splats.resize(textures.size());
// build indices for base splats // build indices for base splats
size_t base=m_VBBase->m_Index; size_t base=m_VBBase->m_Index;
debug_assert(base + vsize*vsize < 65536); // mustn't overflow u16 indexes
for (size_t i=0;i<m_Splats.size();i++) { for (size_t i=0;i<m_Splats.size();i++) {
CTerrainTextureEntry* tex=textures[i]; CTerrainTextureEntry* tex=textures[i];
SSplat& splat=m_Splats[i]; SSplat& splat=m_Splats[i];
splat.m_Texture=tex; splat.m_Texture=tex;
splat.m_IndexStart=m_Indices.size(); splat.m_IndexStart=indices.size();
for (ssize_t j=0;j<PATCH_SIZE;j++) { for (ssize_t j=0;j<PATCH_SIZE;j++) {
for (ssize_t i=0;i<PATCH_SIZE;i++) { for (ssize_t i=0;i<PATCH_SIZE;i++) {
if (texgrid[j][i]==tex){ if (texgrid[j][i]==tex){
m_Indices.push_back(u16(((j+0)*vsize+(i+0))+base)); indices.push_back(u16(((j+0)*vsize+(i+0))+base));
m_Indices.push_back(u16(((j+0)*vsize+(i+1))+base)); indices.push_back(u16(((j+0)*vsize+(i+1))+base));
m_Indices.push_back(u16(((j+1)*vsize+(i+1))+base)); indices.push_back(u16(((j+1)*vsize+(i+1))+base));
m_Indices.push_back(u16(((j+1)*vsize+(i+0))+base)); indices.push_back(u16(((j+1)*vsize+(i+0))+base));
} }
} }
} }
splat.m_IndexCount=m_Indices.size()-splat.m_IndexStart; splat.m_IndexCount=indices.size()-splat.m_IndexStart;
} }
// Release existing vertex buffer chunk
if (m_VBBaseIndices)
{
g_VBMan.Release(m_VBBaseIndices);
m_VBBaseIndices = 0;
}
debug_assert(indices.size());
// Construct vertex buffer
m_VBBaseIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices, &indices[0]);
} }
@ -578,137 +597,297 @@ void CPatchRData::Update()
} }
} }
void CPatchRData::RenderBase() void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches)
{ {
debug_assert(m_UpdateFlags==0); // Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
typedef std::pair<std::vector<GLint>, std::vector<void*> > BatchElements;
SBaseVertex *base=(SBaseVertex *)m_VBBase->m_Owner->Bind(); // Group batches by index buffer
typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
// setup data pointers // Group batches by vertex buffer
GLsizei stride = sizeof(SBaseVertex); typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]);
// render each splat // Group batches by texture
for (size_t i=0;i<m_Splats.size();i++) { typedef std::map<CTerrainTextureEntry*, VertexBufferBatches> TextureBatches;
SSplat& splat=m_Splats[i];
if (splat.m_Texture) TextureBatches batches;
splat.m_Texture->GetTexture()->Bind();
PROFILE_START("compute batches");
// Collect all the patches' base splats into their appropriate batches
for (size_t i = 0; i < patches.size(); ++i)
{
CPatchRData* patch = patches[i];
for (size_t j = 0; j < patch->m_Splats.size(); ++j)
{
SSplat& splat = patch->m_Splats[j];
BatchElements& batch = batches[splat.m_Texture][patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
batch.first.push_back(splat.m_IndexCount);
u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart));
}
}
PROFILE_END("compute batches");
// Render each batch
for (TextureBatches::iterator itt = batches.begin(); itt != batches.end(); ++itt)
{
if (itt->first)
itt->first->GetTexture()->Bind();
else else
g_Renderer.GetTextureManager().GetErrorTexture()->Bind(); g_Renderer.GetTextureManager().GetErrorTexture()->Bind();
if (!g_Renderer.m_SkipSubmit) { for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
glDrawElements(GL_QUADS, (GLsizei)splat.m_IndexCount, {
GL_UNSIGNED_SHORT, &m_Indices[splat.m_IndexStart]); GLsizei stride = sizeof(SBaseVertex);
SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]);
for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
{
it->first->Bind();
BatchElements& batch = it->second;
if (!g_Renderer.m_SkipSubmit)
{
pglMultiDrawElementsEXT(GL_QUADS, &batch.first[0], GL_UNSIGNED_SHORT,
(GLvoid**)&batch.second[0], batch.first.size());
}
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 2;
}
}
}
CVertexBuffer::Unbind();
}
/**
* Helper structure for RenderBlends.
*/
struct SBlendBatch
{
CTerrainTextureEntry* m_Texture;
// Each multidraw batch has a list of start vertex offsets, and a list of vertex counts
typedef std::pair<std::vector<GLint>, std::vector<GLsizei> > BatchElements;
// Group batches by vertex buffer
typedef std::map<CVertexBuffer*, BatchElements> VertexBufferBatches;
VertexBufferBatches m_Batches;
};
void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
{
std::vector<SBlendBatch> batches;
PROFILE_START("compute batches");
// Reserve an arbitrary size that's probably big enough in most cases,
// to avoid heavy reallocations
batches.reserve(256);
std::vector<std::pair<CVertexBuffer*, std::vector<SSplat> > > blendStacks;
blendStacks.reserve(patches.size());
// Extract all the blend splats from each patch
for (size_t i = 0; i < patches.size(); ++i)
{
CPatchRData* patch = patches[i];
if (!patch->m_BlendSplats.empty())
{
blendStacks.push_back(std::make_pair(patch->m_VBBlends->m_Owner, patch->m_BlendSplats));
// Reverse the splats so the first to be rendered is at the back of the list
std::reverse(blendStacks.back().second.begin(), blendStacks.back().second.end());
}
}
// Rearrange the collection of splats to be grouped by texture, preserving
// order of splats within each patch:
// (This is exactly the same algorithm used in CPatchRData::BuildBlends,
// but applied to patch-sized splats rather than to tile-sized splats;
// see that function for comments on the algorithm.)
while (true)
{
if (!batches.empty())
{
CTerrainTextureEntry* tex = batches.back().m_Texture;
for (size_t k = 0; k < blendStacks.size(); ++k)
{
if (!blendStacks[k].second.empty() && blendStacks[k].second.back().m_Texture == tex)
{
SBlendBatch::BatchElements& batch = batches.back().m_Batches[blendStacks[k].first];
batch.first.push_back(blendStacks[k].second.back().m_IndexStart);
batch.second.push_back(blendStacks[k].second.back().m_IndexCount);
blendStacks[k].second.pop_back();
}
}
} }
// bump stats CTerrainTextureEntry* bestTex = NULL;
g_Renderer.m_Stats.m_DrawCalls++; size_t bestStackSize = 0;
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
for (size_t k = 0; k < blendStacks.size(); ++k)
{
if (blendStacks[k].second.size() > bestStackSize)
{
bestStackSize = blendStacks[k].second.size();
bestTex = blendStacks[k].second.back().m_Texture;
}
}
if (bestStackSize == 0)
break;
SBlendBatch layer;
layer.m_Texture = bestTex;
batches.push_back(layer);
} }
CVertexBuffer::Unbind(); PROFILE_END("compute batches");
}
void CPatchRData::RenderStreams(int streamflags) CVertexBuffer* lastVB = NULL;
{
debug_assert(m_UpdateFlags==0);
SBaseVertex* base=(SBaseVertex *)m_VBBase->m_Owner->Bind(); for (std::vector<SBlendBatch>::iterator itt = batches.begin(); itt != batches.end(); ++itt)
// setup data pointers
GLsizei stride = sizeof(SBaseVertex);
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position);
if (streamflags & STREAM_UV0)
{ {
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs); if (itt->m_Texture)
} itt->m_Texture->GetTexture()->Bind();
else if (streamflags & STREAM_POSTOUV0)
{
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
}
if (streamflags & STREAM_POSTOUV1)
{
pglClientActiveTextureARB(GL_TEXTURE1);
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
pglClientActiveTextureARB(GL_TEXTURE0);
}
if (streamflags & STREAM_POSTOUV2)
{
pglClientActiveTextureARB(GL_TEXTURE2);
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
pglClientActiveTextureARB(GL_TEXTURE0);
}
if (streamflags & STREAM_POSTOUV3)
{
pglClientActiveTextureARB(GL_TEXTURE3);
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
pglClientActiveTextureARB(GL_TEXTURE0);
}
if (streamflags & STREAM_COLOR)
{
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
}
// render all base splats at once
if (!g_Renderer.m_SkipSubmit) {
glDrawElements(GL_QUADS,(GLsizei)m_Indices.size(),GL_UNSIGNED_SHORT,&m_Indices[0]);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=m_Indices.size()/2;
CVertexBuffer::Unbind();
}
void CPatchRData::RenderBlends()
{
debug_assert(m_UpdateFlags==0);
if (m_BlendVertices.empty())
return;
u8* base=m_VBBlends->m_Owner->Bind();
// setup data pointers
GLsizei stride=sizeof(SBlendVertex);
// ((GCC warns about offsetof: SBlendVertex contains a CVector3D which has
// a constructor, and so is not a POD type, and so offsetof is theoretically
// invalid - see http://gcc.gnu.org/ml/gcc/2003-11/msg00281.html - but it
// doesn't seem to be worth changing this code since it works anyway.))
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position));
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0]));
pglClientActiveTextureARB(GL_TEXTURE1);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0]));
for (size_t i=0;i<m_BlendSplats.size();i++) {
SSplat& splat=m_BlendSplats[i];
if (splat.m_Texture)
splat.m_Texture->GetTexture()->Bind();
else else
g_Renderer.GetTextureManager().GetErrorTexture()->Bind(); g_Renderer.GetTextureManager().GetErrorTexture()->Bind();
if (!g_Renderer.m_SkipSubmit) { for (SBlendBatch::VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
{
// Rebind the VB only if it changed since the last batch
if (itv->first != lastVB)
{
lastVB = itv->first;
GLsizei stride = sizeof(SBlendVertex);
SBlendVertex *base = (SBlendVertex *)itv->first->Bind();
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]);
pglClientActiveTextureARB(GL_TEXTURE1);
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_AlphaUVs[0]);
}
SBlendBatch::BatchElements& batch = itv->second;
// Since every blend vertex likely has distinct UV even if they // Since every blend vertex likely has distinct UV even if they
// share positions, there's no value in using indexed arrays, so // share positions, there's no value in using indexed arrays, so
// we just use DrawArrays instead of DrawElements // we just use DrawArrays instead of DrawElements
glDrawArrays(GL_QUADS, (GLint)splat.m_IndexStart, (GLsizei)splat.m_IndexCount); if (!g_Renderer.m_SkipSubmit)
pglMultiDrawArraysEXT(GL_QUADS, &batch.first[0], &batch.second[0], batch.first.size());
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_BlendSplats++;
g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.second.begin(), batch.second.end(), 0) / 2;
}
}
pglClientActiveTextureARB(GL_TEXTURE0);
CVertexBuffer::Unbind();
}
void CPatchRData::RenderStreams(const std::vector<CPatchRData*>& patches, int streamflags)
{
// Each batch has a list of index counts, and a list of pointers-to-first-indexes
typedef std::pair<std::vector<GLint>, std::vector<void*> > BatchElements;
// Group batches by index buffer
typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
// Group batches by vertex buffer
typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
VertexBufferBatches batches;
PROFILE_START("compute batches");
// Collect all the patches into their appropriate batches
for (size_t i = 0; i < patches.size(); ++i)
{
CPatchRData* patch = patches[i];
BatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
batch.first.push_back(patch->m_VBBaseIndices->m_Count);
u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index));
}
PROFILE_END("compute batches");
// Render each batch
for (VertexBufferBatches::iterator itv = batches.begin(); itv != batches.end(); ++itv)
{
GLsizei stride = sizeof(SBaseVertex);
SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position);
if (streamflags & STREAM_UV0)
{
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs);
}
if (streamflags & STREAM_POSTOUV0)
{
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
}
if (streamflags & STREAM_POSTOUV1)
{
pglClientActiveTextureARB(GL_TEXTURE1);
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
}
if (streamflags & STREAM_POSTOUV2)
{
pglClientActiveTextureARB(GL_TEXTURE2);
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
}
if (streamflags & STREAM_POSTOUV3)
{
pglClientActiveTextureARB(GL_TEXTURE3);
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
}
if (streamflags & STREAM_COLOR)
{
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
} }
// bump stats for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
g_Renderer.m_Stats.m_DrawCalls++; {
g_Renderer.m_Stats.m_BlendSplats++; it->first->Bind();
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
BatchElements& batch = it->second;
if (!g_Renderer.m_SkipSubmit)
{
pglMultiDrawElementsEXT(GL_QUADS, &batch.first[0], GL_UNSIGNED_SHORT,
(GLvoid**)&batch.second[0], batch.first.size());
}
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 2;
}
} }
pglClientActiveTextureARB(GL_TEXTURE0);
CVertexBuffer::Unbind(); CVertexBuffer::Unbind();
} }

View File

@ -37,13 +37,14 @@ public:
~CPatchRData(); ~CPatchRData();
void Update(); void Update();
void RenderBase();
void RenderBlends();
void RenderOutline(); void RenderOutline();
void RenderStreams(int streamflags);
void RenderSides(); void RenderSides();
void RenderPriorities(); void RenderPriorities();
static void RenderBases(const std::vector<CPatchRData*>& patches);
static void RenderBlends(const std::vector<CPatchRData*>& patches);
static void RenderStreams(const std::vector<CPatchRData*>& patches, int streamflags);
private: private:
struct SSplat { struct SSplat {
SSplat() : m_Texture(0), m_IndexCount(0) {} SSplat() : m_Texture(0), m_IndexCount(0) {}
@ -63,12 +64,18 @@ private:
SColor4ub m_DiffuseColor; SColor4ub m_DiffuseColor;
// vertex uvs for base texture // vertex uvs for base texture
float m_UVs[2]; float m_UVs[2];
// add some padding since VBOs prefer power-of-two sizes
u32 m_Padding[2];
}; };
cassert(sizeof(SBaseVertex) == 32);
struct SSideVertex { struct SSideVertex {
// vertex position // vertex position
CVector3D m_Position; CVector3D m_Position;
// add some padding
u32 m_Padding[1];
}; };
cassert(sizeof(SSideVertex) == 16);
struct SBlendVertex { struct SBlendVertex {
// vertex position // vertex position
@ -77,7 +84,10 @@ private:
float m_UVs[2]; float m_UVs[2];
// vertex uvs for alpha texture // vertex uvs for alpha texture
float m_AlphaUVs[2]; float m_AlphaUVs[2];
// add some padding
u32 m_Padding[1];
}; };
cassert(sizeof(SBlendVertex) == 32);
// build this renderdata object // build this renderdata object
void Build(); void Build();
@ -97,6 +107,9 @@ private:
// vertex buffer handle for base vertices // vertex buffer handle for base vertices
CVertexBuffer::VBChunk* m_VBBase; CVertexBuffer::VBChunk* m_VBBase;
// vertex buffer handle for base vertex indices
CVertexBuffer::VBChunk* m_VBBaseIndices;
// vertex buffer handle for side vertices // vertex buffer handle for side vertices
CVertexBuffer::VBChunk* m_VBSides; CVertexBuffer::VBChunk* m_VBSides;
@ -106,9 +119,6 @@ private:
// patch render vertices // patch render vertices
SBaseVertex* m_Vertices; SBaseVertex* m_Vertices;
// indices into base vertices for the base splats
std::vector<unsigned short> m_Indices;
// list of base splats to apply to this patch // list of base splats to apply to this patch
std::vector<SSplat> m_Splats; std::vector<SSplat> m_Splats;

View File

@ -155,15 +155,22 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
{ {
debug_assert(m->phase == Phase_Render); debug_assert(m->phase == Phase_Render);
std::vector<CPatchRData*> patchRDatas;
patchRDatas.reserve(m->visiblePatches.size());
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
patchRDatas.push_back(static_cast<CPatchRData*>(m->visiblePatches[i]->GetRenderData()));
// render the solid black sides of the map first // render the solid black sides of the map first
g_Renderer.BindTexture(0, 0); g_Renderer.BindTexture(0, 0);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(0, 0, 0); glColor3f(0, 0, 0);
for(size_t i = 0; i < m->visiblePatches.size(); ++i) PROFILE_START("render terrain sides");
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{ {
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderSides(); patchdata->RenderSides();
} }
PROFILE_END("render terrain sides");
// switch on required client states // switch on required client states
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@ -184,12 +191,10 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
static const float one[4] = { 1.f, 1.f, 1.f, 1.f }; static const float one[4] = { 1.f, 1.f, 1.f, 1.f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
for(size_t i = 0; i < m->visiblePatches.size(); ++i) PROFILE_START("render terrain base");
{ CPatchRData::RenderBases(patchRDatas);
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); PROFILE_END("render terrain base");
patchdata->RenderBase();
}
// render blends // render blends
// switch on the composite alpha map texture // switch on the composite alpha map texture
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1); (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
@ -216,12 +221,10 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
glDepthMask(0); glDepthMask(0);
// render blend passes for each patch // render blend passes for each patch
for(size_t i = 0; i < m->visiblePatches.size(); ++i) PROFILE_START("render terrain blends");
{ CPatchRData::RenderBlends(patchRDatas);
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); PROFILE_END("render terrain blends");
patchdata->RenderBlends();
}
// Disable second texcoord array // Disable second texcoord array
pglClientActiveTextureARB(GL_TEXTURE1); pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@ -412,11 +415,9 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
pglActiveTextureARB(GL_TEXTURE0); pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0);
for (size_t i = 0; i < m->visiblePatches.size(); ++i) PROFILE_START("render terrain streams");
{ CPatchRData::RenderStreams(patchRDatas, streamflags);
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); PROFILE_END("render terrain streams");
patchdata->RenderStreams(streamflags);
}
glMatrixMode(GL_TEXTURE); glMatrixMode(GL_TEXTURE);
glLoadIdentity(); glLoadIdentity();
@ -474,12 +475,13 @@ void TerrainRenderer::RenderPatches()
{ {
debug_assert(m->phase == Phase_Render); debug_assert(m->phase == Phase_Render);
std::vector<CPatchRData*> patchRDatas;
patchRDatas.reserve(m->visiblePatches.size());
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
patchRDatas.push_back(static_cast<CPatchRData*>(m->visiblePatches[i]->GetRenderData()));
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
for(size_t i = 0; i < m->visiblePatches.size(); ++i) CPatchRData::RenderStreams(patchRDatas, STREAM_POS);
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderStreams(STREAM_POS);
}
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
} }

View File

@ -35,6 +35,13 @@ CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
{ {
size_t size = MAX_VB_SIZE_BYTES; size_t size = MAX_VB_SIZE_BYTES;
if (target == GL_ARRAY_BUFFER)
{
// We want to store 16-bit indices to any vertex in a buffer, so the
// buffer must never be bigger than vertexSize*64K bytes
size = std::min(size, vertexSize*65536);
}
// allocate raw buffer // allocate raw buffer
if (g_Renderer.m_Caps.m_VBO) if (g_Renderer.m_Caps.m_VBO)
{ {
@ -64,13 +71,10 @@ CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
CVertexBuffer::~CVertexBuffer() CVertexBuffer::~CVertexBuffer()
{ {
if (m_Handle) if (m_Handle)
{
pglDeleteBuffersARB(1, &m_Handle); pglDeleteBuffersARB(1, &m_Handle);
}
else if (m_SysMem) if (m_SysMem)
{
delete[] m_SysMem; delete[] m_SysMem;
}
// janwas 2004-06-14: release freelist // janwas 2004-06-14: release freelist
typedef std::list<VBChunk*>::iterator Iter; typedef std::list<VBChunk*>::iterator Iter;
@ -175,6 +179,14 @@ u8* CVertexBuffer::Bind()
} }
} }
u8* CVertexBuffer::GetBindAddress()
{
if (g_Renderer.m_Caps.m_VBO)
return (u8*)0;
else
return m_SysMem;
}
void CVertexBuffer::Unbind() void CVertexBuffer::Unbind()
{ {
if (g_Renderer.m_Caps.m_VBO) if (g_Renderer.m_Caps.m_VBO)

View File

@ -56,6 +56,9 @@ public:
// to glVertexPointer ( + etc) calls // to glVertexPointer ( + etc) calls
u8* Bind(); u8* Bind();
// get the address that Bind() will return, without actually binding
u8* GetBindAddress();
// unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it // unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it
static void Unbind(); static void Unbind();