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 "llfasttimer.h"
#include "llsys.h"
#include "llvertexbuffer.h"
// #include "llrender.h"
#include "llglheaders.h"
#include "llrender.h"
David Parks
committed
#include "llshadermgr.h"
#include "llglslshader.h"
//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
LLVBOPool LLVertexBuffer::sStreamVBOPool(GL_STREAM_DRAW, GL_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sDynamicVBOPool(GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sDynamicCopyVBOPool(GL_DYNAMIC_COPY, GL_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sStreamIBOPool(GL_STREAM_DRAW, GL_ELEMENT_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sDynamicIBOPool(GL_DYNAMIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
U32 LLVBOPool::sBytesPooled = 0;
U32 LLVBOPool::sIndexBytesPooled = 0;
David Parks
committed
std::list<U32> LLVertexBuffer::sAvailableVAOName;
U32 LLVertexBuffer::sCurVAOName = 1;
U32 LLVertexBuffer::sAllocatedIndexBytes = 0;
U32 LLVertexBuffer::sIndexCount = 0;
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
David Parks
committed
U32 LLVBOPool::genBuffer()
David Parks
committed
{
David Parks
committed
U32 ret = 0;
David Parks
committed
glGenBuffers(1, &ret);
David Parks
committed
return ret;
}
void LLVBOPool::deleteBuffer(U32 name)
{
if (gGLManager.mInited)
{
LLVertexBuffer::unbind();
David Parks
committed
glBindBuffer(mType, name);
glBufferData(mType, 0, NULL, mUsage);
glBindBuffer(mType, 0);
glDeleteBuffers(1, &name);
David Parks
committed
}
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);
}
volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed)
llassert(vbo_block_size(size) == size);
volatile U8* ret = NULL;
U32 i = vbo_block_index(size);
if (mFreeList.size() <= i)
{
mFreeList.resize(i+1);
}
David Parks
committed
if (mFreeList[i].empty() || for_seed)
{
//make a new buffer
name = genBuffer();
glBindBuffer(mType, name);
David Parks
committed
if (!for_seed && i < LL_VBO_POOL_SEED_COUNT)
{ //record this miss
mMissCount[i]++;
}
if (mType == GL_ARRAY_BUFFER)
{
LLVertexBuffer::sAllocatedBytes += size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes += size;
}
if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW)
glBufferData(mType, size, 0, mUsage);
if (mUsage != GL_DYNAMIC_COPY)
{ //data will be provided by application
Richard Linden
committed
ret = (U8*) ll_aligned_malloc<64>(size);
LL_ERRS() << "Failed to allocate "<< size << " bytes for LLVBOPool buffer " << name <<"." << LL_NEWLINE
<< "Free list size: " << mFreeList.size() // this happens if we are out of memory so a solution might be to clear some from freelist
<< " Allocated Bytes: " << LLVertexBuffer::sAllocatedBytes
<< " Allocated Index Bytes: " << LLVertexBuffer::sAllocatedIndexBytes
<< " Pooled Bytes: " << sBytesPooled
<< " Pooled Index Bytes: " << sIndexBytesPooled
<< LL_ENDL;
else
{ //always use a true hint of static draw when allocating non-client-backed buffers
glBufferData(mType, size, nullptr, GL_STATIC_DRAW);
}
glBindBuffer(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)
David Parks
committed
{
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)
{
sBytesPooled -= size;
}
else
{
sIndexBytesPooled -= size;
}
mFreeList[i].pop_front();
}
return ret;
}
void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
llassert(vbo_block_size(size) == size);
David Parks
committed
deleteBuffer(name);
ll_aligned_free<64>((U8*) buffer);
if (mType == GL_ARRAY_BUFFER)
{
LLVertexBuffer::sAllocatedBytes -= size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes -= size;
}
}
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);
}
}
}
}
simon@Simon-PC.lindenlab.com
committed
void LLVBOPool::cleanup()
{
David Parks
committed
U32 size = LL_VBO_BLOCK_SIZE;
for (U32 i = 0; i < mFreeList.size(); ++i)
{
record_list_t& l = mFreeList[i];
while (!l.empty())
{
Record& r = l.front();
David Parks
committed
deleteBuffer(r.mGLName);
if (r.mClientData)
{
Richard Linden
committed
ll_aligned_free<64>((void*) r.mClientData);
}
l.pop_front();
if (mType == GL_ARRAY_BUFFER)
{
sBytesPooled -= size;
LLVertexBuffer::sAllocatedBytes -= size;
}
else
{
sIndexBytesPooled -= size;
LLVertexBuffer::sAllocatedIndexBytes -= size;
}
}
David Parks
committed
size += LL_VBO_BLOCK_SIZE;
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
const 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
David Parks
committed
sizeof(LLVector4), // TYPE_TANGENT,
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
{
"TYPE_VERTEX",
"TYPE_NORMAL",
"TYPE_TEXCOORD0",
"TYPE_TEXCOORD1",
"TYPE_TEXCOORD2",
"TYPE_TEXCOORD3",
"TYPE_COLOR",
"TYPE_EMISSIVE",
David Parks
committed
"TYPE_TANGENT",
"TYPE_WEIGHT",
"TYPE_WEIGHT4",
"TYPE_CLOTHWEIGHT",
"TYPE_TEXTURE_INDEX",
"TYPE_MAX",
"TYPE_INDEX",
};
const 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
U32 LLVertexBuffer::getVAOName()
{
U32 ret = 0;
if (!sAvailableVAOName.empty())
{
ret = sAvailableVAOName.front();
sAvailableVAOName.pop_front();
}
else
{
David Parks
committed
glGenVertexArrays(1, &ret);
David Parks
committed
}
return ret;
}
//static
void LLVertexBuffer::releaseVAOName(U32 name)
{
sAvailableVAOName.push_back(name);
}
David Parks
committed
//static
void LLVertexBuffer::seedPools()
{
sStreamVBOPool.seedPool();
sDynamicVBOPool.seedPool();
sDynamicCopyVBOPool.seedPool();
David Parks
committed
sStreamIBOPool.seedPool();
sDynamicIBOPool.seedPool();
}
void LLVertexBuffer::setupClientArrays(U32 data_mask)
David Parks
committed
if (sLastMask != data_mask)
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
glDisableVertexAttribArray(loc);
}
else
{ //was disabled
if (data_mask & mask)
{ //needs to be enabled
glEnableVertexAttribArray(loc);
}
}
}
}
else
{
static const GLenum array[] =
{
GL_VERTEX_ARRAY,
GL_NORMAL_ARRAY,
GL_TEXTURE_COORD_ARRAY,
GL_COLOR_ARRAY,
};
static const 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)
{
gFailLog << "Bad client state! " << array[i] << " disabled." << std::endl;
}
else
{
LL_ERRS() << "Bad client state! " << array[i] << " disabled." << LL_ENDL;
}
}
}
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)
{
gFailLog << "Bad client state! " << array[i] << " enabled." << std::endl;
}
else
{
LL_ERRS() << "Bad client state! " << array[i] << " enabled." << LL_ENDL;
static const 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
glClientActiveTexture(GL_TEXTURE1+i);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0);
else if (data_mask & map_tc[i])
glClientActiveTexture(GL_TEXTURE1+i);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0);
}
David Parks
committed
if (sLastMask & MAP_TANGENT)
David Parks
committed
if (!(data_mask & MAP_TANGENT))
glClientActiveTexture(GL_TEXTURE2);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0);
David Parks
committed
else if (data_mask & MAP_TANGENT)
glClientActiveTexture(GL_TEXTURE2);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0);
David Parks
committed
sLastMask = data_mask;
static LLTrace::BlockTimerStatHandle FTM_VB_DRAW_ARRAYS("drawArrays");
void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
//LL_RECORD_BLOCK_TIME(FTM_VB_DRAW_ARRAYS);
David Parks
committed
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
gGL.syncMatrices();
David Parks
committed
llassert(norm.size() >= pos.size());
llassert(count > 0);
if( count == 0 )
{
LL_WARNS() << "Called drawArrays with 0 vertices" << LL_ENDL;
return;
}
if( norm.size() < pos.size() )
{
LL_WARNS() << "Called drawArrays with #" << norm.size() << " normals and #" << pos.size() << " vertices" << LL_ENDL;
return;
}
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)
{
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[0].mV);
David Parks
committed
}
David Parks
committed
loc = LLVertexBuffer::TYPE_NORMAL;
David Parks
committed
if (loc > -1)
{
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, norm[0].mV);
David Parks
committed
}
}
else
{
glVertexPointer(3, GL_FLOAT, 0, pos[0].mV);
glNormalPointer(GL_FLOAT, 0, norm[0].mV);
}
David Parks
committed
LLGLSLShader::startProfile();
glDrawArrays(sGLMode[mode], 0, count);
David Parks
committed
LLGLSLShader::stopProfile(count, mode);
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;
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);
David Parks
committed
David Parks
committed
if (tc)
{
loc = LLVertexBuffer::TYPE_TEXCOORD0;
glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, tc);
David Parks
committed
}
}
else
{
glTexCoordPointer(2, GL_FLOAT, 0, tc);
glVertexPointer(3, GL_FLOAT, 16, pos);
}
David Parks
committed
LLGLSLShader::startProfile();
David Parks
committed
glDrawElements(sGLMode[mode], num_indices, GL_UNSIGNED_SHORT, indicesp);
David Parks
committed
LLGLSLShader::stopProfile(num_indices, mode);
David Parks
committed
}
void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
llassert(start < (U32)mNumVerts);
llassert(end < (U32)mNumVerts);
if (start >= (U32) mNumVerts ||
end >= (U32) mNumVerts)
LL_ERRS() << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mNumVerts << LL_ENDL;
llassert(mNumIndices >= 0);
if (indices_offset >= (U32) mNumIndices ||
indices_offset + count > (U32) mNumIndices)
LL_ERRS() << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << LL_ENDL;
if (gDebugGL && !useVBOs())
{
U16* idx = ((U16*) getIndicesPointer())+indices_offset;
for (U32 i = 0; i < count; ++i)
{
llassert(idx[i] >= start);
llassert(idx[i] <= end);
if (idx[i] < start || idx[i] > end)
{
LL_ERRS() << "Index out of range: " << idx[i] << " not in [" << start << ", " << end << "]" << LL_ENDL;
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);
llassert(idx >= 0);
if (idx < 0 || idx >= shader->mFeatures.mIndexedTextureChannels)
{
LL_ERRS() << "Bad texture index found in vertex data stream." << LL_ENDL;
}
}
}
}
}
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)
{
LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
David Parks
committed
}
David Parks
committed
else
{
if (mGLIndices != sGLRenderIndices)
{
LL_ERRS() << "Wrong index buffer bound." << LL_ENDL;
David Parks
committed
}
David Parks
committed
if (mGLBuffer != sGLRenderBuffer)
{
LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
David Parks
committed
}
}
if (gDebugGL && !mGLArray && useVBOs())
David Parks
committed
GLint elem = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &elem);
David Parks
committed
if (elem != mGLIndices)
{
LL_ERRS() << "Wrong index buffer bound!" << LL_ENDL;
David Parks
committed
}
LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
stop_glerror();
David Parks
committed
LLGLSLShader::startProfile();
glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT,
David Parks
committed
LLGLSLShader::stopProfile(count, mode);
David Parks
committed
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)
LL_ERRS() << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << LL_ENDL;
David Parks
committed
if (mGLArray)
David Parks
committed
if (mGLArray != sGLRenderArray)
{
LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
David Parks
committed
}
David Parks
committed
else
David Parks
committed
if (mGLIndices != sGLRenderIndices)
{
LL_ERRS() << "Wrong index buffer bound." << LL_ENDL;
David Parks
committed
}
if (mGLBuffer != sGLRenderBuffer)
{
LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
David Parks
committed
}
LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
stop_glerror();
David Parks
committed
LLGLSLShader::startProfile();
glDrawElements(sGLMode[mode], count, GL_UNSIGNED_SHORT,
((U16*) getIndicesPointer()) + indices_offset);
David Parks
committed
LLGLSLShader::stopProfile(count, mode);
stop_glerror();
David Parks
committed
placeFence();
static LLTrace::BlockTimerStatHandle FTM_GL_DRAW_ARRAYS("GL draw arrays");
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)
LL_ERRS() << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << LL_ENDL;
David Parks
committed
if (mGLArray)
David Parks
committed
if (mGLArray != sGLRenderArray)
{
LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
David Parks
committed
}
}
else
{
if (mGLBuffer != sGLRenderBuffer || useVBOs() != sVBOActive)
{
LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
David Parks
committed
}
LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
//LL_RECORD_BLOCK_TIME(FTM_GL_DRAW_ARRAYS);
stop_glerror();
LLGLSLShader::startProfile();
stop_glerror();
glDrawArrays(sGLMode[mode], first, count);
stop_glerror();
LLGLSLShader::stopProfile(count, mode);
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;
}
//static
void LLVertexBuffer::unbind()
{
if (sGLRenderArray)
{
glBindVertexArray(0);
sGLRenderArray = 0;
David Parks
committed
sGLRenderIndices = 0;
}
if (sVBOActive)
{
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
if (sIBOActive)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
sGLRenderBuffer = 0;
sGLRenderIndices = 0;
}
//static
void LLVertexBuffer::cleanupClass()
{
sStreamIBOPool.cleanup();
sDynamicIBOPool.cleanup();
sStreamVBOPool.cleanup();
sDynamicVBOPool.cleanup();
sDynamicCopyVBOPool.cleanup();
}
//----------------------------------------------------------------------------
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 && !sUseStreamDraw)
Leslie Linden
committed
ret_usage = 0;
if (ret_usage == GL_DYNAMIC_DRAW && sPreferStreamDraw)
ret_usage = GL_STREAM_DRAW;
Leslie Linden
committed
if (ret_usage == 0 && LLRender::sGLCoreProfile)
{ //MUST use VBOs for all rendering
ret_usage = GL_STREAM_DRAW;
}
Leslie Linden
committed
if (ret_usage && ret_usage != GL_STREAM_DRAW)
{ //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
if (ret_usage != GL_DYNAMIC_COPY)
{
if (sDisableVBOMapping)
{ //always use stream draw if VBO mapping is disabled
ret_usage = GL_STREAM_DRAW;
ret_usage = GL_DYNAMIC_DRAW;
Leslie Linden
committed
return ret_usage;
}
LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage)
: LLTrace::MemTrackable<LLVertexBuffer>("LLVertexBuffer"),
Leslie Linden
committed
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)
{
mMappable = (mUsage == GL_DYNAMIC_DRAW && !sDisableVBOMapping);
//zero out offsets
for (U32 i = 0; i < TYPE_MAX; i++)
{
mOffsets[i] = 0;
}