1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
23 #include "nel/3d/vegetablevb_allocator.h"
24 #include "nel/3d/vegetable_def.h"
25 #include "nel/3d/debug_vb.h"
29 using namespace NLMISC
;
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()
57 _MaxVertexInBufferHard
= 0;
60 _VertexFreeMemory
.reserve(NL3D_VEGETABLE_VERTEX_FREE_MEMORY_RESERVE
);
61 _NumVerticesAllocated
= 0;
70 // ***************************************************************************
71 void CVegetableVBAllocator::init(TVBType type
, uint maxVertexInBufferHard
)
74 _MaxVertexInBufferHard
= maxVertexInBufferHard
;
76 // According to _Type, build VB format, and create VertexProgram
81 // ***************************************************************************
82 CVegetableVBAllocator::~CVegetableVBAllocator()
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)))
96 deleteVertexBufferHard();
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())
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
);
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()
130 _VertexFreeMemory
.clear();
131 _NumVerticesAllocated
= 0;
133 // must unlock for vbhard and vbsoft
137 deleteVertexBufferHard();
138 // really delete the VB soft too
139 _VBSoft
.deleteAllVertices();
141 // clear other states.
147 // ***************************************************************************
148 void CVegetableVBAllocator::lockBuffer()
153 // need to lock only if the VBHard is created
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()
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() )
201 if(_NumVerticesAllocated
==0)
202 newResize
= NL3D_VEGETABLE_VERTEX_ALLOCATE_START
;
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
;
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;
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
)
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()
276 nlassert(!_VBHard
.isLocked());
277 nlassert(!_VBSoft
.isLocked());
280 if(_VBHard
.getNumVertices())
281 _Driver
->activeVertexBuffer(_VBHard
);
283 _Driver
->activeVertexBuffer(_VBSoft
);
287 // ***************************************************************************
288 // ***************************************************************************
289 // Vertex Buffer hard.
290 // ***************************************************************************
291 // ***************************************************************************
294 // ***************************************************************************
295 void CVegetableVBAllocator::deleteVertexBufferHard()
297 // must unlock VBhard before.
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.
318 // must unlock VBhard and VBSoft before.
319 bool wasLocked
= bufferLocked();
322 // resize the Soft VB.
323 _VBSoft
.setNumVertices(numVertices
);
325 // try to allocate a vbufferhard if possible.
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
)
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");
365 _VBHard
.deleteAllVertices();
367 // If KO, never try again.
368 if(_VBHard
.getNumVertices()==0)
373 // if still OK, must refill the VBHard. Slow, but rare
376 // else, fill this AGP VBuffer Hard.
377 // lock before the AGP buffer
380 // copy all the vertices to AGP.
381 memcpy(_AGPBufferPtr
, _RAMBufferPtr
, _VBSoft
.getVertexSize() * numVertices
);
383 // If was not locked before, unlock this VB
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]
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]