1
0
forked from 0ad/0ad

Performance improvements to VertexBuffer.

Patch By: dm
Tested By: OptimusShepard, Stan, wraitii
Differential Revision: https://code.wildfiregames.com/D2528
This was SVN commit r24834.
This commit is contained in:
Vladislav Belov 2021-02-04 23:26:37 +00:00
parent a564892fab
commit b3cbee6ba9
3 changed files with 63 additions and 49 deletions

View File

@ -66,6 +66,7 @@
{ "name": "Daniel Trevitz" },
{ "nick": "Dariost", "name": "Dario Ostuni" },
{ "nick": "Dave", "name": "David Protasowski" },
{ "name": "David Marshall" },
{ "nick": "dax", "name": "Dacian Fiordean" },
{ "nick": "deebee", "name": "Deepak Anthony" },
{ "nick": "Deiz" },

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -15,18 +15,18 @@
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* encapsulation of VBOs with sharing
*/
#include "precompiled.h"
#include "ps/Errors.h"
#include "VertexBuffer.h"
#include "lib/ogl.h"
#include "lib/sysdep/cpu.h"
#include "Renderer.h"
#include "VertexBuffer.h"
#include "VertexBufferManager.h"
#include "ps/CLogger.h"
#include "ps/Errors.h"
#include <algorithm>
#include <iterator>
// Absolute maximum (bytewise) size of each GL vertex buffer object.
// Make it large enough for the maximum feasible mesh size (64K vertexes,
@ -35,7 +35,7 @@
#define MAX_VB_SIZE_BYTES (4*1024*1024)
CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
: m_VertexSize(vertexSize), m_Handle(0), m_SysMem(0), m_Usage(usage), m_Target(target)
: m_VertexSize(vertexSize), m_Handle(0), m_SysMem(0), m_Usage(usage), m_Target(target), m_HasNeededChunks(false)
{
size_t size = MAX_VB_SIZE_BYTES;
@ -68,7 +68,7 @@ CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
chunk->m_Owner = this;
chunk->m_Count = m_FreeVertices;
chunk->m_Index = 0;
m_FreeList.push_front(chunk);
m_FreeList.emplace_back(chunk);
}
CVertexBuffer::~CVertexBuffer()
@ -79,20 +79,16 @@ CVertexBuffer::~CVertexBuffer()
if (m_Handle)
pglDeleteBuffersARB(1, &m_Handle);
delete[] m_SysMem;
SAFE_ARRAY_DELETE(m_SysMem);
typedef std::list<VBChunk*>::iterator Iter;
for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
delete *iter;
for (VBChunk* const& chunk : m_FreeList)
delete chunk;
}
bool CVertexBuffer::CompatibleVertexType(size_t vertexSize, GLenum usage, GLenum target)
{
if (usage != m_Usage || target != m_Target || vertexSize != m_VertexSize)
return false;
return true;
return usage == m_Usage && target == m_Target && vertexSize == m_VertexSize;
}
///////////////////////////////////////////////////////////////////////////////
@ -113,23 +109,25 @@ CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize, size_t numVer
return 0;
// trawl free list looking for first free chunk with enough space
VBChunk* chunk = 0;
typedef std::list<VBChunk*>::iterator Iter;
for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter) {
if (numVertices <= (*iter)->m_Count) {
chunk = *iter;
// remove this chunk from the free list
m_FreeList.erase(iter);
m_FreeVertices -= chunk->m_Count;
// no need to search further ..
std::vector<VBChunk*>::iterator best_iter = m_FreeList.end();
for (std::vector<VBChunk*>::iterator iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
{
if (numVertices == (*iter)->m_Count)
{
best_iter = iter;
break;
}
else if (numVertices < (*iter)->m_Count && (best_iter == m_FreeList.end() || (*best_iter)->m_Count < (*iter)->m_Count))
best_iter = iter;
}
if (!chunk) {
// no big enough spare chunk available
return 0;
}
// We could not find a large enough chunk.
if (best_iter == m_FreeList.end())
return nullptr;
VBChunk* chunk = *best_iter;
m_FreeList.erase(best_iter);
m_FreeVertices -= chunk->m_Count;
chunk->m_BackingStore = backingStore;
chunk->m_Dirty = false;
@ -143,7 +141,7 @@ CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize, size_t numVer
newchunk->m_Owner = this;
newchunk->m_Count = chunk->m_Count - numVertices;
newchunk->m_Index = chunk->m_Index + numVertices;
m_FreeList.push_front(newchunk);
m_FreeList.emplace_back(newchunk);
m_FreeVertices += newchunk->m_Count;
// resize given chunk
@ -162,9 +160,9 @@ void CVertexBuffer::Release(VBChunk* chunk)
// Update total free count before potentially modifying this chunk's count
m_FreeVertices += chunk->m_Count;
m_AllocList.remove(chunk);
m_AllocList.erase(std::find(m_AllocList.begin(), m_AllocList.end(), chunk));
typedef std::list<VBChunk*>::iterator Iter;
typedef std::vector<VBChunk*>::iterator Iter;
// Coalesce with any free-list items that are adjacent to this chunk;
// merge the found chunk with the new one, and remove the old one
@ -189,7 +187,7 @@ void CVertexBuffer::Release(VBChunk* chunk)
}
while (coalesced);
m_FreeList.push_front(chunk);
m_FreeList.emplace_back(chunk);
}
///////////////////////////////////////////////////////////////////////////////
@ -234,6 +232,9 @@ u8* CVertexBuffer::Bind()
if (UseStreaming(m_Usage))
{
if (!m_HasNeededChunks)
return nullptr;
// If any chunks are out of sync with the current VBO, and are
// needed for rendering this frame, we'll need to re-upload the VBO
bool needUpload = false;
@ -294,17 +295,24 @@ u8* CVertexBuffer::Bind()
for (VBChunk* const& chunk : m_AllocList)
{
if (chunk->m_Needed)
{
chunk->m_Dirty = false;
chunk->m_Needed = false;
}
else
chunk->m_Dirty = true;
}
}
// Reset the flags for the next phase
else
{
// Reset the flags for the next phase.
for (VBChunk* const& chunk : m_AllocList)
chunk->m_Needed = false;
}
m_HasNeededChunks = false;
}
return (u8*)0;
}
@ -337,19 +345,24 @@ size_t CVertexBuffer::GetBytesAllocated() const
void CVertexBuffer::DumpStatus()
{
debug_printf("freeverts = %d\n", (int)m_FreeVertices);
debug_printf("freeverts = %d\n", static_cast<int>(m_FreeVertices));
size_t maxSize = 0;
typedef std::list<VBChunk*>::iterator Iter;
for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
for (VBChunk* const& chunk : m_FreeList)
{
debug_printf("free chunk %p: size=%d\n", (void *)*iter, (int)((*iter)->m_Count));
maxSize = std::max((*iter)->m_Count, maxSize);
debug_printf("free chunk %p: size=%d\n", static_cast<void *>(chunk), static_cast<int>(chunk->m_Count));
maxSize = std::max(chunk->m_Count, maxSize);
}
debug_printf("max size = %d\n", (int)maxSize);
debug_printf("max size = %d\n", static_cast<int>(maxSize));
}
bool CVertexBuffer::UseStreaming(GLenum usage)
{
return (usage == GL_DYNAMIC_DRAW || usage == GL_STREAM_DRAW);
}
void CVertexBuffer::PrepareForRendering(VBChunk* chunk)
{
chunk->m_Needed = true;
m_HasNeededChunks = true;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -24,7 +24,6 @@
#include "lib/res/graphics/ogl_tex.h"
#include <list>
#include <vector>
/**
@ -102,7 +101,7 @@ public:
static void Unbind();
/// Make the vertex data available for the next call to Bind()
void PrepareForRendering(VBChunk* chunk) { chunk->m_Needed = true; }
void PrepareForRendering(VBChunk* chunk);
/// Update vertex data for given chunk. Transfers the provided data to the actual OpenGL vertex buffer.
void UpdateChunkVertices(VBChunk* chunk, void* data);
@ -145,9 +144,9 @@ private:
/// Number of vertices of above size in this buffer
size_t m_MaxVertices;
/// List of free chunks in this buffer
std::list<VBChunk*> m_FreeList;
std::vector<VBChunk*> m_FreeList;
/// List of allocated chunks
std::list<VBChunk*> m_AllocList;
std::vector<VBChunk*> m_AllocList;
/// Available free vertices - total of all free vertices in the free list
size_t m_FreeVertices;
/// Handle to the actual GL vertex buffer object
@ -158,6 +157,7 @@ private:
GLenum m_Usage;
/// Buffer target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER)
GLenum m_Target;
bool m_HasNeededChunks;
};
#endif