Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / vegetablevb_allocator.cpp
blob72f7680386acf48050bc6c8e12dbb850fbec9f7f
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
23 #include "nel/3d/vegetablevb_allocator.h"
24 #include "nel/3d/vegetable_def.h"
25 #include "nel/3d/debug_vb.h"
28 using namespace std;
29 using namespace NLMISC;
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 namespace NL3D
40 Once a reallocation of a VB occurs, how many vertices we add to the re-allocation, to avoid
41 as possible reallocations.
43 #define NL3D_VEGETABLE_VERTEX_ALLOCATE_SECURITY 1024
45 The start size of the array.
47 #define NL3D_VEGETABLE_VERTEX_ALLOCATE_START 4048
50 #define NL3D_VEGETABLE_VERTEX_FREE_MEMORY_RESERVE 1024
53 // ***************************************************************************
54 CVegetableVBAllocator::CVegetableVBAllocator()
56 _Type= VBTypeUnlit;
57 _MaxVertexInBufferHard= 0;
59 // Init free list
60 _VertexFreeMemory.reserve(NL3D_VEGETABLE_VERTEX_FREE_MEMORY_RESERVE);
61 _NumVerticesAllocated= 0;
63 // Init vbhard
64 _VBHardOk= false;
65 _AGPBufferPtr= NULL;
66 _RAMBufferPtr= NULL;
70 // ***************************************************************************
71 void CVegetableVBAllocator::init(TVBType type, uint maxVertexInBufferHard)
73 _Type= type;
74 _MaxVertexInBufferHard= maxVertexInBufferHard;
76 // According to _Type, build VB format, and create VertexProgram
77 setupVBFormat();
81 // ***************************************************************************
82 CVegetableVBAllocator::~CVegetableVBAllocator()
84 clear();
87 // ***************************************************************************
88 void CVegetableVBAllocator::updateDriver(IDriver *driver)
90 // test change of driver.
91 nlassert(driver && !_VBHard.isLocked());
92 // If change of driver
93 if( _Driver==NULL || driver!=_Driver || (!_VBHard.isResident() && (_VBHard.capacity()!=0)))
95 // delete old VBHard.
96 deleteVertexBufferHard();
97 _Driver= driver;
98 _VBHardOk= (_MaxVertexInBufferHard>0) && (_Driver->supportVertexBufferHard());
99 /* Because so much lock/unlock are performed during a frame (refine/clip etc...).
100 we must disable VBHard for ATI Gl extension.
101 NB: CLandscape don't do this and fast copy the entire VB each frame.
102 This is not possible for vegetables because the VB describe all Vegetable around the camera, not only
103 what is in frustrum. Hence a fast copy each frame would copy far too much unseen vertices (4x).
105 if(_Driver->slowUnlockVertexBufferHard())
106 _VBHardOk= false;
108 // Driver must support VP.
109 nlassert(_Driver->supportVertexProgram(CVertexProgram::nelvp)
110 // || _Driver->supportVertexProgram(CVertexProgram::glsl330v) // TODO_VP_GLSL
113 // must reallocate the VertexBuffer.
114 if( _NumVerticesAllocated>0 )
115 allocateVertexBufferAndFillVBHard(_NumVerticesAllocated);
117 else
119 // if VBHard possible, and if vbHardDeleted but space needed, reallocate.
120 if( _VBHardOk && _VBHard.getNumVertices()==0 && _NumVerticesAllocated>0 )
121 allocateVertexBufferAndFillVBHard(_NumVerticesAllocated);
126 // ***************************************************************************
127 void CVegetableVBAllocator::clear()
129 // clear list.
130 _VertexFreeMemory.clear();
131 _NumVerticesAllocated= 0;
133 // must unlock for vbhard and vbsoft
134 unlockBuffer();
136 // delete the VB.
137 deleteVertexBufferHard();
138 // really delete the VB soft too
139 _VBSoft.deleteAllVertices();
141 // clear other states.
142 _Driver= NULL;
143 _VBHardOk= false;
147 // ***************************************************************************
148 void CVegetableVBAllocator::lockBuffer()
150 // force unlock
151 unlockBuffer();
153 // need to lock only if the VBHard is created
154 if(_VBHardOk)
156 // lock the VBHard for writing
157 _VBHard.lock(_VBAHard);
158 _AGPBufferPtr=(uint8*)_VBAHard.getVertexCoordPointer();
160 // lock the Input VertexBuffer for reading
161 _VBSoft.lock(_VBASoft);
162 _RAMBufferPtr=(const uint8*)_VBASoft.getVertexCoordPointer();
166 // ***************************************************************************
167 void CVegetableVBAllocator::unlockBuffer()
169 // unlock the VBHard
170 _VBAHard.unlock();
171 _AGPBufferPtr= NULL;
173 // unlock the VBSoft
174 _VBASoft.unlock();
175 _RAMBufferPtr= NULL;
179 // ***************************************************************************
180 uint CVegetableVBAllocator::getNumUserVerticesAllocated() const
182 // get the number of vertices which are allocated by allocateVertex().
183 return _NumVerticesAllocated - (uint)_VertexFreeMemory.size();
186 // ***************************************************************************
187 bool CVegetableVBAllocator::exceedMaxVertexInBufferHard(uint numAddVerts) const
189 return (getNumUserVerticesAllocated() + numAddVerts) > _MaxVertexInBufferHard;
193 // ***************************************************************************
194 uint CVegetableVBAllocator::allocateVertex()
196 // if no more free, allocate.
197 if( _VertexFreeMemory.empty() )
199 // enlarge capacity.
200 uint newResize;
201 if(_NumVerticesAllocated==0)
202 newResize= NL3D_VEGETABLE_VERTEX_ALLOCATE_START;
203 else
204 newResize= NL3D_VEGETABLE_VERTEX_ALLOCATE_SECURITY;
205 // try to not overlap _MaxVertexInBufferHard limit, to avoid VBufferHard to be disabled.
206 if(_NumVerticesAllocated<_MaxVertexInBufferHard && _NumVerticesAllocated+newResize > _MaxVertexInBufferHard)
208 newResize= _MaxVertexInBufferHard - _NumVerticesAllocated;
210 _NumVerticesAllocated+= newResize;
211 // re-allocate VB.
212 allocateVertexBufferAndFillVBHard(_NumVerticesAllocated);
213 // resize infos on vertices.
214 _VertexInfos.resize(_NumVerticesAllocated);
216 // Fill list of free elements.
217 for(uint i=0;i<newResize;i++)
219 // create a new entry which points to this vertex.
220 // the list is made so allocation is in growing order.
221 _VertexFreeMemory.push_back( _NumVerticesAllocated - (i+1) );
223 // Mark as free the new vertices. (Debug).
224 _VertexInfos[_NumVerticesAllocated - (i+1)].Free= true;
228 // get a vertex (pop_back).
229 uint id= _VertexFreeMemory.back();
230 // delete this vertex free entry.
231 _VertexFreeMemory.pop_back();
233 // check and Mark as not free the vertex. (Debug).
234 nlassert(id<_NumVerticesAllocated);
235 nlassert(_VertexInfos[id].Free);
236 _VertexInfos[id].Free= false;
239 return id;
242 // ***************************************************************************
243 void CVegetableVBAllocator::deleteVertex(uint vid)
245 // check and Mark as free the vertex. (Debug).
246 nlassert(vid<_NumVerticesAllocated);
247 nlassert(!_VertexInfos[vid].Free);
248 _VertexInfos[vid].Free= true;
250 // Add this vertex to the free list.
251 // create a new entry which points to this vertex.
252 _VertexFreeMemory.push_back( vid );
255 // ***************************************************************************
256 void CVegetableVBAllocator::flushVertex(uint i)
258 if(_VBHardOk)
260 nlassert(_VBHard.getNumVertices() && _VBHard.isLocked() && _VBSoft.isLocked());
262 // copy the VB soft to the VBHard.
263 uint size= _VBSoft.getVertexSize();
264 const void *src= _RAMBufferPtr + i*size;
265 void *dst= _AGPBufferPtr + i*size;
266 CHECK_VBA_RANGE(_VBAHard, (uint8 *) dst, size);
267 CHECK_VBA_RANGE(_VBASoft, (uint8 *) src, size);
268 memcpy(dst, src, size);
272 // ***************************************************************************
273 void CVegetableVBAllocator::activate()
275 nlassert(_Driver);
276 nlassert(!_VBHard.isLocked());
277 nlassert(!_VBSoft.isLocked());
279 // Activate VB.
280 if(_VBHard.getNumVertices())
281 _Driver->activeVertexBuffer(_VBHard);
282 else
283 _Driver->activeVertexBuffer(_VBSoft);
287 // ***************************************************************************
288 // ***************************************************************************
289 // Vertex Buffer hard.
290 // ***************************************************************************
291 // ***************************************************************************
294 // ***************************************************************************
295 void CVegetableVBAllocator::deleteVertexBufferHard()
297 // must unlock VBhard before.
298 unlockBuffer();
300 // test (refptr) if the object still exist in memory.
301 if(_VBHard.getNumVertices()!=0)
303 // A vbufferhard should still exist only if driver still exist.
304 nlassert(_Driver!=NULL);
306 // delete it from driver.
307 _VBHard.deleteAllVertices ();
312 // ***************************************************************************
313 void CVegetableVBAllocator::allocateVertexBufferAndFillVBHard(uint32 numVertices)
315 // no allocation must be done if the Driver is not setuped, or if the driver has been deleted by refPtr.
316 nlassert(_Driver);
318 // must unlock VBhard and VBSoft before.
319 bool wasLocked= bufferLocked();
320 unlockBuffer();
322 // resize the Soft VB.
323 _VBSoft.setNumVertices(numVertices);
325 // try to allocate a vbufferhard if possible.
326 if( _VBHardOk )
328 /* Prefer allocate the VBHard with the Max Vertex count only ONCE,
329 to avoid problems with AGP allocation
330 The problem is with 50000 AGP vertices, it costs 3 Mo. If we do iterative allocation
331 (500 Ko, 600 Ko, 700 Ko,.....)
332 we may have problem with free holes (Vegetable could no more enter in the 16 or 8 Mo AGP limit!)
334 if(_VBHard.getNumVertices() != _MaxVertexInBufferHard)
336 // delete possible old _VBHard.
337 if(_VBHard.getNumVertices()!=0)
339 // VertexBufferHard lifetime < Driver lifetime.
340 nlassert(_Driver!=NULL);
341 _VBHard.deleteAllVertices();
344 // try to create new one, in AGP Ram
345 // If too many vertices wanted, abort VBHard.
346 if(numVertices <= _MaxVertexInBufferHard)
348 _VBHard = _VBSoft;
349 _VBHard.setPreferredMemory(CVertexBuffer::AGPPreferred, false);
350 _VBHard.setNumVertices (_MaxVertexInBufferHard);
352 // Force this VB to be hard
353 nlverify (_Driver->activeVertexBuffer (_VBHard));
354 nlassert (_VBHard.isResident());
356 // if fails, abort VBHard.
357 if (_VBHard.getLocation() == CVertexBuffer::RAMResident)
358 _VBHard.deleteAllVertices();
360 // Set Name For lock Profiling.
361 if(_VBHard.getNumVertices()!=0)
362 _VBHard.setName("VegetableVB");
364 else
365 _VBHard.deleteAllVertices();
367 // If KO, never try again.
368 if(_VBHard.getNumVertices()==0)
369 _VBHardOk= false;
373 // if still OK, must refill the VBHard. Slow, but rare
374 if(_VBHardOk)
376 // else, fill this AGP VBuffer Hard.
377 // lock before the AGP buffer
378 lockBuffer();
380 // copy all the vertices to AGP.
381 memcpy(_AGPBufferPtr, _RAMBufferPtr, _VBSoft.getVertexSize() * numVertices);
383 // If was not locked before, unlock this VB
384 if(!wasLocked)
385 unlockBuffer();
388 //nlinfo("VEGET: Alloc %d verts. %s", numVertices, _VBHardOk?"VBHard":"VBSoft");
392 // ***************************************************************************
393 void CVegetableVBAllocator::setupVBFormat()
395 // Build the Vertex Format.
396 _VBSoft.clearValueEx();
398 // if lighted, need world space normal and AmbientColor for each vertex.
399 if( _Type == VBTypeLighted )
401 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_POS, CVertexBuffer::Float3); // v[0]
402 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_NORMAL, CVertexBuffer::Float3); // v[2]
403 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_BENDINFO, CVertexBuffer::Float3); // v[9]
405 // If unlit
406 else
408 // slightly different VertexProgram, v[0].w== BendWeight, and v[9].x== v[0].norm()
409 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_POS, CVertexBuffer::Float4); // v[0]
410 // Unlit VP has BlendDistance in v[9].w
411 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_BENDINFO, CVertexBuffer::Float4); // v[9]
413 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_COLOR0, CVertexBuffer::UChar4); // v[3]
414 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_COLOR1, CVertexBuffer::UChar4); // v[4]
415 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_TEX0, CVertexBuffer::Float2); // v[8]
416 _VBSoft.addValueEx(NL3D_VEGETABLE_VPPOS_CENTER, CVertexBuffer::Float3); // v[10]
417 _VBSoft.initEx();
423 } // NL3D