From 5da88f5a809fd6f29d7704cfd947226e23714bd1 Mon Sep 17 00:00:00 2001 From: Dave Houlton Date: Wed, 5 May 2021 13:09:16 -0600 Subject: SL-15221 add VBO dirty bit to avoid many per-frame checks --- indra/llrender/llvertexbuffer.cpp | 45 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'indra/llrender/llvertexbuffer.cpp') diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 7d2b09ca4a..0d39d503d7 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -144,9 +144,10 @@ void LLVBOPool::deleteBuffer(U32 name) LLVBOPool::LLVBOPool(U32 vboUsage, U32 vboType) -: mUsage(vboUsage), mType(vboType) +: mUsage(vboUsage), mType(vboType), mMissCountDirty(true) { - mMissCount.resize(LL_VBO_POOL_SEED_COUNT); + mFreeList.resize(LL_VBO_POOL_SEED_COUNT); + mMissCount.resize(LL_VBO_POOL_SEED_COUNT); std::fill(mMissCount.begin(), mMissCount.end(), 0); } @@ -173,7 +174,8 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed) if (!for_seed && i < LL_VBO_POOL_SEED_COUNT) { //record this miss mMissCount[i]++; - } + mMissCountDirty = true; // signal to ::seedPool() + } if (mType == GL_ARRAY_BUFFER_ARB) { @@ -226,6 +228,7 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed) sIndexBytesPooled += size; } mFreeList[i].push_back(rec); + mMissCountDirty = true; // signal to ::seedPool() } } else @@ -243,6 +246,7 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed) } mFreeList[i].pop_front(); + mMissCountDirty = true; // signal to ::seedPool() } return ret; @@ -267,26 +271,25 @@ void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size) void LLVBOPool::seedPool() { - U32 dummy_name = 0; + if (mMissCountDirty) + { + 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; - 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); - } - } - } + S32 count = mMissCount[i] - mFreeList[i].size(); + for (U32 j = 0; j < count; ++j) + { + allocate(dummy_name, size, true); + } + } + } + mMissCountDirty = false; + } } -- cgit v1.2.3 From fd9fc8e4a5d49d0d24e22c6cb2391f75bf0ea763 Mon Sep 17 00:00:00 2001 From: Dave Houlton Date: Wed, 5 May 2021 15:15:50 -0600 Subject: SL-15221 fixed off-by-one accounting error --- indra/llrender/llvertexbuffer.cpp | 107 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) (limited to 'indra/llrender/llvertexbuffer.cpp') diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 0d39d503d7..ebf0b2fe78 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -74,10 +74,12 @@ U32 vbo_block_size(U32 size) U32 vbo_block_index(U32 size) { - return vbo_block_size(size)/LL_VBO_BLOCK_SIZE; + U32 blocks = vbo_block_size(size)/LL_VBO_BLOCK_SIZE; // block count reqd + llassert(blocks > 0); + return blocks - 1; // Adj index, i.e. single-block allocations are at index 0, etc } -const U32 LL_VBO_POOL_SEED_COUNT = vbo_block_index(LL_VBO_POOL_MAX_SEED_SIZE); +const U32 LL_VBO_POOL_SEED_COUNT = vbo_block_index(LL_VBO_POOL_MAX_SEED_SIZE) + 1; //============================================================================ @@ -118,6 +120,97 @@ bool LLVertexBuffer::sUseStreamDraw = true; bool LLVertexBuffer::sUseVAO = false; bool LLVertexBuffer::sPreferStreamDraw = false; +// DEBUG, temporary +#include + +typedef struct +{ + U32 allocated_size; + void* memptr; +} glbufmem; + +static std::unordered_map buf_list; + +void add_buf(U32 name, U32 size, void* ptr) +{ + LL_WARNS("VBO_ACCOUNTING") << "ADD add_buf GLname " << name << ", size " << size << ", ptr " << ptr << LL_ENDL; + + if (0 < buf_list.count(name)) + { + if (buf_list[name].allocated_size > 0) + { + U32 a = buf_list[name].allocated_size; + LL_WARNS("VBO_ACC_ERROR") << "add_buf error, adding duplicate GLname " << name << ", size " << size << ", allocated " << a << LL_ENDL; + } + } + + buf_list[name].allocated_size = size; + buf_list[name].memptr = ptr; +} + +U32 find_buf_size(U32 name) +{ + if (buf_list.count(name) > 0) return buf_list[name].allocated_size; + return 0; +} + +bool clear_buf(U32 name, U32 size, void* ptr) +{ + LL_WARNS("VBO_ACCOUNTING") << "DEL clear_buf GLname " << name << ", size " << size << ", ptr " << ptr << LL_ENDL; + + if (0 == buf_list.count(name)) + { + LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, attempt to remove unknown GLname " << name << ", size " << size << LL_ENDL; + return false; + } + + if (buf_list[name].allocated_size == 0) + { + LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, attempt to remove empty GLname " << name << ", size " << size << LL_ENDL; + return false; + } + + if (buf_list[name].allocated_size != size) + { + U32 a = buf_list[name].allocated_size; + LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, size mismatch " << name << ", size " << size << ", allocated " << a << LL_ENDL; + buf_list[name].allocated_size = 0; + buf_list[name].memptr = nullptr; + return false; + } + + if (buf_list[name].memptr != ptr) + { + void* p = buf_list[name].memptr; + LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, ptr mismatch " << name << ", ptr " << ptr << ", saved ptr " << p << LL_ENDL; + buf_list[name].allocated_size = 0; + buf_list[name].memptr = nullptr; + return false; + } + + buf_list[name].allocated_size = 0; + buf_list[name].memptr = nullptr; + return true; +} + +#if 0 + // Find the GLname in the freelist +for (U32 i = 0; i < LL_VBO_POOL_SEED_COUNT; i++) +{ + for (auto &r : mFreeList[i]) + { + if (r.mGLName == name) + { + LL_WARNS() << "Release size " << size << ", found Name " << name << " in bucket " << i << LL_ENDL; + } + } +} + +// Remove the buffer record from the free record list, by GLname +U32 i = vbo_block_index(size); +mFreeList[i].remove_if([name](Record r) {return (name == r.mGLName); }); +i++; +#endif U32 LLVBOPool::genBuffer() { @@ -230,6 +323,8 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed) mFreeList[i].push_back(rec); mMissCountDirty = true; // signal to ::seedPool() } + + add_buf(name, size, (void*)ret); } else { @@ -267,6 +362,8 @@ void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size) { LLVertexBuffer::sAllocatedIndexBytes -= size; } + + clear_buf(name, size, (void*)buffer); } void LLVBOPool::seedPool() @@ -274,19 +371,19 @@ void LLVBOPool::seedPool() if (mMissCountDirty) { U32 dummy_name = 0; + U32 size = LL_VBO_BLOCK_SIZE; 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); } } + size += LL_VBO_BLOCK_SIZE; } mMissCountDirty = false; } @@ -313,6 +410,8 @@ void LLVBOPool::cleanup() ll_aligned_free<64>((void*) r.mClientData); } + clear_buf(r.mGLName, size, (void*)r.mClientData); + l.pop_front(); if (mType == GL_ARRAY_BUFFER_ARB) -- cgit v1.2.3 From 7131f8c46499b8f856c35137cda5ad4cb1bce527 Mon Sep 17 00:00:00 2001 From: Dave Houlton Date: Thu, 6 May 2021 14:50:26 -0600 Subject: SL-15221 strip debug code, add sanity asserts, format --- indra/llrender/llvertexbuffer.cpp | 275 +++++++++++++------------------------- 1 file changed, 90 insertions(+), 185 deletions(-) (limited to 'indra/llrender/llvertexbuffer.cpp') diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index ebf0b2fe78..93967b128d 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -68,8 +68,8 @@ 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 mod = size % LL_VBO_BLOCK_SIZE; + return mod == 0 ? size : size + (LL_VBO_BLOCK_SIZE-mod); } U32 vbo_block_index(U32 size) @@ -120,98 +120,6 @@ bool LLVertexBuffer::sUseStreamDraw = true; bool LLVertexBuffer::sUseVAO = false; bool LLVertexBuffer::sPreferStreamDraw = false; -// DEBUG, temporary -#include - -typedef struct -{ - U32 allocated_size; - void* memptr; -} glbufmem; - -static std::unordered_map buf_list; - -void add_buf(U32 name, U32 size, void* ptr) -{ - LL_WARNS("VBO_ACCOUNTING") << "ADD add_buf GLname " << name << ", size " << size << ", ptr " << ptr << LL_ENDL; - - if (0 < buf_list.count(name)) - { - if (buf_list[name].allocated_size > 0) - { - U32 a = buf_list[name].allocated_size; - LL_WARNS("VBO_ACC_ERROR") << "add_buf error, adding duplicate GLname " << name << ", size " << size << ", allocated " << a << LL_ENDL; - } - } - - buf_list[name].allocated_size = size; - buf_list[name].memptr = ptr; -} - -U32 find_buf_size(U32 name) -{ - if (buf_list.count(name) > 0) return buf_list[name].allocated_size; - return 0; -} - -bool clear_buf(U32 name, U32 size, void* ptr) -{ - LL_WARNS("VBO_ACCOUNTING") << "DEL clear_buf GLname " << name << ", size " << size << ", ptr " << ptr << LL_ENDL; - - if (0 == buf_list.count(name)) - { - LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, attempt to remove unknown GLname " << name << ", size " << size << LL_ENDL; - return false; - } - - if (buf_list[name].allocated_size == 0) - { - LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, attempt to remove empty GLname " << name << ", size " << size << LL_ENDL; - return false; - } - - if (buf_list[name].allocated_size != size) - { - U32 a = buf_list[name].allocated_size; - LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, size mismatch " << name << ", size " << size << ", allocated " << a << LL_ENDL; - buf_list[name].allocated_size = 0; - buf_list[name].memptr = nullptr; - return false; - } - - if (buf_list[name].memptr != ptr) - { - void* p = buf_list[name].memptr; - LL_WARNS("VBO_ACC_ERROR") << "clear_buf error, ptr mismatch " << name << ", ptr " << ptr << ", saved ptr " << p << LL_ENDL; - buf_list[name].allocated_size = 0; - buf_list[name].memptr = nullptr; - return false; - } - - buf_list[name].allocated_size = 0; - buf_list[name].memptr = nullptr; - return true; -} - -#if 0 - // Find the GLname in the freelist -for (U32 i = 0; i < LL_VBO_POOL_SEED_COUNT; i++) -{ - for (auto &r : mFreeList[i]) - { - if (r.mGLName == name) - { - LL_WARNS() << "Release size " << size << ", found Name " << name << " in bucket " << i << LL_ENDL; - } - } -} - -// Remove the buffer record from the free record list, by GLname -U32 i = vbo_block_index(size); -mFreeList[i].remove_if([name](Record r) {return (name == r.mGLName); }); -i++; -#endif - U32 LLVBOPool::genBuffer() { U32 ret = 0; @@ -244,107 +152,105 @@ LLVBOPool::LLVBOPool(U32 vboUsage, U32 vboType) std::fill(mMissCount.begin(), mMissCount.end(), 0); } -volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed) +volatile U8 *LLVBOPool::allocate(U32 &name, U32 size, bool for_seed) { - llassert(vbo_block_size(size) == size); - - volatile U8* ret = NULL; + llassert(vbo_block_size(size) == size); - U32 i = vbo_block_index(size); + volatile U8 *ret = NULL; - if (mFreeList.size() <= i) - { - mFreeList.resize(i+1); - } + U32 i = vbo_block_index(size); - if (mFreeList[i].empty() || for_seed) - { - //make a new buffer - name = genBuffer(); - - glBindBufferARB(mType, name); + if (mFreeList.size() <= i) + { + mFreeList.resize(i + 1); + } - if (!for_seed && i < LL_VBO_POOL_SEED_COUNT) - { //record this miss - mMissCount[i]++; + if (mFreeList[i].empty() || for_seed) + { + // make a new buffer + name = genBuffer(); + + glBindBufferARB(mType, name); + + if (!for_seed && i < LL_VBO_POOL_SEED_COUNT) + { // record this miss + mMissCount[i]++; mMissCountDirty = true; // signal to ::seedPool() } - if (mType == GL_ARRAY_BUFFER_ARB) - { - LLVertexBuffer::sAllocatedBytes += size; - } - else - { - LLVertexBuffer::sAllocatedIndexBytes += size; - } + 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); - if (mUsage != GL_DYNAMIC_COPY_ARB) - { //data will be provided by application - ret = (U8*) ll_aligned_malloc<64>(size); - if (!ret) - { - 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 - glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB); - } + if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB) + { + glBufferDataARB(mType, size, 0, mUsage); + if (mUsage != GL_DYNAMIC_COPY_ARB) + { // data will be provided by application + ret = (U8 *) ll_aligned_malloc<64>(size); + if (!ret) + { + 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 + glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB); + } - glBindBufferARB(mType, 0); + glBindBufferARB(mType, 0); - if (for_seed) - { //put into pool for future use - llassert(mFreeList.size() > i); + 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); - mMissCountDirty = true; // signal to ::seedPool() - } + Record rec; + rec.mGLName = name; + rec.mClientData = ret; - add_buf(name, size, (void*)ret); - } - else - { - name = mFreeList[i].front().mGLName; - ret = mFreeList[i].front().mClientData; + if (mType == GL_ARRAY_BUFFER_ARB) + { + sBytesPooled += size; + } + else + { + sIndexBytesPooled += size; + } + mFreeList[i].push_back(rec); + mMissCountDirty = true; // signal to ::seedPool() + } + } + else + { + name = mFreeList[i].front().mGLName; + ret = mFreeList[i].front().mClientData; - if (mType == GL_ARRAY_BUFFER_ARB) - { - sBytesPooled -= size; - } - else - { - sIndexBytesPooled -= size; - } + if (mType == GL_ARRAY_BUFFER_ARB) + { + sBytesPooled -= size; + } + else + { + sIndexBytesPooled -= size; + } - mFreeList[i].pop_front(); + mFreeList[i].pop_front(); mMissCountDirty = true; // signal to ::seedPool() - } + } - return ret; + return ret; } void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size) @@ -362,8 +268,6 @@ void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size) { LLVertexBuffer::sAllocatedIndexBytes -= size; } - - clear_buf(name, size, (void*)buffer); } void LLVBOPool::seedPool() @@ -371,7 +275,7 @@ void LLVBOPool::seedPool() if (mMissCountDirty) { U32 dummy_name = 0; - U32 size = LL_VBO_BLOCK_SIZE; + U32 size = LL_VBO_BLOCK_SIZE; for (U32 i = 0; i < LL_VBO_POOL_SEED_COUNT; i++) { @@ -389,8 +293,6 @@ void LLVBOPool::seedPool() } } - - void LLVBOPool::cleanup() { U32 size = LL_VBO_BLOCK_SIZE; @@ -410,8 +312,6 @@ void LLVBOPool::cleanup() ll_aligned_free<64>((void*) r.mClientData); } - clear_buf(r.mGLName, size, (void*)r.mClientData); - l.pop_front(); if (mType == GL_ARRAY_BUFFER_ARB) @@ -1019,6 +919,11 @@ void LLVertexBuffer::cleanupClass() sStreamVBOPool.cleanup(); sDynamicVBOPool.cleanup(); sDynamicCopyVBOPool.cleanup(); + + llassert(0 == LLVBOPool::sBytesPooled); + llassert(0 == LLVBOPool::sIndexBytesPooled); + llassert(0 == sAllocatedBytes); + llassert(0 == sAllocatedIndexBytes); } //---------------------------------------------------------------------------- -- cgit v1.2.3