Newer
Older
/**
* @file llvertexbuffer.cpp
* @brief LLVertexBuffer implementation
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
* This library 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
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
#include "linden_common.h"
#include <boost/static_assert.hpp>
#include "llsys.h"
#include "llvertexbuffer.h"
// #include "llrender.h"
#include "llglheaders.h"
#include "llmemtype.h"
#include "llrender.h"
David Parks
committed
#include "llshadermgr.h"
#include "llglslshader.h"
David Parks
committed
#define LL_VBO_POOLING 1
//Next Highest Power Of Two
//helper function, returns first number > v that is a power of 2, or v if v is already a power of 2
U32 nhpo2(U32 v)
{
U32 r = 1;
while (r < v) {
r *= 2;
}
return r;
}
//which power of 2 is i?
//assumes i is a power of 2 > 0
U32 wpo2(U32 i)
{
llassert(i > 0);
llassert(nhpo2(i) == i);
U32 r = 0;
while (i >>= 1) ++r;
return r;
}
const U32 LL_VBO_BLOCK_SIZE = 2048;
David Parks
committed
const U32 LL_VBO_POOL_MAX_SEED_SIZE = 256*1024;
U32 vbo_block_size(U32 size)
{ //what block size will fit size?
U32 mod = size % LL_VBO_BLOCK_SIZE;
return mod == 0 ? size : size + (LL_VBO_BLOCK_SIZE-mod);
}
U32 vbo_block_index(U32 size)
{
return vbo_block_size(size)/LL_VBO_BLOCK_SIZE;
}
David Parks
committed
const U32 LL_VBO_POOL_SEED_COUNT = vbo_block_index(LL_VBO_POOL_MAX_SEED_SIZE);
//============================================================================
//static
Leslie Linden
committed
LLVBOPool LLVertexBuffer::sStreamVBOPool(GL_STREAM_DRAW_ARB, GL_ARRAY_BUFFER_ARB);
LLVBOPool LLVertexBuffer::sDynamicVBOPool(GL_DYNAMIC_DRAW_ARB, GL_ARRAY_BUFFER_ARB);
LLVBOPool LLVertexBuffer::sStreamIBOPool(GL_STREAM_DRAW_ARB, GL_ELEMENT_ARRAY_BUFFER_ARB);
LLVBOPool LLVertexBuffer::sDynamicIBOPool(GL_DYNAMIC_DRAW_ARB, GL_ELEMENT_ARRAY_BUFFER_ARB);
U32 LLVBOPool::sBytesPooled = 0;
U32 LLVBOPool::sIndexBytesPooled = 0;
U32 LLVertexBuffer::sAllocatedIndexBytes = 0;
U32 LLVertexBuffer::sIndexCount = 0;
Leslie Linden
committed
LLPrivateMemoryPool* LLVertexBuffer::sPrivatePoolp = NULL;
U32 LLVertexBuffer::sBindCount = 0;
U32 LLVertexBuffer::sSetCount = 0;
S32 LLVertexBuffer::sCount = 0;
S32 LLVertexBuffer::sGLCount = 0;
S32 LLVertexBuffer::sMappedCount = 0;
bool LLVertexBuffer::sDisableVBOMapping = false;
bool LLVertexBuffer::sEnableVBOs = true;
Josh Bell
committed
U32 LLVertexBuffer::sGLRenderBuffer = 0;
U32 LLVertexBuffer::sGLRenderArray = 0;
Josh Bell
committed
U32 LLVertexBuffer::sGLRenderIndices = 0;
U32 LLVertexBuffer::sLastMask = 0;
bool LLVertexBuffer::sVBOActive = false;
bool LLVertexBuffer::sIBOActive = false;
U32 LLVertexBuffer::sAllocatedBytes = 0;
U32 LLVertexBuffer::sVertexCount = 0;
bool LLVertexBuffer::sMapped = false;
bool LLVertexBuffer::sUseStreamDraw = true;
bool LLVertexBuffer::sUseVAO = false;
bool LLVertexBuffer::sPreferStreamDraw = false;
David Parks
committed
const U32 FENCE_WAIT_TIME_NANOSECONDS = 10000; //1 ms
class LLGLSyncFence : public LLGLFence
{
public:
David Parks
committed
GLsync mSync;
David Parks
committed
LLGLSyncFence()
{
David Parks
committed
mSync = 0;
David Parks
committed
}
virtual ~LLGLSyncFence()
David Parks
committed
{
David Parks
committed
#ifdef GL_ARB_sync
David Parks
committed
if (mSync)
{
glDeleteSync(mSync);
}
David Parks
committed
#endif
David Parks
committed
}
void placeFence()
{
David Parks
committed
#ifdef GL_ARB_sync
David Parks
committed
if (mSync)
{
glDeleteSync(mSync);
}
mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
David Parks
committed
#endif
David Parks
committed
}
void wait()
{
David Parks
committed
#ifdef GL_ARB_sync
David Parks
committed
if (mSync)
{
while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
{ //track the number of times we've waited here
static S32 waits = 0;
waits++;
}
}
David Parks
committed
}
};
David Parks
committed
LLVBOPool::LLVBOPool(U32 vboUsage, U32 vboType)
: mUsage(vboUsage), mType(vboType)
{
mMissCount.resize(LL_VBO_POOL_SEED_COUNT);
std::fill(mMissCount.begin(), mMissCount.end(), 0);
}
David Parks
committed
volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed)
llassert(vbo_block_size(size) == size);
volatile U8* ret = NULL;
#if LL_VBO_POOLING
U32 i = vbo_block_index(size);
if (mFreeList.size() <= i)
{
mFreeList.resize(i+1);
David Parks
committed
mMissCount.resize(i+1);
}
David Parks
committed
if (mFreeList[i].empty() || for_seed)
{
//make a new buffer
glGenBuffersARB(1, &name);
glBindBufferARB(mType, name);
David Parks
committed
if (!for_seed && i < LL_VBO_POOL_SEED_COUNT)
{ //record this miss
mMissCount[i]++;
}
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes += size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes += size;
}
if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
glBufferDataARB(mType, size, 0, mUsage);
ret = (U8*) ll_aligned_malloc_16(size);
}
else
{ //always use a true hint of static draw when allocating non-client-backed buffers
glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
}
glBindBufferARB(mType, 0);
David Parks
committed
if (for_seed)
{ //put into pool for future use
llassert(mFreeList.size() > i);
Record rec;
rec.mGLName = name;
rec.mClientData = ret;
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled += size;
}
else
{
sIndexBytesPooled += size;
}
mFreeList[i].push_back(rec);
}
}
else
{
name = mFreeList[i].front().mGLName;
ret = mFreeList[i].front().mClientData;
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled -= size;
}
else
{
sIndexBytesPooled -= size;
}
mFreeList[i].pop_front();
}
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#else //no pooling
glGenBuffersARB(1, &name);
glBindBufferARB(mType, name);
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes += size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes += size;
}
if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
{
glBufferDataARB(mType, size, 0, mUsage);
ret = (U8*) ll_aligned_malloc_16(size);
}
else
{ //always use a true hint of static draw when allocating non-client-backed buffers
glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
}
glBindBufferARB(mType, 0);
#endif
return ret;
}
void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
llassert(vbo_block_size(size) == size);
David Parks
committed
#if 0 && LL_VBO_POOLING
U32 i = vbo_block_index(size);
llassert(mFreeList.size() > i);
Record rec;
rec.mGLName = name;
rec.mClientData = buffer;
Leslie Linden
committed
if (buffer == NULL)
{
glDeleteBuffersARB(1, &rec.mGLName);
}
else
{
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled += size;
}
else
{
sIndexBytesPooled += size;
}
mFreeList[i].push_back(rec);
}
#else //no pooling
glDeleteBuffersARB(1, &name);
ll_aligned_free_16((U8*) buffer);
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes -= size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes -= size;
}
#endif
}
David Parks
committed
void LLVBOPool::seedPool()
{
U32 dummy_name = 0;
if (mFreeList.size() < LL_VBO_POOL_SEED_COUNT)
{
mFreeList.resize(LL_VBO_POOL_SEED_COUNT);
}
for (U32 i = 0; i < LL_VBO_POOL_SEED_COUNT; i++)
{
if (mMissCount[i] > mFreeList[i].size())
{
U32 size = i*LL_VBO_BLOCK_SIZE;
S32 count = mMissCount[i] - mFreeList[i].size();
for (U32 j = 0; j < count; ++j)
{
allocate(dummy_name, size, true);
}
}
}
}
void LLVBOPool::cleanup()
{
U32 size = 1;
for (U32 i = 0; i < mFreeList.size(); ++i)
{
record_list_t& l = mFreeList[i];
while (!l.empty())
{
Record& r = l.front();
glDeleteBuffersARB(1, &r.mGLName);
if (r.mClientData)
{
ll_aligned_free_16((void*) r.mClientData);
}
l.pop_front();
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled -= size;
LLVertexBuffer::sAllocatedBytes -= size;
}
else
{
sIndexBytesPooled -= size;
LLVertexBuffer::sAllocatedIndexBytes -= size;
}
}
size *= 2;
}
David Parks
committed
//reset miss counts
std::fill(mMissCount.begin(), mMissCount.end(), 0);
}
David Parks
committed
//NOTE: each component must be AT LEAST 4 bytes in size to avoid a performance penalty on AMD hardware
S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
sizeof(LLVector4), // TYPE_VERTEX,
sizeof(LLVector4), // TYPE_NORMAL,
sizeof(LLVector2), // TYPE_TEXCOORD0,
sizeof(LLVector2), // TYPE_TEXCOORD1,
sizeof(LLVector2), // TYPE_TEXCOORD2,
sizeof(LLVector2), // TYPE_TEXCOORD3,
sizeof(LLColor4U), // TYPE_COLOR,
David Parks
committed
sizeof(LLColor4U), // TYPE_EMISSIVE, only alpha is used currently
sizeof(F32), // TYPE_WEIGHT,
sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes
U32 LLVertexBuffer::sGLMode[LLRender::NUM_MODES] =
{
GL_TRIANGLES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
GL_POINTS,
GL_LINES,
GL_LINE_STRIP,
GL_QUADS,
GL_LINE_LOOP,
David Parks
committed
//static
void LLVertexBuffer::seedPools()
{
sStreamVBOPool.seedPool();
sDynamicVBOPool.seedPool();
sStreamIBOPool.seedPool();
sDynamicIBOPool.seedPool();
}
void LLVertexBuffer::setupClientArrays(U32 data_mask)
David Parks
committed
if (sLastMask != data_mask)
Leslie Linden
committed
bool error = false;
David Parks
committed
David Parks
committed
if (gGLManager.mGLSLVersionMajor < 2 && gGLManager.mGLSLVersionMinor < 30)
{
//make sure texture index is disabled
data_mask = data_mask & ~MAP_TEXTURE_INDEX;
}
if (LLGLSLShader::sNoFixedFunction)
{
for (U32 i = 0; i < TYPE_MAX; ++i)
S32 loc = i;
U32 mask = 1 << i;
if (sLastMask & (1 << i))
{ //was enabled
if (!(data_mask & mask))
{ //needs to be disabled
glDisableVertexAttribArrayARB(loc);
}
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
}
else
{ //was disabled
if (data_mask & mask)
{ //needs to be enabled
glEnableVertexAttribArrayARB(loc);
}
}
}
}
else
{
GLenum array[] =
{
GL_VERTEX_ARRAY,
GL_NORMAL_ARRAY,
GL_TEXTURE_COORD_ARRAY,
GL_COLOR_ARRAY,
};
GLenum mask[] =
{
MAP_VERTEX,
MAP_NORMAL,
MAP_TEXCOORD0,
MAP_COLOR
};
for (U32 i = 0; i < 4; ++i)
{
if (sLastMask & mask[i])
{ //was enabled
if (!(data_mask & mask[i]))
{ //needs to be disabled
glDisableClientState(array[i]);
}
else if (gDebugGL)
{ //needs to be enabled, make sure it was (DEBUG)
if (!glIsEnabled(array[i]))
{
if (gDebugSession)
{
Leslie Linden
committed
error = true;
gFailLog << "Bad client state! " << array[i] << " disabled." << std::endl;
}
else
{
llerrs << "Bad client state! " << array[i] << " disabled." << llendl;
}
}
}
else
{ //was disabled
if (data_mask & mask[i])
{ //needs to be enabled
glEnableClientState(array[i]);
}
else if (gDebugGL && glIsEnabled(array[i]))
{ //needs to be disabled, make sure it was (DEBUG TEMPORARY)
if (gDebugSession)
{
Leslie Linden
committed
error = true;
gFailLog << "Bad client state! " << array[i] << " enabled." << std::endl;
}
else
{
llerrs << "Bad client state! " << array[i] << " enabled." << llendl;
U32 map_tc[] =
{
MAP_TEXCOORD1,
MAP_TEXCOORD2,
MAP_TEXCOORD3
};
David Parks
committed
for (U32 i = 0; i < 3; i++)
if (sLastMask & map_tc[i])
{
if (!(data_mask & map_tc[i]))
{ //disable
glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
}
else if (data_mask & map_tc[i])
{
glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
}
}
David Parks
committed
if (sLastMask & MAP_BINORMAL)
if (!(data_mask & MAP_BINORMAL))
{
glClientActiveTextureARB(GL_TEXTURE2_ARB);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
}
else if (data_mask & MAP_BINORMAL)
glClientActiveTextureARB(GL_TEXTURE2_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
David Parks
committed
sLastMask = data_mask;
//static
void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
David Parks
committed
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
gGL.syncMatrices();
David Parks
committed
Xiaohong Bao
committed
llassert_always(norm.size() >= pos.size());
llassert_always(count > 0);
unbind();
setupClientArrays(MAP_VERTEX | MAP_NORMAL);
David Parks
committed
LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
if (shader)
{
David Parks
committed
S32 loc = LLVertexBuffer::TYPE_VERTEX;
David Parks
committed
if (loc > -1)
{
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[0].mV);
}
David Parks
committed
loc = LLVertexBuffer::TYPE_NORMAL;
David Parks
committed
if (loc > -1)
{
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, norm[0].mV);
}
}
else
{
glVertexPointer(3, GL_FLOAT, 0, pos[0].mV);
glNormalPointer(GL_FLOAT, 0, norm[0].mV);
}
glDrawArrays(sGLMode[mode], 0, count);
}
David Parks
committed
//static
void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp)
{
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
gGL.syncMatrices();
David Parks
committed
U32 mask = LLVertexBuffer::MAP_VERTEX;
if (tc)
{
mask = mask | LLVertexBuffer::MAP_TEXCOORD0;
}
unbind();
setupClientArrays(mask);
David Parks
committed
if (LLGLSLShader::sNoFixedFunction)
David Parks
committed
{
David Parks
committed
S32 loc = LLVertexBuffer::TYPE_VERTEX;
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);
David Parks
committed
David Parks
committed
if (tc)
{
loc = LLVertexBuffer::TYPE_TEXCOORD0;
glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, 0, tc);
David Parks
committed
}
}
else
{
glTexCoordPointer(2, GL_FLOAT, 0, tc);
glVertexPointer(3, GL_FLOAT, 16, pos);
}
glDrawElements(sGLMode[mode], num_indices, GL_UNSIGNED_SHORT, indicesp);
}
void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
if (start >= (U32) mNumVerts ||
end >= (U32) mNumVerts)
llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mNumVerts << llendl;
llassert(mNumIndices >= 0);
if (indices_offset >= (U32) mNumIndices ||
indices_offset + count > (U32) mNumIndices)
{
llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
}
if (gDebugGL && !useVBOs())
{
U16* idx = ((U16*) getIndicesPointer())+indices_offset;
for (U32 i = 0; i < count; ++i)
{
if (idx[i] < start || idx[i] > end)
{
llerrs << "Index out of range: " << idx[i] << " not in [" << start << ", " << end << "]" << llendl;
}
}
LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
if (shader && shader->mFeatures.mIndexedTextureChannels > 1)
{
LLStrider<LLVector4a> v;
//hack to get non-const reference
LLVertexBuffer* vb = (LLVertexBuffer*) this;
vb->getVertexStrider(v);
for (U32 i = start; i < end; i++)
{
S32 idx = (S32) (v[i][3]+0.25f);
if (idx < 0 || idx >= shader->mFeatures.mIndexedTextureChannels)
{
llerrs << "Bad texture index found in vertex data stream." << llendl;
}
}
}
}
}
void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
{
validateRange(start, end, count, indices_offset);
Leslie Linden
committed
mMappable = false;
gGL.syncMatrices();
llassert(mNumVerts >= 0);
David Parks
committed
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
David Parks
committed
if (mGLArray)
David Parks
committed
if (mGLArray != sGLRenderArray)
{
llerrs << "Wrong vertex array bound." << llendl;
}
David Parks
committed
else
{
if (mGLIndices != sGLRenderIndices)
{
llerrs << "Wrong index buffer bound." << llendl;
}
David Parks
committed
if (mGLBuffer != sGLRenderBuffer)
{
llerrs << "Wrong vertex buffer bound." << llendl;
}
}
if (gDebugGL && !mGLArray && useVBOs())
David Parks
committed
GLint elem = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &elem);
if (elem != mGLIndices)
{
llerrs << "Wrong index buffer bound!" << llendl;
}
{
llerrs << "Invalid draw mode: " << mode << llendl;
stop_glerror();
glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT,
David Parks
committed
placeFence();
}
void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
{
David Parks
committed
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
Leslie Linden
committed
mMappable = false;
gGL.syncMatrices();
llassert(mNumIndices >= 0);
if (indices_offset >= (U32) mNumIndices ||
indices_offset + count > (U32) mNumIndices)
{
llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
}
David Parks
committed
if (mGLArray)
David Parks
committed
if (mGLArray != sGLRenderArray)
{
llerrs << "Wrong vertex array bound." << llendl;
}
David Parks
committed
else
David Parks
committed
if (mGLIndices != sGLRenderIndices)
{
llerrs << "Wrong index buffer bound." << llendl;
}
if (mGLBuffer != sGLRenderBuffer)
{
llerrs << "Wrong vertex buffer bound." << llendl;
}
{
llerrs << "Invalid draw mode: " << mode << llendl;
stop_glerror();
glDrawElements(sGLMode[mode], count, GL_UNSIGNED_SHORT,
((U16*) getIndicesPointer()) + indices_offset);
stop_glerror();
David Parks
committed
placeFence();
void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
{
David Parks
committed
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
Leslie Linden
committed
mMappable = false;
gGL.syncMatrices();
llassert(mNumVerts >= 0);
if (first >= (U32) mNumVerts ||
first + count > (U32) mNumVerts)
{
llerrs << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << llendl;
}
David Parks
committed
if (mGLArray)
David Parks
committed
if (mGLArray != sGLRenderArray)
{
llerrs << "Wrong vertex array bound." << llendl;
}
}
else
{
if (mGLBuffer != sGLRenderBuffer || useVBOs() != sVBOActive)
{
llerrs << "Wrong vertex buffer bound." << llendl;
}
{
llerrs << "Invalid draw mode: " << mode << llendl;
stop_glerror();
glDrawArrays(sGLMode[mode], first, count);
David Parks
committed
placeFence();
Xiaohong Bao
committed
void LLVertexBuffer::initClass(bool use_vbo, bool no_vbo_mapping)
Leslie Linden
committed
sEnableVBOs = use_vbo && gGLManager.mHasVertexBufferObject;
sDisableVBOMapping = sEnableVBOs && no_vbo_mapping;
Leslie Linden
committed
if (!sPrivatePoolp)
Leslie Linden
committed
sPrivatePoolp = LLPrivateMemoryPoolManager::getInstance()->newPool(LLPrivateMemoryPool::STATIC);
}
//static
void LLVertexBuffer::unbind()
{
if (sGLRenderArray)
{
glBindVertexArray(0);
sGLRenderArray = 0;
David Parks
committed
sGLRenderIndices = 0;
}
if (sVBOActive)
{
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
}
if (sIBOActive)
{
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
}
sGLRenderBuffer = 0;
sGLRenderIndices = 0;
}
//static
void LLVertexBuffer::cleanupClass()
{
LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
sStreamIBOPool.cleanup();
sDynamicIBOPool.cleanup();
sStreamVBOPool.cleanup();
sDynamicVBOPool.cleanup();
LLPrivateMemoryPoolManager::getInstance()->deletePool(sPrivatePoolp);
sPrivatePoolp = NULL;
}
//----------------------------------------------------------------------------
Leslie Linden
committed
S32 LLVertexBuffer::determineUsage(S32 usage)
Leslie Linden
committed
S32 ret_usage = usage;
if (!sEnableVBOs)
{
Leslie Linden
committed
ret_usage = 0;
Leslie Linden
committed
if (ret_usage == GL_STREAM_DRAW_ARB && !sUseStreamDraw)
Leslie Linden
committed
ret_usage = 0;
if (ret_usage == GL_DYNAMIC_DRAW_ARB && sPreferStreamDraw)
Leslie Linden
committed
ret_usage = GL_STREAM_DRAW_ARB;
Leslie Linden
committed
if (ret_usage == 0 && LLRender::sGLCoreProfile)
{ //MUST use VBOs for all rendering
Leslie Linden
committed
ret_usage = GL_STREAM_DRAW_ARB;
}
Leslie Linden
committed
if (ret_usage && ret_usage != GL_STREAM_DRAW_ARB)
{ //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
if (sDisableVBOMapping)
{ //always use stream draw if VBO mapping is disabled
Leslie Linden
committed
ret_usage = GL_STREAM_DRAW_ARB;
}
else
{
Leslie Linden
committed
ret_usage = GL_DYNAMIC_DRAW_ARB;
}
Leslie Linden
committed
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
return ret_usage;
}
LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
LLRefCount(),
mNumVerts(0),
mNumIndices(0),
mAlignedOffset(0),
mAlignedIndexOffset(0),
mSize(0),
mIndicesSize(0),
mTypeMask(typemask),
mUsage(LLVertexBuffer::determineUsage(usage)),
mGLBuffer(0),
mGLIndices(0),
mGLArray(0),
mMappedData(NULL),
mMappedIndexData(NULL),
mMappedDataUsingVBOs(false),
mMappedIndexDataUsingVBOs(false),
mVertexLocked(false),
mIndexLocked(false),
mFinal(false),
mEmpty(true),
mMappable(false),
mFence(NULL)
{
LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR);
mMappable = (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping);
//zero out offsets
for (U32 i = 0; i < TYPE_MAX; i++)
{
mOffsets[i] = 0;
}
sCount++;
}
//static