Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / driver / direct3d / driver_direct3d_index.cpp
blobb9dca00902e33f360de0ef744f1924ce6d6768a5
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) 2014 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 "stddirect3d.h"
22 #include "nel/3d/index_buffer.h"
23 #include "nel/3d/light.h"
24 #include "nel/misc/rect.h"
25 #include "nel/3d/viewport.h"
26 #include "nel/3d/scissor.h"
27 #include "nel/3d/u_driver.h"
29 #include "driver_direct3d.h"
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 using namespace std;
36 using namespace NLMISC;
38 namespace NL3D
41 // ***************************************************************************
43 CIBDrvInfosD3D::CIBDrvInfosD3D(CDriverD3D *drv, ItIBDrvInfoPtrList it, CIndexBuffer *ib) : IIBDrvInfos(drv, it, ib)
45 H_AUTO_D3D(CIBDrvInfosD3D_CIBDrvInfosD3D)
46 Driver = drv;
47 IndexBuffer = NULL;
48 VolatileIndexBuffer = NULL;
52 // ***************************************************************************
54 uint indexCount=0;
56 CIBDrvInfosD3D::~CIBDrvInfosD3D()
58 H_AUTO_D3D(CIBDrvInfosD3D_CIBDrvInfosD3DDtor);
59 // Restore non resident memory
60 if (IndexBufferPtr)
62 IndexBufferPtr->setLocation(CIndexBuffer::NotResident);
63 IndexBufferPtr= NULL;
66 // release index buffer
67 if (IndexBuffer && !Volatile)
69 if (Driver)
71 if (Driver->_IndexBufferCache.IndexBuffer == IndexBuffer)
73 Driver->_IndexBufferCache.IndexBuffer = NULL;
74 Driver->touchRenderVariable(&Driver->_IndexBufferCache);
77 indexCount--;
78 IndexBuffer->Release();
82 // ***************************************************************************
84 void *CIBDrvInfosD3D::lock (uint first, uint last, bool readOnly)
86 H_AUTO_D3D(CIBDrvInfosD3D_lock);
87 nlassert (first != last);
88 CDriverD3D *driver = static_cast<CDriverD3D*>(_Driver);
90 if (driver->getMaxVertexIndex() <= 0xffff && getFormat() != CIndexBuffer::Indices16)
92 nlassert(getFormat() == CIndexBuffer::Indices32);
93 // 32-bit index not supported -> uses RAM mirror
94 nlassert(!RamVersion.empty());
95 return &RamVersion[0];
97 else
99 if (Volatile)
101 // Lock the good buffer
102 CVolatileIndexBuffer **buffer = NULL;
103 if (getFormat() == CIndexBuffer::Indices16)
105 buffer = VolatileRAM ? (&driver->_VolatileIndexBuffer16RAM[driver->_CurrentRenderPass&1]):(&driver->_VolatileIndexBuffer16AGP[driver->_CurrentRenderPass&1]);
107 else if (getFormat() == CIndexBuffer::Indices32)
109 buffer = VolatileRAM ? (&driver->_VolatileIndexBuffer32RAM[driver->_CurrentRenderPass&1]):(&driver->_VolatileIndexBuffer32AGP[driver->_CurrentRenderPass&1]);
111 else
113 nlassert(0);
115 void *ptr = (*buffer)->lock ((last-first)*getIndexNumBytes(), Offset);
116 if (!ptr)
118 // buffer full, swap them
119 CVolatileIndexBuffer **bufferOther;
120 if (getFormat() == CIndexBuffer::Indices16)
122 bufferOther = VolatileRAM ? (&driver->_VolatileIndexBuffer16RAM[(driver->_CurrentRenderPass + 1) &1]):(&driver->_VolatileIndexBuffer16AGP[(driver->_CurrentRenderPass + 1 ) &1]);
124 else
126 bufferOther = VolatileRAM ? (&driver->_VolatileIndexBuffer32RAM[(driver->_CurrentRenderPass + 1) &1]):(&driver->_VolatileIndexBuffer32AGP[(driver->_CurrentRenderPass + 1 ) &1]);
128 std::swap(*buffer, *bufferOther);
129 (*buffer)->reset();
130 ptr = (*buffer)->lock ((last-first)*getIndexNumBytes(), Offset);
131 nlassert(ptr);
133 nlassert(!VolatileIndexBuffer);
134 VolatileIndexBuffer = *buffer;
135 IndexBuffer = (*buffer)->IndexBuffer;
136 ptr = (uint8 *) ptr - first * getIndexNumBytes();
138 // Current lock time
139 VolatileLockTime = driver->_CurrentRenderPass;
141 // Touch the index buffer
142 driver->touchRenderVariable (&driver->_IndexBufferCache);
144 return ptr;
146 else
148 nlassert (IndexBuffer);
149 // Lock Profile?
150 TTicks beforeLock = 0;
151 if(driver->_IBProfiling /*&& Hardware*/)
153 beforeLock= CTime::getPerformanceTime();
155 void *pbData;
156 HRESULT result = IndexBuffer->Lock ( first*getIndexNumBytes(), (last-first)*getIndexNumBytes(), &pbData, readOnly?D3DLOCK_READONLY:0);
157 nlassert(result == D3D_OK);
158 // Lock Profile?
159 if(driver->_IBProfiling /*&& Hardware*/)
161 TTicks afterLock;
162 afterLock= CTime::getPerformanceTime();
163 driver->appendIBLockProfile(afterLock-beforeLock, IndexBufferPtr);
165 if (result == D3D_OK) return pbData;
168 return NULL;
171 // ***************************************************************************
173 void CIBDrvInfosD3D::unlock (uint /* first */, uint /* last */)
175 H_AUTO_D3D(CIBDrvInfosD3D_unlock)
176 CDriverD3D *driver = static_cast<CDriverD3D*>(_Driver);
177 if (driver->getMaxVertexIndex() > 0xffff || getFormat() == CIndexBuffer::Indices16)
179 if (Volatile)
181 nlassert(VolatileIndexBuffer);
182 VolatileIndexBuffer->unlock ();
183 VolatileIndexBuffer = NULL;
185 else
187 if (IndexBuffer) IndexBuffer->Unlock ();
192 // ***************************************************************************
194 DWORD RemapIndexBufferUsage[CIndexBuffer::LocationCount]=
196 D3DUSAGE_DYNAMIC, // RAMResident
197 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, // AGPResident
198 D3DUSAGE_WRITEONLY, // VRAMResident
199 0, // Not used
202 // ***************************************************************************
204 D3DPOOL RemapIndexBufferPool[CIndexBuffer::LocationCount]=
206 D3DPOOL_SYSTEMMEM, // RAMResident
207 D3DPOOL_DEFAULT, // AGPResident
208 D3DPOOL_DEFAULT, // VRAMResident
209 D3DPOOL_DEFAULT, // Not used
212 // ***************************************************************************
214 bool CDriverD3D::activeIndexBuffer(CIndexBuffer& IB)
216 H_AUTO_D3D(CDriverD3D_activeIndexBuffer)
218 // Must not be locked
219 nlassert (!IB.isLocked());
221 // Must not be empty
222 if (IB.capacity() == 0)
223 return false;
225 const bool touched = (IB.getTouchFlags() & (CIndexBuffer::TouchedReserve|CIndexBuffer::TouchedIndexFormat)) != 0;
226 CIBDrvInfosD3D *info = static_cast<CIBDrvInfosD3D*>(static_cast<IIBDrvInfos*>(IB.DrvInfos));
228 // Volatile buffers must be filled at each pass (exception if emulated)
229 if (_MaxVertexIndex > 0xffff)
231 nlassertex (!info || !info->Volatile || IB.getKeepLocalMemory() || (info->VolatileLockTime == _CurrentRenderPass), ("Volatile buffers must be filled at each pass"));
234 // Build the driver info
235 if (touched)
237 // Delete previous index buffer info
238 if (IB.DrvInfos)
240 delete IB.DrvInfos;
241 nlassert (IB.DrvInfos == NULL);
244 // Rebuild it
245 _IBDrvInfos.push_front (NULL);
246 ItIBDrvInfoPtrList ite = _IBDrvInfos.begin();
247 info = new CIBDrvInfosD3D(this, ite, &IB);
248 *ite = info;
249 // Create the index buffer
250 const uint size = (uint)IB.capacity();
251 uint preferredMemory = 0;
252 if (_DisableHardwareIndexArrayAGP)
254 preferredMemory = CIndexBuffer::RAMResident;
255 info->Volatile = false;
257 else
259 switch (IB.getPreferredMemory ())
261 case CIndexBuffer::RAMPreferred:
262 preferredMemory = CIndexBuffer::RAMResident;
263 info->Volatile = false;
264 break;
265 case CIndexBuffer::AGPPreferred:
266 preferredMemory = CIndexBuffer::AGPResident;
267 info->Volatile = false;
268 break;
269 case CIndexBuffer::StaticPreferred:
270 if (getStaticMemoryToVRAM())
271 preferredMemory = CIndexBuffer::VRAMResident;
272 else
273 preferredMemory = CIndexBuffer::AGPResident;
274 info->Volatile = false;
275 break;
276 case CIndexBuffer::RAMVolatile:
277 preferredMemory = CIndexBuffer::RAMResident;
278 info->Volatile = true;
279 break;
280 case CIndexBuffer::AGPVolatile:
281 preferredMemory = CIndexBuffer::AGPResident;
282 info->Volatile = true;
283 break;
287 // if 32 bit index not supported, the index buffer will be reformated so return a RAM mirror
288 // Real index buffer will be allocated if indices are not modified (e.g a single lock is used to update the content)
289 if (_MaxVertexIndex <= 0xffff && IB.getFormat() == CIndexBuffer::Indices32)
291 info->RamVersion.resize(size);
292 info->IndexBuffer = NULL;
294 else
296 // Volatile index buffer
297 if (info->Volatile)
299 nlassert (info->IndexBuffer == NULL);
300 info->VolatileRAM = preferredMemory == CIndexBuffer::RAMResident;
302 else
304 // Offset will be 0
305 info->Offset = 0;
306 bool success = false;
309 success = _DeviceInterface->CreateIndexBuffer(size*IB.getIndexNumBytes(),
310 RemapIndexBufferUsage[preferredMemory],
311 IB.getFormat() == CIndexBuffer::Indices32 ? D3DFMT_INDEX32 : D3DFMT_INDEX16,
312 RemapIndexBufferPool[preferredMemory],
313 &(info->IndexBuffer), NULL) == D3D_OK;
315 if (success)
316 break;
318 while (preferredMemory--);
319 if (!success)
320 return false;
321 ++indexCount;
323 // Force the vertex buffer update
324 touchRenderVariable (&_IndexBufferCache);
326 // Release the local index buffer
327 IB.DrvInfos = info;
328 IB.setLocation((CIndexBuffer::TLocation)preferredMemory);
331 // Set the current index buffer
332 nlassert (info);
333 _LastIndexBufferInfo = info;
335 // Fill the buffer if in local memory
336 IB.fillBuffer ();
338 _CurrIndexBufferFormat = IB.getFormat();
340 // Set the index buffer
341 if (_MaxVertexIndex > 0xffff || IB.getFormat() == CIndexBuffer::Indices16)
343 setIndexBuffer (info->IndexBuffer, info->Offset);
345 return true;
348 // ***************************************************************************
350 bool CDriverD3D::supportIndexBufferHard() const
352 H_AUTO_D3D(CDriverD3D_supportIndexBufferHard);
353 return !_DisableHardwareIndexArrayAGP;
356 // ***************************************************************************
358 void CDriverD3D::disableHardwareIndexArrayAGP()
360 H_AUTO_D3D(CDriverD3D_disableHardwareIndexArrayAGP)
361 _DisableHardwareIndexArrayAGP = true;
364 // ***************************************************************************
365 // CVolatileIndexBuffer
366 // ***************************************************************************
368 CVolatileIndexBuffer::CVolatileIndexBuffer()
370 H_AUTO_D3D(CVolatileIndexBuffer_CVolatileIndexBuffer);
371 IndexBuffer = NULL;
372 Locked = false;
375 // ***************************************************************************
377 CVolatileIndexBuffer::~CVolatileIndexBuffer()
379 H_AUTO_D3D(CVolatileIndexBuffer_CVolatileIndexBufferDtor);
380 release ();
383 // ***************************************************************************
385 void CVolatileIndexBuffer::release ()
387 H_AUTO_D3D(CVolatileIndexBuffer_release);
388 if (IndexBuffer)
389 IndexBuffer->Release();
390 IndexBuffer = NULL;
393 // ***************************************************************************
394 void CVolatileIndexBuffer::init (CIndexBuffer::TLocation location, uint sizeInBytes, uint maxSize, CDriverD3D *driver, CIndexBuffer::TFormat format)
396 H_AUTO_D3D(CVolatileIndexBuffer_init);
397 release();
398 if (maxSize < sizeInBytes) maxSize = sizeInBytes;
400 // Init the buffer
401 Location = location;
402 Size = sizeInBytes;
403 MaxSize = maxSize;
404 Driver = driver;
406 nlassert(format == CIndexBuffer::Indices16 || format == CIndexBuffer::Indices32);
407 if (format == CIndexBuffer::Indices32)
409 // device must support 32 bits indices
410 nlassert(driver->_MaxVertexIndex > 0xffff);
412 D3DFORMAT d3dFormat = format == CIndexBuffer::Indices16 ? D3DFMT_INDEX16 : D3DFMT_INDEX32;
414 // Allocate the vertex buffer
415 if (Driver->_DeviceInterface->CreateIndexBuffer(sizeInBytes, RemapIndexBufferUsage[location],
416 d3dFormat, RemapIndexBufferPool[location], &IndexBuffer, NULL) != D3D_OK)
418 // Location in RAM must not failed
419 nlassert (location != CIndexBuffer::RAMResident);
421 // Allocate in RAM
422 nlverify (Driver->_DeviceInterface->CreateIndexBuffer(sizeInBytes, RemapIndexBufferUsage[CIndexBuffer::RAMResident],
423 d3dFormat, RemapIndexBufferPool[CIndexBuffer::RAMResident], &IndexBuffer, NULL) != D3D_OK);
424 Location = CIndexBuffer::RAMResident;
426 Format = format;
432 // ***************************************************************************
433 void *CVolatileIndexBuffer::lock (uint size, uint &offset)
435 nlassertex(!Locked, ("Volatile buffer usage should follow an atomic lock/unlock/render sequence"));
436 H_AUTO_D3D(CVolatileIndexBuffer_lock);
437 /* If not enough room to allocate this buffer, resise the buffer to Size+Size/2 but do not reset CurrentIndex
438 * to be sure the buffer will be large enough next pass. */
440 // Enough room for this index ?
441 if (CurrentIndex+size > Size)
443 if (CurrentIndex+size > MaxSize && CurrentIndex != 0)
445 return NULL; // max size exceeded -> can reallocate only if we are at start of block
447 // No, reallocate
448 init (Location, std::max (std::min(Size+Size/2, MaxSize), CurrentIndex+size), MaxSize, Driver, Format);
450 // Lock Profile?
451 TTicks beforeLock = 0;
452 if(Driver->_IBProfiling /*&& Hardware*/)
454 beforeLock= CTime::getPerformanceTime();
456 // Lock the buffer, noblocking lock here if not the first allocation since a reset
457 VOID *pbData;
458 if (CurrentIndex==0)
460 nlverify (IndexBuffer->Lock (0, size, &pbData, 0) == D3D_OK);
462 else
464 nlverify (IndexBuffer->Lock (CurrentIndex, size, &pbData, D3DLOCK_NOOVERWRITE) == D3D_OK);
466 if(Driver->_IBProfiling /*&& Hardware*/)
468 TTicks afterLock;
469 afterLock= CTime::getPerformanceTime();
470 Driver->_VolatileIBLockTime += afterLock - beforeLock;
473 // Old buffer position
474 offset = CurrentIndex / (Format == CIndexBuffer::Indices32 ? sizeof(uint32) : sizeof(uint16));
476 // New buffer position
477 CurrentIndex += size;
478 Locked = true;
479 return pbData;
482 // ***************************************************************************
484 void CVolatileIndexBuffer::unlock ()
486 H_AUTO_D3D(CVolatileIndexBuffer_unlock);
487 nlassertex(Locked, ("Volatile buffer usage should follow an atomic lock/unlock/render sequence"));
488 nlverify (IndexBuffer->Unlock () == D3D_OK);
489 Locked = false;
492 // ***************************************************************************
493 void CVolatileIndexBuffer::reset ()
495 H_AUTO_D3D(CVolatileIndexBuffer_reset);
496 CurrentIndex = 0;
499 // ***************************************************************************
500 bool CDriverD3D::buildQuadIndexBuffer()
502 // this code will becomes useless when 16 bits buffer are really supported
503 nlassert(!_QuadIB);
504 uint numQuads = std::min(MAX_NUM_QUADS, (uint) (_MaxPrimitiveCount * 2)); // 2 primitives for each quads
505 HRESULT r = _DeviceInterface->CreateIndexBuffer(sizeof(uint16) * 6 * numQuads, D3DUSAGE_DYNAMIC, D3DFMT_INDEX16, D3DPOOL_SYSTEMMEM, &_QuadIB, NULL);
506 if (r != D3D_OK) return false;
507 void *datas;
508 r = _QuadIB->Lock(0, sizeof(uint16) * 6 * numQuads, &datas, 0);
509 if (r != D3D_OK) return false;
510 fillQuadIndexes((uint16 *) datas, 0, 6 * numQuads);
511 _QuadIB->Unlock();
512 return true;
516 // ***************************************************************************
518 } // NL3D