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) 2014 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/>.
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"
36 using namespace NLMISC
;
41 // ***************************************************************************
43 CIBDrvInfosD3D::CIBDrvInfosD3D(CDriverD3D
*drv
, ItIBDrvInfoPtrList it
, CIndexBuffer
*ib
) : IIBDrvInfos(drv
, it
, ib
)
45 H_AUTO_D3D(CIBDrvInfosD3D_CIBDrvInfosD3D
)
48 VolatileIndexBuffer
= NULL
;
52 // ***************************************************************************
56 CIBDrvInfosD3D::~CIBDrvInfosD3D()
58 H_AUTO_D3D(CIBDrvInfosD3D_CIBDrvInfosD3DDtor
);
59 // Restore non resident memory
62 IndexBufferPtr
->setLocation(CIndexBuffer::NotResident
);
66 // release index buffer
67 if (IndexBuffer
&& !Volatile
)
71 if (Driver
->_IndexBufferCache
.IndexBuffer
== IndexBuffer
)
73 Driver
->_IndexBufferCache
.IndexBuffer
= NULL
;
74 Driver
->touchRenderVariable(&Driver
->_IndexBufferCache
);
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];
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]);
115 void *ptr
= (*buffer
)->lock ((last
-first
)*getIndexNumBytes(), Offset
);
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]);
126 bufferOther
= VolatileRAM
? (&driver
->_VolatileIndexBuffer32RAM
[(driver
->_CurrentRenderPass
+ 1) &1]):(&driver
->_VolatileIndexBuffer32AGP
[(driver
->_CurrentRenderPass
+ 1 ) &1]);
128 std::swap(*buffer
, *bufferOther
);
130 ptr
= (*buffer
)->lock ((last
-first
)*getIndexNumBytes(), Offset
);
133 nlassert(!VolatileIndexBuffer
);
134 VolatileIndexBuffer
= *buffer
;
135 IndexBuffer
= (*buffer
)->IndexBuffer
;
136 ptr
= (uint8
*) ptr
- first
* getIndexNumBytes();
139 VolatileLockTime
= driver
->_CurrentRenderPass
;
141 // Touch the index buffer
142 driver
->touchRenderVariable (&driver
->_IndexBufferCache
);
148 nlassert (IndexBuffer
);
150 TTicks beforeLock
= 0;
151 if(driver
->_IBProfiling
/*&& Hardware*/)
153 beforeLock
= CTime::getPerformanceTime();
156 HRESULT result
= IndexBuffer
->Lock ( first
*getIndexNumBytes(), (last
-first
)*getIndexNumBytes(), &pbData
, readOnly
?D3DLOCK_READONLY
:0);
157 nlassert(result
== D3D_OK
);
159 if(driver
->_IBProfiling
/*&& Hardware*/)
162 afterLock
= CTime::getPerformanceTime();
163 driver
->appendIBLockProfile(afterLock
-beforeLock
, IndexBufferPtr
);
165 if (result
== D3D_OK
) return pbData
;
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
)
181 nlassert(VolatileIndexBuffer
);
182 VolatileIndexBuffer
->unlock ();
183 VolatileIndexBuffer
= NULL
;
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
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());
222 if (IB
.capacity() == 0)
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
237 // Delete previous index buffer info
241 nlassert (IB
.DrvInfos
== NULL
);
245 _IBDrvInfos
.push_front (NULL
);
246 ItIBDrvInfoPtrList ite
= _IBDrvInfos
.begin();
247 info
= new CIBDrvInfosD3D(this, ite
, &IB
);
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;
259 switch (IB
.getPreferredMemory ())
261 case CIndexBuffer::RAMPreferred
:
262 preferredMemory
= CIndexBuffer::RAMResident
;
263 info
->Volatile
= false;
265 case CIndexBuffer::AGPPreferred
:
266 preferredMemory
= CIndexBuffer::AGPResident
;
267 info
->Volatile
= false;
269 case CIndexBuffer::StaticPreferred
:
270 if (getStaticMemoryToVRAM())
271 preferredMemory
= CIndexBuffer::VRAMResident
;
273 preferredMemory
= CIndexBuffer::AGPResident
;
274 info
->Volatile
= false;
276 case CIndexBuffer::RAMVolatile
:
277 preferredMemory
= CIndexBuffer::RAMResident
;
278 info
->Volatile
= true;
280 case CIndexBuffer::AGPVolatile
:
281 preferredMemory
= CIndexBuffer::AGPResident
;
282 info
->Volatile
= true;
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
;
296 // Volatile index buffer
299 nlassert (info
->IndexBuffer
== NULL
);
300 info
->VolatileRAM
= preferredMemory
== CIndexBuffer::RAMResident
;
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
;
318 while (preferredMemory
--);
323 // Force the vertex buffer update
324 touchRenderVariable (&_IndexBufferCache
);
326 // Release the local index buffer
328 IB
.setLocation((CIndexBuffer::TLocation
)preferredMemory
);
331 // Set the current index buffer
333 _LastIndexBufferInfo
= info
;
335 // Fill the buffer if in local memory
338 _CurrIndexBufferFormat
= IB
.getFormat();
340 // Set the index buffer
341 if (_MaxVertexIndex
> 0xffff || IB
.getFormat() == CIndexBuffer::Indices16
)
343 setIndexBuffer (info
->IndexBuffer
, info
->Offset
);
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
);
375 // ***************************************************************************
377 CVolatileIndexBuffer::~CVolatileIndexBuffer()
379 H_AUTO_D3D(CVolatileIndexBuffer_CVolatileIndexBufferDtor
);
383 // ***************************************************************************
385 void CVolatileIndexBuffer::release ()
387 H_AUTO_D3D(CVolatileIndexBuffer_release
);
389 IndexBuffer
->Release();
393 // ***************************************************************************
394 void CVolatileIndexBuffer::init (CIndexBuffer::TLocation location
, uint sizeInBytes
, uint maxSize
, CDriverD3D
*driver
, CIndexBuffer::TFormat format
)
396 H_AUTO_D3D(CVolatileIndexBuffer_init
);
398 if (maxSize
< sizeInBytes
) maxSize
= sizeInBytes
;
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
);
422 nlverify (Driver
->_DeviceInterface
->CreateIndexBuffer(sizeInBytes
, RemapIndexBufferUsage
[CIndexBuffer::RAMResident
],
423 d3dFormat
, RemapIndexBufferPool
[CIndexBuffer::RAMResident
], &IndexBuffer
, NULL
) != D3D_OK
);
424 Location
= CIndexBuffer::RAMResident
;
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
448 init (Location
, std::max (std::min(Size
+Size
/2, MaxSize
), CurrentIndex
+size
), MaxSize
, Driver
, Format
);
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
460 nlverify (IndexBuffer
->Lock (0, size
, &pbData
, 0) == D3D_OK
);
464 nlverify (IndexBuffer
->Lock (CurrentIndex
, size
, &pbData
, D3DLOCK_NOOVERWRITE
) == D3D_OK
);
466 if(Driver
->_IBProfiling
/*&& Hardware*/)
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
;
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
);
492 // ***************************************************************************
493 void CVolatileIndexBuffer::reset ()
495 H_AUTO_D3D(CVolatileIndexBuffer_reset
);
499 // ***************************************************************************
500 bool CDriverD3D::buildQuadIndexBuffer()
502 // this code will becomes useless when 16 bits buffer are really supported
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;
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
);
516 // ***************************************************************************