/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2006 Torus Knot Software Ltd Also see acknowledgements in Readme.html This program 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; either version 2 of the License, or (at your option) any later version. This program 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. You may alternatively use this source under the terms of a specific version of the OGRE Unrestricted License provided you have obtained such a license from Torus Knot Software Ltd. ----------------------------------------------------------------------------- */ #include "OgreGLHardwareBufferManager.h" #include "OgreGLHardwareVertexBuffer.h" #include "OgreGLHardwareIndexBuffer.h" #include "OgreHardwareBuffer.h" #include "OgreAlignedAllocator.h" namespace Ogre { //----------------------------------------------------------------------- // Scratch pool management (32 bit structure) struct GLScratchBufferAlloc { /// Size in bytes uint32 size: 31; /// Free? (pack with size) uint32 free: 1; }; #define SCRATCH_POOL_SIZE 1 * 1024 * 1024 //--------------------------------------------------------------------- GLHardwareBufferManager::GLHardwareBufferManager() { // Init scratch pool // TODO make it a configurable size? // 32-bit aligned buffer mScratchBufferPool = static_cast(AlignedMemory::allocate(SCRATCH_POOL_SIZE, 32)); GLScratchBufferAlloc* ptrAlloc = (GLScratchBufferAlloc*)mScratchBufferPool; ptrAlloc->size = SCRATCH_POOL_SIZE - sizeof(GLScratchBufferAlloc); ptrAlloc->free = 1; } //----------------------------------------------------------------------- GLHardwareBufferManager::~GLHardwareBufferManager() { destroyAllDeclarations(); destroyAllBindings(); AlignedMemory::deallocate(mScratchBufferPool); } //----------------------------------------------------------------------- HardwareVertexBufferSharedPtr GLHardwareBufferManager::createVertexBuffer( size_t vertexSize, size_t numVerts, HardwareBuffer::Usage usage, bool useShadowBuffer) { GLHardwareVertexBuffer* buf = new GLHardwareVertexBuffer(vertexSize, numVerts, usage, useShadowBuffer); { OGRE_LOCK_MUTEX(mVertexBuffersMutex) mVertexBuffers.insert(buf); } return HardwareVertexBufferSharedPtr(buf); } //----------------------------------------------------------------------- HardwareIndexBufferSharedPtr GLHardwareBufferManager::createIndexBuffer( HardwareIndexBuffer::IndexType itype, size_t numIndexes, HardwareBuffer::Usage usage, bool useShadowBuffer) { GLHardwareIndexBuffer* buf = new GLHardwareIndexBuffer(itype, numIndexes, usage, useShadowBuffer); { OGRE_LOCK_MUTEX(mIndexBuffersMutex) mIndexBuffers.insert(buf); } return HardwareIndexBufferSharedPtr(buf); } //--------------------------------------------------------------------- GLenum GLHardwareBufferManager::getGLUsage(unsigned int usage) { switch(usage) { case HardwareBuffer::HBU_STATIC: case HardwareBuffer::HBU_STATIC_WRITE_ONLY: return GL_STATIC_DRAW_ARB; case HardwareBuffer::HBU_DYNAMIC: case HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY: return GL_DYNAMIC_DRAW_ARB; case HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE: return GL_STREAM_DRAW_ARB; default: return GL_DYNAMIC_DRAW_ARB; }; } //--------------------------------------------------------------------- GLenum GLHardwareBufferManager::getGLType(unsigned int type) { switch(type) { case VET_FLOAT1: case VET_FLOAT2: case VET_FLOAT3: case VET_FLOAT4: return GL_FLOAT; case VET_SHORT1: case VET_SHORT2: case VET_SHORT3: case VET_SHORT4: return GL_SHORT; case VET_COLOUR: case VET_COLOUR_ABGR: case VET_COLOUR_ARGB: case VET_UBYTE4: return GL_UNSIGNED_BYTE; default: return 0; }; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- void* GLHardwareBufferManager::allocateScratch(uint32 size) { // simple forward link search based on alloc sizes // not that fast but the list should never get that long since not many // locks at once (hopefully) OGRE_LOCK_MUTEX(mScratchMutex) // Alignment - round up the size to 32 bits // control blocks are 32 bits too so this packs nicely if (size % 4 != 0) { size += 4 - (size % 4); } uint32 bufferPos = 0; while (bufferPos < SCRATCH_POOL_SIZE) { GLScratchBufferAlloc* pNext = (GLScratchBufferAlloc*)(mScratchBufferPool + bufferPos); // Big enough? if (pNext->free && pNext->size >= size) { // split? And enough space for control block if(pNext->size > size + sizeof(GLScratchBufferAlloc)) { uint32 offset = sizeof(GLScratchBufferAlloc) + size; GLScratchBufferAlloc* pSplitAlloc = (GLScratchBufferAlloc*) (mScratchBufferPool + bufferPos + offset); pSplitAlloc->free = 1; // split size is remainder minus new control block pSplitAlloc->size = pNext->size - size - sizeof(GLScratchBufferAlloc); // New size of current pNext->size = size; } // allocate and return pNext->free = 0; // return pointer just after this control block (++ will do that for us) return ++pNext; } bufferPos += sizeof(GLScratchBufferAlloc) + pNext->size; } // no available alloc return 0; } //--------------------------------------------------------------------- void GLHardwareBufferManager::deallocateScratch(void* ptr) { OGRE_LOCK_MUTEX(mScratchMutex) // Simple linear search dealloc uint32 bufferPos = 0; GLScratchBufferAlloc* pLast = 0; while (bufferPos < SCRATCH_POOL_SIZE) { GLScratchBufferAlloc* pCurrent = (GLScratchBufferAlloc*)(mScratchBufferPool + bufferPos); // Pointers match? if ((mScratchBufferPool + bufferPos + sizeof(GLScratchBufferAlloc)) == ptr) { // dealloc pCurrent->free = 1; // merge with previous if (pLast && pLast->free) { // adjust buffer pos bufferPos -= (pLast->size + sizeof(GLScratchBufferAlloc)); // merge free space pLast->size += pCurrent->size + sizeof(GLScratchBufferAlloc); pCurrent = pLast; } // merge with next uint32 offset = bufferPos + pCurrent->size + sizeof(GLScratchBufferAlloc); if (offset < SCRATCH_POOL_SIZE) { GLScratchBufferAlloc* pNext = (GLScratchBufferAlloc*)( mScratchBufferPool + offset); if (pNext->free) { pCurrent->size += pNext->size + sizeof(GLScratchBufferAlloc); } } // done return; } bufferPos += sizeof(GLScratchBufferAlloc) + pCurrent->size; pLast = pCurrent; } // Should never get here unless there's a corruption assert (false && "Memory deallocation error"); } }