// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include "nel/3d/vegetablevb_allocator.h" #include "nel/3d/vegetable_def.h" using namespace std; using namespace NLMISC; namespace NL3D { /* Once a reallocation of a VB occurs, how many vertices we add to the re-allocation, to avoid as possible reallocations. */ #define NL3D_VEGETABLE_VERTEX_ALLOCATE_SECURITY 1024 /* The start size of the array. */ #define NL3D_VEGETABLE_VERTEX_ALLOCATE_START 4048 #define NL3D_VEGETABLE_VERTEX_FREE_MEMORY_RESERVE 1024 // *************************************************************************** CVegetableVBAllocator::CVegetableVBAllocator() { _Type= VBTypeUnlit; _MaxVertexInBufferHard= 0; // Init free list _VertexFreeMemory.reserve(NL3D_VEGETABLE_VERTEX_FREE_MEMORY_RESERVE); _NumVerticesAllocated= 0; // Init vbhard _VBHardOk= false; _AGPBufferPtr= NULL; _RAMBufferPtr= NULL; } // *************************************************************************** void CVegetableVBAllocator::init(TVBType type, uint maxVertexInBufferHard) { _Type= type; _MaxVertexInBufferHard= maxVertexInBufferHard; // According to _Type, build VB format, and create VertexProgram setupVBFormat(); } // *************************************************************************** CVegetableVBAllocator::~CVegetableVBAllocator() { clear(); } // *************************************************************************** void CVegetableVBAllocator::updateDriver(IDriver *driver) { // test change of driver. nlassert(driver && !_VBHard.isLocked()); // If change of driver if( _Driver==NULL || driver!=_Driver || (!_VBHard.isResident() && (_VBHard.capacity()!=0))) { // delete old VBHard. deleteVertexBufferHard(); _Driver= driver; _VBHardOk= (_MaxVertexInBufferHard>0) && (_Driver->supportVertexBufferHard()); /* Because so much lock/unlock are performed during a frame (refine/clip etc...). we must disable VBHard for ATI Gl extension. NB: CLandscape don't do this and fast copy the entire VB each frame. This is not possible for vegetables because the VB describe all Vegetable around the camera, not only what is in frustrum. Hence a fast copy each frame would copy far too much unseen vertices (4x). */ if(_Driver->slowUnlockVertexBufferHard()) _VBHardOk= false; // Driver must support VP. nlassert(_Driver->isVertexProgramSupported()); // must reallocate the VertexBuffer. if( _NumVerticesAllocated>0 ) allocateVertexBufferAndFillVBHard(_NumVerticesAllocated); } else { // if VBHard possible, and if vbHardDeleted but space needed, reallocate. if( _VBHardOk && _VBHard.getNumVertices()==0 && _NumVerticesAllocated>0 ) allocateVertexBufferAndFillVBHard(_NumVerticesAllocated); } } // *************************************************************************** void CVegetableVBAllocator::clear() { // clear list. _VertexFreeMemory.clear(); _NumVerticesAllocated= 0; // must unlock for vbhard and vbsoft unlockBuffer(); // delete the VB. deleteVertexBufferHard(); // really delete the VB soft too _VBSoft.deleteAllVertices(); // clear other states. _Driver= NULL; _VBHardOk= false; } // *************************************************************************** void CVegetableVBAllocator::lockBuffer() { // force unlock unlockBuffer(); // need to lock only if the VBHard is created if(_VBHardOk) { // lock the VBHard for writing _VBHard.lock(_VBAHard); _AGPBufferPtr=(uint8*)_VBAHard.getVertexCoordPointer(); // lock the Input VertexBuffer for reading _VBSoft.lock(_VBASoft); _RAMBufferPtr=(const uint8*)_VBASoft.getVertexCoordPointer(); } } // *************************************************************************** void CVegetableVBAllocator::unlockBuffer() { // unlock the VBHard _VBAHard.unlock(); _AGPBufferPtr= NULL; // unlock the VBSoft _VBASoft.unlock(); _RAMBufferPtr= NULL; } // *************************************************************************** uint CVegetableVBAllocator::getNumUserVerticesAllocated() const { // get the number of vertices which are allocated by allocateVertex(). return _NumVerticesAllocated - (uint)_VertexFreeMemory.size(); } // *************************************************************************** bool CVegetableVBAllocator::exceedMaxVertexInBufferHard(uint numAddVerts) const { return (getNumUserVerticesAllocated() + numAddVerts) > _MaxVertexInBufferHard; } // *************************************************************************** uint CVegetableVBAllocator::allocateVertex() { // if no more free, allocate. if( _VertexFreeMemory.size()==0 ) { // enlarge capacity. uint newResize; if(_NumVerticesAllocated==0) newResize= NL3D_VEGETABLE_VERTEX_ALLOCATE_START; else newResize= NL3D_VEGETABLE_VERTEX_ALLOCATE_SECURITY; // try to not overlap _MaxVertexInBufferHard limit, to avoid VBufferHard to be disabled. if(_NumVerticesAllocated<_MaxVertexInBufferHard && _NumVerticesAllocated+newResize > _MaxVertexInBufferHard) { newResize= _MaxVertexInBufferHard - _NumVerticesAllocated; } _NumVerticesAllocated+= newResize; // re-allocate VB. allocateVertexBufferAndFillVBHard(_NumVerticesAllocated); // resize infos on vertices. _VertexInfos.resize(_NumVerticesAllocated); // Fill list of free elements. for(uint i=0;iactiveVertexBuffer(_VBHard); else _Driver->activeVertexBuffer(_VBSoft); } // *************************************************************************** // *************************************************************************** // Vertex Buffer hard. // *************************************************************************** // *************************************************************************** // *************************************************************************** void CVegetableVBAllocator::deleteVertexBufferHard() { // must unlock VBhard before. unlockBuffer(); // test (refptr) if the object still exist in memory. if(_VBHard.getNumVertices()!=0) { // A vbufferhard should still exist only if driver still exist. nlassert(_Driver!=NULL); // delete it from driver. _VBHard.deleteAllVertices (); } } // *************************************************************************** void CVegetableVBAllocator::allocateVertexBufferAndFillVBHard(uint32 numVertices) { // no allocation must be done if the Driver is not setuped, or if the driver has been deleted by refPtr. nlassert(_Driver); // must unlock VBhard and VBSoft before. bool wasLocked= bufferLocked(); unlockBuffer(); // resize the Soft VB. _VBSoft.setNumVertices(numVertices); // try to allocate a vbufferhard if possible. if( _VBHardOk ) { /* Prefer allocate the VBHard with the Max Vertex count only ONCE, to avoid problems with AGP allocation The problem is with 50000 AGP vertices, it costs 3 Mo. If we do iterative allocation (500 Ko, 600 Ko, 700 Ko,.....) we may have problem with free holes (Vegetable could no more enter in the 16 or 8 Mo AGP limit!) */ if(_VBHard.getNumVertices() != _MaxVertexInBufferHard) { // delete possible old _VBHard. if(_VBHard.getNumVertices()!=0) { // VertexBufferHard lifetime < Driver lifetime. nlassert(_Driver!=NULL); _VBHard.deleteAllVertices(); } // try to create new one, in AGP Ram // If too many vertices wanted, abort VBHard. if(numVertices <= _MaxVertexInBufferHard) { _VBHard = _VBSoft; _VBHard.setPreferredMemory(CVertexBuffer::AGPPreferred, false); _VBHard.setNumVertices (_MaxVertexInBufferHard); // Force this VB to be hard nlverify (_Driver->activeVertexBuffer (_VBHard)); nlassert (_VBHard.isResident()); // if fails, abort VBHard. if (_VBHard.getLocation() == CVertexBuffer::RAMResident) _VBHard.deleteAllVertices(); // Set Name For lock Profiling. if(_VBHard.getNumVertices()!=0) _VBHard.setName("VegetableVB"); } else _VBHard.deleteAllVertices(); // If KO, never try again. if(_VBHard.getNumVertices()==0) _VBHardOk= false; } } // if still OK, must refill the VBHard. Slow, but rare if(_VBHardOk) { // else, fill this AGP VBuffer Hard. // lock before the AGP buffer lockBuffer(); // copy all the vertices to AGP. memcpy(_AGPBufferPtr, _RAMBufferPtr, _VBSoft.getVertexSize() * numVertices); // If was not locked before, unlock this VB if(!wasLocked) unlockBuffer(); } //nlinfo("VEGET: Alloc %d verts. %s", numVertices, _VBHardOk?"VBHard":"VBSoft"); } // *************************************************************************** void CVegetableVBAllocator::setupVBFormat() { // Build the Vertex Format. _VBSoft.clearValueEx(); // if lighted, need world space normal and AmbientColor for each vertex. if( _Type == VBTypeLighted ) { _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_POS, CVertexBuffer::Float3); // v[0] _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_NORMAL, CVertexBuffer::Float3); // v[2] _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_BENDINFO, CVertexBuffer::Float3); // v[9] } // If unlit else { // slightly different VertexProgram, v[0].w== BendWeight, and v[9].x== v[0].norm() _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_POS, CVertexBuffer::Float4); // v[0] // Unlit VP has BlendDistance in v[9].w _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_BENDINFO, CVertexBuffer::Float4); // v[9] } _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_COLOR0, CVertexBuffer::UChar4); // v[3] _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_COLOR1, CVertexBuffer::UChar4); // v[4] _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_TEX0, CVertexBuffer::Float2); // v[8] _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_CENTER, CVertexBuffer::Float3); // v[10] _VBSoft.initEx(); } } // NL3D