827 lines
25 KiB
C++
827 lines
25 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef NL_INDEX_BUFFER_H
|
|
#define NL_INDEX_BUFFER_H
|
|
|
|
#include "nel/misc/types_nl.h"
|
|
#include "nel/misc/smart_ptr.h"
|
|
#include <vector>
|
|
|
|
namespace NLMISC
|
|
{
|
|
class IStream;
|
|
}
|
|
|
|
namespace NL3D {
|
|
|
|
using NLMISC::CRefCount;
|
|
using NLMISC::CRefPtr;
|
|
|
|
// List typedef.
|
|
class IIBDrvInfos;
|
|
class CIndexBuffer;
|
|
class IDriver;
|
|
class CIndexBufferReadWrite;
|
|
class CIndexBufferRead;
|
|
typedef std::list<IIBDrvInfos*> TIBDrvInfoPtrList;
|
|
typedef TIBDrvInfoPtrList::iterator ItIBDrvInfoPtrList;
|
|
|
|
|
|
#define NL_NAMED_INDEX_BUFFER
|
|
#ifdef NL_NAMED_INDEX_BUFFER
|
|
#define NL_SET_IB_NAME(ib, n) if ((ib).getName().empty()) (ib).setName(n);
|
|
#else
|
|
#define NL_SET_IB_NAME(ib, n)
|
|
#endif
|
|
|
|
|
|
|
|
#define NL_FORCE_INDEX_BUFFER_16
|
|
|
|
#ifdef NL_FORCE_INDEX_BUFFER_16
|
|
typedef uint16 TIndexType; // default index type
|
|
#define NL_DEFAULT_INDEX_BUFFER_FORMAT CIndexBuffer::Indices16
|
|
|
|
#define NL_MESH_INDEX16
|
|
#define NL_MESH_MRM_INDEX16
|
|
#define NL_SKINNED_MESH_MRM_INDEX16
|
|
//#define NL_LANDSCAPE_INDEX16
|
|
#define NL_LOD_CHARACTER_INDEX16
|
|
#define NL_COARSE_MESH_INDEX16
|
|
#else
|
|
typedef uint32 TIndexType; // default index type
|
|
#define NL_DEFAULT_INDEX_BUFFER_FORMAT CIndexBuffer::Indices32
|
|
#endif
|
|
|
|
|
|
|
|
// 16 bit indices for meshs ?
|
|
#ifdef NL_MESH_INDEX16
|
|
typedef uint16 TMeshIndexType;
|
|
#define NL_MESH_INDEX_FORMAT CIndexBuffer::Indices16
|
|
#else
|
|
typedef uint32 TMeshIndexType;
|
|
#define NL_MESH_INDEX_FORMAT CIndexBuffer::Indices32
|
|
#endif
|
|
|
|
// 16 bit indices for mesh_mrm ?
|
|
#ifdef NL_MESH_MRM_INDEX16
|
|
typedef uint16 TMeshMRMIndexType;
|
|
#define NL_MESH_MRM_INDEX_FORMAT CIndexBuffer::Indices16
|
|
#else
|
|
typedef uint32 TMeshMRMIndexType;
|
|
#define NL_MESH_MRM_INDEX_FORMAT CIndexBuffer::Indices32
|
|
#endif
|
|
|
|
// 16 bit indices for skinned mesh_mrm ?
|
|
#ifdef NL_SKINNED_MESH_MRM_INDEX16
|
|
typedef uint16 TSkinnedMeshMRMIndexType;
|
|
#define NL_SKINNED_MESH_MRM_INDEX_FORMAT CIndexBuffer::Indices16
|
|
#else
|
|
typedef uint32 TSkinnedMeshMRMIndexType;
|
|
#define NL_SKINNED_MESH_MRM_INDEX_FORMAT CIndexBuffer::Indices32
|
|
#endif
|
|
|
|
|
|
// 16 bit indices for landscape ?
|
|
#ifdef NL_LANDSCAPE_INDEX16
|
|
typedef uint16 TLandscapeIndexType;
|
|
#define NL_LANDSCAPE_INDEX_FORMAT CIndexBuffer::Indices16
|
|
#else
|
|
typedef uint32 TLandscapeIndexType;
|
|
#define NL_LANDSCAPE_INDEX_FORMAT CIndexBuffer::Indices32
|
|
#endif
|
|
|
|
// 16 bit for lod characters ?
|
|
#ifdef NL_LOD_CHARACTER_INDEX16
|
|
typedef uint16 TLodCharacterIndexType;
|
|
#define NL_LOD_CHARACTER_INDEX_FORMAT CIndexBuffer::Indices16
|
|
#else
|
|
typedef uint32 TLodCharacterIndexType;
|
|
#define NL_LOD_CHARACTER_INDEX_FORMAT CIndexBuffer::Indices32
|
|
#endif
|
|
|
|
// 16 bit for coarse meshs ?
|
|
#ifdef NL_COARSE_MESH_INDEX16
|
|
typedef uint16 TCoarseMeshIndexType;
|
|
#define NL_COARSE_MESH_INDEX_FORMAT CIndexBuffer::Indices16
|
|
#else
|
|
typedef uint32 TCoarseMeshIndexType;
|
|
#define NL_COARSE_MESH_INDEX_FORMAT CIndexBuffer::Indices32
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
* An index buffer to work with the driver
|
|
*
|
|
* Before the index buffer is resident (IDriver::activeIndexBuffer), it is in system memory.
|
|
* Once the index buffer is resident, the driver creates its proprietary index buffer and release the internal index buffer.
|
|
* At this moment the index buffer can be in VRAM, AGP or system memory.
|
|
*
|
|
* The index buffers resident in AGP and VRAM are writeonly, i-e, you can't read them after a lock(). If you change the capacity
|
|
* of the format of a writeonly buffer, the content is lost.
|
|
*
|
|
* Volatile buffers must be completely filled at each pass. They are lost after each swapBuffers(). They are writeonly.
|
|
* Volatile buffers must be resized before the lock call. Only one lock per render must be done with volatile buffers.
|
|
*
|
|
*/
|
|
/* *** IMPORTANT ********************
|
|
* *** IF YOU MODIFY THE STRUCTURE OF THIS CLASS, PLEASE INCREMENT IDriver::InterfaceVersion TO INVALIDATE OLD DRIVER DLL
|
|
* **********************************
|
|
*/
|
|
|
|
// All these flags are similar to DX8
|
|
class CIndexBuffer : public CRefCount
|
|
{
|
|
public:
|
|
friend class CIndexBufferReadWrite;
|
|
friend class CIndexBufferRead;
|
|
|
|
/**
|
|
* Type of preferred memory
|
|
*/
|
|
enum TPreferredMemory
|
|
{
|
|
RAMPreferred = 0, // A block of driver RAM memory is allocated for this buffer. The buffer is read/write.
|
|
AGPPreferred, // A block of driver AGP memory is allocated for this buffer. The buffer is writeonly.
|
|
StaticPreferred, // The buffer will not be modified. A block of driver AGP or VRAM memory is allocated for this buffer. The buffer is writeonly.
|
|
RAMVolatile, // A block of temporary driver RAM memory will be returned by lock(). The buffer must be entirely filled after each swapBuffers(). The buffer is writeonly.
|
|
AGPVolatile, // A block of temporary driver AGP memory will be returned by lock(). The buffer must be entirely filled after each swapBuffers(). The buffer is writeonly.
|
|
PreferredCount,
|
|
};
|
|
|
|
/**
|
|
* Type of index buffer location
|
|
*/
|
|
enum TLocation
|
|
{
|
|
RAMResident = 0,
|
|
AGPResident,
|
|
VRAMResident,
|
|
NotResident,
|
|
LocationCount,
|
|
};
|
|
|
|
enum TFormat
|
|
{
|
|
Indices16 = 0,
|
|
Indices32,
|
|
IndicesUnknownFormat // reserved, for use by driver
|
|
};
|
|
|
|
/**
|
|
* Internal flags
|
|
*/
|
|
enum
|
|
{
|
|
/// Vertex format touched
|
|
TouchedIndexFormat = 1,
|
|
|
|
/// Num vertices touched
|
|
TouchedNumIndexes = 2,
|
|
|
|
/// Reserve touched
|
|
TouchedReserve = 4,
|
|
|
|
/// All touhched
|
|
TouchedAll = 0xFFFF
|
|
};
|
|
|
|
public:
|
|
|
|
// \name Private. For Driver only.
|
|
// @{
|
|
CRefPtr<IIBDrvInfos> DrvInfos;
|
|
uint getTouchFlags() const { return _InternalFlags&TouchedAll; }
|
|
|
|
/** Used by the driver implementation. The driver must first allocate its internal buffer and fill DrvInfos. Then it has to call setLocation(true).
|
|
*
|
|
* If newLocation!=NotResident, setLocation() will copy the non resident buffer in the choosed resident memory and
|
|
* untouch the buffer. If the buffer preferres RAM or AGP memory, it will release the non resident memory.
|
|
*
|
|
* If newLocation==NotResident, setLocation() will realloc the non resident buffer, copy the vertex data if the buffer was resident in RAM. Then
|
|
* it will touch the buffer.*/
|
|
void setLocation (TLocation newLocation); // The driver implementation must set the location after activeIndexBuffer.
|
|
|
|
/** Called by the driver implementation during the buffer activation */
|
|
void fillBuffer ();
|
|
// @}
|
|
|
|
public:
|
|
/* ***********************************************
|
|
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
|
|
* It can be loaded/called through CAsyncFileManager for instance
|
|
* ***********************************************/
|
|
|
|
/**
|
|
* Default constructor. Make an empty index buffer. No value, no index. Index color format is set to TRGBA.
|
|
*/
|
|
CIndexBuffer(void);
|
|
|
|
/** Make an empty index buffer. No value, no index. Index color format is set to TRGBA.
|
|
* Set its name to the given value
|
|
*/
|
|
CIndexBuffer(const char *name);
|
|
|
|
/**
|
|
* Copy constructor.
|
|
* Do not copy DrvInfos, copy all infos and set IDRV_VF_TOUCHED_ALL.
|
|
*/
|
|
CIndexBuffer(const CIndexBuffer &vb);
|
|
|
|
/**
|
|
* Destructor.
|
|
*/
|
|
~CIndexBuffer(void);
|
|
|
|
/**
|
|
* Copy operator.
|
|
* Do not copy DrvInfos, copy all infos and set IDRV_VF_TOUCHED_ALL.
|
|
* All the destination index buffer is invalidated. Data are lost.
|
|
* The source and destination buffers must be unlocked.
|
|
*/
|
|
CIndexBuffer &operator=(const CIndexBuffer &vb);
|
|
|
|
/**
|
|
* Set the buffer preferred memory. Default preferred memory is RAM.
|
|
*
|
|
* Indices can be either 16 or 32 bits
|
|
* Default format is 32 bits
|
|
*
|
|
* Preferre RAM if the buffer is changed several times in the same render pass.
|
|
* Preferre AGP if the buffer is changed only one time in the same render pass.
|
|
* Preferre Static if the buffer is changed only one time for initialisation.
|
|
*
|
|
* If static memory is chosen, the driver will choose VRAM or AGP depending of the user configuration.
|
|
*
|
|
* If static or RAM memory is preferred, the buffer won't be lost after a driver reset.
|
|
*
|
|
* If the buffer preferres AGP memory, the buffer is lost after a driver reset. When the buffer is lost, it returns
|
|
* in a non resident state. The state must be tested at each pass with isResident(). If the buffer is in a
|
|
* non resident state, the user must refill it.
|
|
*
|
|
* If VRAM memory allocation failed, the driver will try with AGP and then with RAM.
|
|
* If AGP memory allocation failed, the driver will try with RAM.
|
|
* RAM allocation should never failed.
|
|
*
|
|
* Performance note:
|
|
* - for RAM CVertexBuffer, you can read / write as you like.
|
|
* - for AGP CVertexBuffer, you should write sequentially to take full advantage of the write combiners. You can't read.
|
|
* - for VRAM CVertexBuffer, you should write only one time, to init. You can't read.
|
|
*
|
|
* Volatile buffers must be completely filled at each pass. They are lost after each swapBuffers(). They are writeonly.
|
|
* Volatile buffers must be resized before the lock call. Only one lock per render must be done with volatile buffers if
|
|
* keepLocalMemory is false.
|
|
*
|
|
* If keepLocalMemory is true, lock() will return a local memory pointer. The local memory will copied in resident memory
|
|
* during the activation of the buffer. The not all the buffer capacity is copied but only the used size.
|
|
*
|
|
* If the buffer preferres AGP memory, the data are lost.
|
|
* The buffer is no more resident.
|
|
* The buffer is invalidated.
|
|
* The buffer must be unlocked before the call.
|
|
*/
|
|
void setPreferredMemory (TPreferredMemory preferredMemory, bool keepLocalMemory);
|
|
|
|
/**
|
|
* Get the index buffer preferred memory.
|
|
*/
|
|
TPreferredMemory getPreferredMemory () const { return _PreferredMemory; }
|
|
|
|
/**
|
|
* Get the keep local memory flag.
|
|
*/
|
|
bool getKeepLocalMemory () const { return _KeepLocalMemory; }
|
|
|
|
/**
|
|
* Get the index buffer current location.
|
|
*/
|
|
TLocation getLocation () const { return _Location; }
|
|
|
|
/**
|
|
* Returns if the index buffer is driver resident or not.
|
|
* The index buffer is resident after a call to IDriver::activeIndexBuffer().
|
|
*/
|
|
bool isResident () const { return (_Location != NotResident) && DrvInfos; }
|
|
|
|
/**
|
|
* Set the number of active indexes. It enlarge capacity, if needed.
|
|
* If the new size is bigger than capacity, reserve() will be called. see reserve().
|
|
* If the new size is smaller than capacity, the data are keeped, the index buffer stay resident if it is resident.
|
|
*/
|
|
void setNumIndexes(uint32 n);
|
|
|
|
// Set format for indices. This discards previous indices
|
|
void setFormat(TFormat format);
|
|
|
|
// Get format
|
|
TFormat getFormat() const { return _Format; }
|
|
|
|
// get number of bytes per index
|
|
uint getIndexNumBytes() const { return _Format == Indices16 ? 2 : 4; }
|
|
|
|
/**
|
|
* Get the number of active indexes.
|
|
*/
|
|
uint32 getNumIndexes(void) const { return(_NbIndexes); }
|
|
|
|
/**
|
|
* Reset all the indexes from memory (contReset()), so that capacity() == getNumIndexes() == 0.
|
|
*
|
|
* If the index buffer is resident in AGP or VRAM, the data are lost.
|
|
* The index buffer is no more resident.
|
|
* The index buffer is invalidated.
|
|
* The index buffer must be unlocked before the call.
|
|
*/
|
|
void deleteAllIndexes();
|
|
|
|
/**
|
|
* Reserve space for nIndexes indexes. You are allowed to write your indexes on this space.
|
|
*
|
|
* If the index buffer is resident in AGP or VRAM, the data are lost.
|
|
* The index buffer is no more resident.
|
|
* The index buffer is invalidated.
|
|
* The index buffer must be unlocked before the call.
|
|
*/
|
|
void reserve(uint32 nIndexes);
|
|
|
|
/**
|
|
* Return the number of indexes reserved.
|
|
*/
|
|
uint32 capacity() { return _Capacity; }
|
|
|
|
/**
|
|
* If the index buffer is resident in AGP or VRAM, the data are lost.
|
|
* The index buffer is no more resident.
|
|
* The index buffer is invalidated.
|
|
* The index buffer must be unlocked before the call.
|
|
*/
|
|
void serial(NLMISC::IStream &f);
|
|
|
|
/**
|
|
* Access indexes. Multi lock is possible only if no regions are used. Each lock need an accessor to be unlocked.
|
|
*
|
|
* Lock the index buffer and return and fill an accessor object. Once the object is destroyed, the buffer in unlocked.
|
|
*
|
|
* \param accessor is the accessor object to fill
|
|
* \param first is the first index to be accessed. Put 0 to select all the indexes. What ever is this index,
|
|
* the indexices in the index buffer remain the same.
|
|
* \param last is the last index to be accessed + 1. Put 0 to select all the indexes.
|
|
*/
|
|
inline void lock (CIndexBufferReadWrite &accessor, uint first=0, uint last=0);
|
|
|
|
/**
|
|
* Read only indexes access. Multi lock is possible only if no regions are used. Each lock need an accessor to be unlocked.
|
|
*
|
|
* Lock the index buffer and return and fill an accessor object. Once the object is destroyed, the buffer in unlocked.
|
|
*
|
|
* Readonly lock will fail if the buffer is resident in AGP or VRAM.
|
|
*
|
|
* \param accessor is the accessor object to fill
|
|
* \param first is the first index to be accessed. Put 0 to select all the indexes. What ever is this index,
|
|
* the indexices in the index buffer remain the same.
|
|
* \param last is the last index to be accessed + 1. Put 0 to select all the indexes.
|
|
*/
|
|
inline void lock (CIndexBufferRead &accessor, uint first=0, uint last=0) const;
|
|
|
|
// Return true if the vetx buffer is locked
|
|
bool isLocked () const {return _LockCounter!=0;}
|
|
|
|
// debug name
|
|
void setName (const std::string &name) { _Name = name; };
|
|
const std::string &getName () const { return _Name; };
|
|
|
|
private:
|
|
|
|
// Check locked buffers
|
|
bool checkLockedBuffer () const { return _LockedBuffer || (!isResident() && _NonResidentIndexes.empty()); }
|
|
|
|
/**
|
|
* Unlock the index buffer. Called by CIndexBufferReadWrite.
|
|
* \param first the first index that as been modified.
|
|
* \param last the last index that as been modified + 1.
|
|
*/
|
|
inline void unlock (uint first, uint last);
|
|
|
|
/**
|
|
* Unlock the index buffer. Called by CIndexBufferRead.
|
|
* \param first the first index that as been modified.
|
|
* \param last the last index that as been modified + 1.
|
|
*/
|
|
inline void unlock () const;
|
|
|
|
// Reset the touch flags
|
|
void resetTouchFlags() {_InternalFlags &= (uint16)(~TouchedAll);}
|
|
|
|
// Force non resident memory
|
|
void restaureNonResidentMemory();
|
|
|
|
// Convert current index to a serializable vector
|
|
void buildSerialVector(std::vector<uint32> &dest) const;
|
|
|
|
// Convert current index from a serialized vector
|
|
void restoreFromSerialVector(const std::vector<uint32> &src);
|
|
|
|
|
|
private:
|
|
// Internal flags
|
|
uint16 _InternalFlags; // Offset 18 : aligned
|
|
|
|
// Format of indices
|
|
TFormat _Format;
|
|
|
|
// Index count in the buffer
|
|
uint32 _NbIndexes; // Offset 20 : aligned
|
|
|
|
// Capacity of the buffer
|
|
uint32 _Capacity;
|
|
|
|
// Index array
|
|
std::vector<uint8> _NonResidentIndexes;
|
|
|
|
// The locked index buffer
|
|
mutable void *_LockedBuffer;
|
|
|
|
// The index buffer is locked n times
|
|
mutable uint _LockCounter;
|
|
|
|
// Location of the buffer
|
|
TLocation _Location;
|
|
|
|
// Prefered memory
|
|
TPreferredMemory _PreferredMemory;
|
|
|
|
// Resident buffer size
|
|
uint32 _ResidentSize;
|
|
|
|
// Keep in local memory
|
|
bool _KeepLocalMemory;
|
|
|
|
// debug name
|
|
std::string _Name;
|
|
};
|
|
|
|
/**
|
|
* Index buffer driver information.
|
|
*/
|
|
// *** IMPORTANT ********************
|
|
// *** IF YOU MODIFY THE STRUCTURE OF THIS CLASS, PLEASE INCREMENT IDriver::InterfaceVersion TO INVALIDATE OLD DRIVER DLL
|
|
// **********************************
|
|
class IIBDrvInfos : public CRefCount
|
|
{
|
|
protected:
|
|
IDriver *_Driver;
|
|
private:
|
|
ItIBDrvInfoPtrList _DriverIterator;
|
|
|
|
public:
|
|
CRefPtr<CIndexBuffer> IndexBufferPtr;
|
|
|
|
IIBDrvInfos(IDriver *drv, ItIBDrvInfoPtrList it, CIndexBuffer *ib) {_Driver= drv; _DriverIterator= it; IndexBufferPtr = ib;}
|
|
|
|
/** Lock method.
|
|
* \param first is the first index to be accessed. Put 0 to select all the indexes. What ever is this index,
|
|
* the indexices in the index buffer remain the same.
|
|
* \param last is the last index to be accessed + 1. Put 0 to select all the indexes.
|
|
*/
|
|
virtual void *lock (uint first, uint last, bool readOnly) =0;
|
|
|
|
/** Unlock method.
|
|
* \param first is the index of the first indexes to update. 0 to update all the indexes.
|
|
* \param last is the index of the last indexes to update + 1. 0 to update all the indexes.
|
|
*/
|
|
virtual void unlock (uint first, uint last) =0;
|
|
|
|
/* The virtual dtor is important.
|
|
* The driver implementation must call setLocation (NotResident) if IndexBufferPtr!=NULL.*/
|
|
virtual ~IIBDrvInfos();
|
|
|
|
|
|
CIndexBuffer::TFormat getFormat() const { nlassert(IndexBufferPtr); return IndexBufferPtr->getFormat(); }
|
|
uint getIndexNumBytes() const { nlassert(IndexBufferPtr); return IndexBufferPtr->getIndexNumBytes(); }
|
|
};
|
|
|
|
/**
|
|
* The index buffer accessor read / write
|
|
*/
|
|
class CIndexBufferReadWrite
|
|
{
|
|
public:
|
|
friend class CIndexBuffer;
|
|
|
|
CIndexBufferReadWrite()
|
|
{
|
|
_Parent = NULL;
|
|
}
|
|
~CIndexBufferReadWrite()
|
|
{
|
|
unlock();
|
|
}
|
|
|
|
/**
|
|
* Unlock the index buffer.
|
|
* After this call, the accessor should not be used before a new lock.
|
|
*/
|
|
void unlock()
|
|
{
|
|
if (_Parent)
|
|
{
|
|
_Parent->unlock(_First, _Last);
|
|
_Parent = NULL;
|
|
}
|
|
}
|
|
|
|
// Set a line index at index idx.
|
|
inline void setLine(uint idx, uint32 i0, uint32 i1)
|
|
{
|
|
nlassert (_Parent->checkLockedBuffer());
|
|
#ifdef NL_DEBUG
|
|
nlassert(idx < _Parent->getNumIndexes());
|
|
nlassert(idx + 1 < _Parent->getNumIndexes());
|
|
#endif
|
|
if (_Parent->getFormat() == CIndexBuffer::Indices16)
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(i0 <= 65535);
|
|
nlassert(i1 <= 65535);
|
|
#endif
|
|
((uint16 *) _Parent->_LockedBuffer)[idx] = (uint16) i0;
|
|
((uint16 *) _Parent->_LockedBuffer)[idx+1] = (uint16) i1;
|
|
}
|
|
else if (_Parent->getFormat() == CIndexBuffer::Indices32)
|
|
{
|
|
((uint32 *) _Parent->_LockedBuffer)[idx] = i0;
|
|
((uint32 *) _Parent->_LockedBuffer)[idx+1] = i1;
|
|
}
|
|
else
|
|
{
|
|
nlassert(0);
|
|
}
|
|
}
|
|
|
|
// Set a line index at index idx.
|
|
inline void setTri(uint idx, uint32 i0, uint32 i1, uint32 i2)
|
|
{
|
|
nlassert (_Parent->checkLockedBuffer());
|
|
#ifdef NL_DEBUG
|
|
nlassert(idx < _Parent->getNumIndexes());
|
|
nlassert(idx + 1 < _Parent->getNumIndexes());
|
|
nlassert(idx + 2 < _Parent->getNumIndexes());
|
|
#endif
|
|
if (_Parent->getFormat() == CIndexBuffer::Indices16)
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(i0 <= 65535);
|
|
nlassert(i1 <= 65535);
|
|
nlassert(i2 <= 65535);
|
|
#endif
|
|
((uint16 *) _Parent->_LockedBuffer)[idx] = (uint16) i0;
|
|
((uint16 *) _Parent->_LockedBuffer)[idx+1] = (uint16) i1;
|
|
((uint16 *) _Parent->_LockedBuffer)[idx+2] = (uint16) i2;
|
|
}
|
|
else if (_Parent->getFormat() == CIndexBuffer::Indices32)
|
|
{
|
|
((uint32 *) _Parent->_LockedBuffer)[idx] = i0;
|
|
((uint32 *) _Parent->_LockedBuffer)[idx+1] = i1;
|
|
((uint32 *) _Parent->_LockedBuffer)[idx+2] = i2;
|
|
}
|
|
else
|
|
{
|
|
nlassert(0);
|
|
}
|
|
}
|
|
|
|
// Get a pointer on an index.
|
|
void *getPtr (uint idx=0)
|
|
{
|
|
nlassert (_Parent->checkLockedBuffer());
|
|
if (_Parent->getFormat() == CIndexBuffer::Indices16)
|
|
{
|
|
return &(((uint16 *) _Parent->_LockedBuffer)[idx]);
|
|
}
|
|
else if (_Parent->getFormat() == CIndexBuffer::Indices32)
|
|
{
|
|
return &(((uint32 *) _Parent->_LockedBuffer)[idx]);
|
|
}
|
|
else
|
|
{
|
|
nlassert(0);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/** Touch the updated indexes. If the method is not call, the accessor update all the indexes.
|
|
* \param first is the index of the first indexes to update.
|
|
* \param last is the index of the last indexes to update + 1.
|
|
*/
|
|
void touchIndexes (uint first, uint last);
|
|
|
|
const CIndexBuffer *getParent() const { return _Parent; }
|
|
|
|
CIndexBuffer::TFormat getFormat() const { nlassert(_Parent); return _Parent->getFormat(); }
|
|
uint getIndexNumBytes() const { nlassert(_Parent); return _Parent->getIndexNumBytes(); }
|
|
|
|
private:
|
|
|
|
// No copy operators available
|
|
void operator=(const CIndexBufferReadWrite& /* other */) {}
|
|
CIndexBufferReadWrite(const CIndexBufferReadWrite& /* other */) {}
|
|
|
|
CIndexBuffer *_Parent;
|
|
uint _First, _Last;
|
|
};
|
|
|
|
/**
|
|
* The index buffer read accessor
|
|
*/
|
|
class CIndexBufferRead
|
|
{
|
|
public:
|
|
friend class CIndexBuffer;
|
|
|
|
CIndexBufferRead()
|
|
{
|
|
_Parent = NULL;
|
|
}
|
|
~CIndexBufferRead()
|
|
{
|
|
unlock();
|
|
}
|
|
|
|
/**
|
|
* Unlock the index buffer.
|
|
* After this call, the accessor should not be used before a new lock.
|
|
*/
|
|
void unlock()
|
|
{
|
|
if (_Parent)
|
|
{
|
|
_Parent->unlock();
|
|
_Parent = NULL;
|
|
}
|
|
}
|
|
|
|
// Get a pointer on an index.
|
|
const void *getPtr (uint idx=0) const
|
|
{
|
|
nlassert (_Parent->checkLockedBuffer());
|
|
if (_Parent->getFormat() == CIndexBuffer::Indices16)
|
|
{
|
|
return &(((const uint16 *) _Parent->_LockedBuffer)[idx]);
|
|
}
|
|
else if (_Parent->getFormat() == CIndexBuffer::Indices32)
|
|
{
|
|
return &(((const uint32 *) _Parent->_LockedBuffer)[idx]);
|
|
}
|
|
else
|
|
{
|
|
nlassert(0);
|
|
}
|
|
return NULL;
|
|
}
|
|
const CIndexBuffer *getParent() const { return _Parent; }
|
|
uint getIndexNumBytes() const { nlassert(_Parent); return _Parent->getIndexNumBytes(); }
|
|
CIndexBuffer::TFormat getFormat() const { nlassert(_Parent); return _Parent->getFormat(); }
|
|
private:
|
|
|
|
// No copy operators available
|
|
void operator=(const CIndexBufferRead& /* other */) {}
|
|
CIndexBufferRead(const CIndexBufferRead& /* other */) {}
|
|
|
|
const CIndexBuffer *_Parent;
|
|
};
|
|
|
|
// ***************************************************************************
|
|
|
|
inline void CIndexBuffer::lock (CIndexBufferReadWrite &accessor, uint first, uint last)
|
|
{
|
|
accessor.unlock();
|
|
accessor._Parent = this;
|
|
accessor._First = 0;
|
|
accessor._Last = 0;
|
|
|
|
// Already locked ?
|
|
if (_LockCounter == 0)
|
|
{
|
|
nlassert (_LockedBuffer == NULL);
|
|
|
|
// No
|
|
if (isResident() && !_KeepLocalMemory)
|
|
{
|
|
if (last == 0)
|
|
last = _NbIndexes;
|
|
_LockedBuffer = DrvInfos->lock (first, last, false);
|
|
}
|
|
else
|
|
{
|
|
if (_NonResidentIndexes.empty())
|
|
_LockedBuffer = NULL;
|
|
else
|
|
_LockedBuffer = &(_NonResidentIndexes[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlassert ((first==0)&&(last==0));
|
|
}
|
|
|
|
_LockCounter++;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
inline void CIndexBuffer::lock (CIndexBufferRead &accessor, uint first, uint last) const
|
|
{
|
|
accessor.unlock();
|
|
accessor._Parent = this;
|
|
|
|
// Already locked ?
|
|
if (_LockCounter == 0)
|
|
{
|
|
nlassert (_LockedBuffer == NULL);
|
|
|
|
// No
|
|
if (isResident() && !_KeepLocalMemory)
|
|
{
|
|
if (last == 0)
|
|
last = _NbIndexes;
|
|
// Can read it ?
|
|
nlassertex (_Location==RAMResident, ("Try to read a write only index buffer"));
|
|
_LockedBuffer = DrvInfos->lock (first, last, true);
|
|
}
|
|
else
|
|
{
|
|
if (_NonResidentIndexes.empty())
|
|
_LockedBuffer = NULL;
|
|
else
|
|
_LockedBuffer = const_cast<void*>((const void *) &(_NonResidentIndexes[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlassert ((first==0)&&(last==0));
|
|
}
|
|
|
|
_LockCounter++;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
inline void CIndexBuffer::unlock (uint first, uint end)
|
|
{
|
|
nlassertex (_LockCounter!=0, ("Index buffer not locked"));
|
|
nlassert (_LockedBuffer || (!isResident() && _NonResidentIndexes.empty()));
|
|
|
|
if (_LockCounter)
|
|
_LockCounter--;
|
|
|
|
if (_LockCounter == 0)
|
|
{
|
|
if (isResident() && !_KeepLocalMemory)
|
|
DrvInfos->unlock (0, 0);
|
|
|
|
_LockedBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
inline void CIndexBuffer::unlock () const
|
|
{
|
|
nlassertex (_LockCounter!=0, ("Index buffer not locked"));
|
|
nlassert (_LockedBuffer || (!isResident() && _NonResidentIndexes.empty()));
|
|
|
|
if (_LockCounter)
|
|
_LockCounter--;
|
|
|
|
if (_LockCounter == 0)
|
|
{
|
|
if (isResident() && !_KeepLocalMemory)
|
|
DrvInfos->unlock (0, 0);
|
|
|
|
_LockedBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
} // NL3D
|
|
|
|
|
|
#endif // NL_INDEX_BUFFER_H
|
|
|
|
/* End of index_buffer.h */
|