// NeL - MMORPG Framework // 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 . #include "stddirect3d.h" #include "nel/3d/vertex_buffer.h" #include "nel/3d/light.h" #include "nel/3d/index_buffer.h" #include "nel/misc/rect.h" #include "nel/misc/di_event_emitter.h" #include "nel/misc/mouse_device.h" #include "nel/3d/viewport.h" #include "nel/3d/scissor.h" #include "nel/3d/u_driver.h" #include "driver_direct3d.h" using namespace std; using namespace NLMISC; // 500K min. #define NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE (20*1024) namespace NL3D { // *************************************************************************** CVBDrvInfosD3D::CVBDrvInfosD3D(CDriverD3D *drv, ItVBDrvInfoPtrList it, CVertexBuffer *vb) : IVBDrvInfos(drv, it, vb) { H_AUTO_D3D(CVBDrvInfosD3D_CVBDrvInfosD3D) VertexDecl = NULL; VertexDeclAliasDiffuseToSpecular = NULL; ColorOffset = 0; VertexBuffer = NULL; Usage = 0; VolatileVertexBuffer = NULL; VertexDeclNoDiffuse = NULL; #ifdef NL_DEBUG Locked = false; #endif Driver = drv; } // *************************************************************************** extern uint vertexCount=0; CVBDrvInfosD3D::~CVBDrvInfosD3D() { H_AUTO_D3D(CVBDrvInfosD3D_CVBDrvInfosD3D) CDriverD3D *driver = static_cast(_Driver); // Restaure non resident memory if (VertexBufferPtr) { VertexBufferPtr->setLocation(CVertexBuffer::NotResident); VertexBufferPtr = NULL; } // Don't release VertexDecl, it is release by the driver if (VertexBuffer && !Volatile) { if (Driver) { if (Driver->_VertexBufferCache.VertexBuffer == VertexBuffer) { Driver->_VertexBufferCache.VertexBuffer = NULL; Driver->touchRenderVariable(&Driver->_VertexBufferCache); } } vertexCount--; VertexBuffer->Release(); } // Stats if (Hardware) driver->_VertexBufferHardSet.erase(this); #ifdef NL_DEBUG if (Locked) { nlinfo("VBuffer %s is still locked at destruction", VertexBufferPtr->getName().c_str()) ; CDriverD3D *drv = NLMISC::safe_cast(_Driver); drv->_LockedBuffers.erase(this); } #endif } // *************************************************************************** uint8 *CVBDrvInfosD3D::lock (uint begin, uint end, bool readOnly) { H_AUTO_D3D(CVBDrvInfosD3D_lock) nlassert (begin != end); CDriverD3D *driver = static_cast(_Driver); //nlinfo("lock from %s", VertexBufferPtr->getName().c_str()); #ifdef NL_DEBUG nlassert(!Locked); driver->_LockedBuffers.insert(this); Locked = true; static volatile bool dumpLockedBuffers = false; if (dumpLockedBuffers) { nlinfo("Num locked buffers = %d", (int) driver->_LockedBuffers.size()); for(std::set::iterator it = driver->_LockedBuffers.begin(); it != driver->_LockedBuffers.end(); ++it) { if (!(*it)->VertexBufferPtr) { nlinfo("Empty buffer"); } else { nlinfo("Buffer %s at %x is Locked", (*it)->VertexBufferPtr->getName().c_str(), (int) *it); } } } #endif if (Volatile) { // Lock the good buffer CVolatileVertexBuffer *&buffer = VolatileRAM ? (driver->_VolatileVertexBufferRAM[driver->_CurrentRenderPass&1]): (driver->_VolatileVertexBufferAGP[driver->_CurrentRenderPass&1]); uint8 *ptr = (uint8*)buffer->lock (end-begin, Stride, Offset); if (!ptr) { // buffer full, swap them CVolatileVertexBuffer *&bufferOther = VolatileRAM ? (driver->_VolatileVertexBufferRAM[(driver->_CurrentRenderPass + 1) &1]): (driver->_VolatileVertexBufferAGP[(driver->_CurrentRenderPass + 1) &1]); std::swap(buffer, bufferOther); buffer->reset(); ptr = (uint8*)buffer->lock (end-begin, Stride, Offset); nlassert(ptr); } nlassert(!VolatileVertexBuffer); VolatileVertexBuffer = buffer; VertexBuffer = buffer->VertexBuffer; ptr -= begin; // Current lock time VolatileLockTime = driver->_CurrentRenderPass; // Touch the vertex buffer driver->touchRenderVariable (&driver->_VertexBufferCache); return ptr; } else { nlassert (VertexBuffer); // Lock Profile? TTicks beforeLock = 0; if(driver->_VBHardProfiling /*&& Hardware*/) { beforeLock= CTime::getPerformanceTime(); } void *pbData; if (VertexBuffer->Lock ( begin, end-begin, &pbData, readOnly?D3DLOCK_READONLY:0) != D3D_OK) return false; // Lock Profile? if(driver->_VBHardProfiling /*&& Hardware*/) { TTicks afterLock; afterLock= CTime::getPerformanceTime(); driver->appendVBHardLockProfile(afterLock-beforeLock, VertexBufferPtr); } return (uint8*)pbData; } } // *************************************************************************** void CVBDrvInfosD3D::unlock (uint /* begin */, uint /* end */) { H_AUTO_D3D(CVBDrvInfosD3D_unlock ) CDriverD3D *drv = NLMISC::safe_cast(_Driver); #ifdef NL_DEBUG nlassert(Locked); drv->_LockedBuffers.erase(this); Locked = false; #endif //nlinfo("unlock from %s", VertexBufferPtr->getName().c_str()); if (Volatile) { nlassert(VolatileVertexBuffer); VolatileVertexBuffer->unlock (); VolatileVertexBuffer = NULL; } else VertexBuffer->Unlock (); } // *************************************************************************** const D3DDECLTYPE RemapVertexBufferTypeNeL2D3D[CVertexBuffer::NumType]= { D3DDECLTYPE_UNUSED, // Double1, D3DDECLTYPE_FLOAT1, // Float1, D3DDECLTYPE_UNUSED, // Short1, D3DDECLTYPE_UNUSED, // Double2, D3DDECLTYPE_FLOAT2, // Float2, D3DDECLTYPE_SHORT2, // Short2, D3DDECLTYPE_UNUSED, // Double3, D3DDECLTYPE_FLOAT3, // Float3, D3DDECLTYPE_UNUSED, // Short3, D3DDECLTYPE_UNUSED, // Double4, D3DDECLTYPE_FLOAT4, // Float4, D3DDECLTYPE_SHORT4, // Short4, D3DDECLTYPE_D3DCOLOR, // UChar4, }; // *************************************************************************** const D3DDECLUSAGE RemapVertexBufferUsageNeL2D3D[CVertexBuffer::NumValue]= { D3DDECLUSAGE_POSITION, // Position D3DDECLUSAGE_NORMAL, // Normal D3DDECLUSAGE_TEXCOORD, // TexCoord0 D3DDECLUSAGE_TEXCOORD, // TexCoord1 D3DDECLUSAGE_TEXCOORD, // TexCoord2 D3DDECLUSAGE_TEXCOORD, // TexCoord3 D3DDECLUSAGE_TEXCOORD, // TexCoord4 D3DDECLUSAGE_TEXCOORD, // TexCoord5 D3DDECLUSAGE_TEXCOORD, // TexCoord6 D3DDECLUSAGE_TEXCOORD, // TexCoord7 D3DDECLUSAGE_COLOR, // PrimaryColor D3DDECLUSAGE_COLOR, // SecondaryColor D3DDECLUSAGE_BLENDWEIGHT, // Weight D3DDECLUSAGE_BLENDINDICES, // PaletteSkin D3DDECLUSAGE_FOG, // Fog }; // *************************************************************************** const uint RemapVertexBufferIndexNeL2D3D[CVertexBuffer::NumValue]= { 0, // Position 0, // Normal 0, // TexCoord0 1, // TexCoord1 2, // TexCoord2 3, // TexCoord3 4, // TexCoord4 5, // TexCoord5 6, // TexCoord6 7, // TexCoord7 0, // PrimaryColor 1, // SecondaryColor 0, // Weight 0, // PaletteSkin 0, // Fog }; // *************************************************************************** DWORD RemapVertexBufferUsage[CVertexBuffer::LocationCount]= { D3DUSAGE_DYNAMIC, // RAMResident D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, // AGPResident D3DUSAGE_WRITEONLY, // VRAMResident 0, // Not used }; // *************************************************************************** D3DPOOL RemapVertexBufferPool[CVertexBuffer::LocationCount]= { D3DPOOL_SYSTEMMEM, // RAMResident D3DPOOL_DEFAULT, // AGPResident D3DPOOL_DEFAULT, // VRAMResident D3DPOOL_DEFAULT, // Not used }; // *************************************************************************** bool CDriverD3D::activeVertexBuffer(CVertexBuffer& VB) { H_AUTO_D3D(CDriverD3D_activeVertexBuffer) // Must not be locked nlassert (!VB.isLocked()); // Must not be empty if (VB.capacity() == 0) return false; const bool touched = (VB.getTouchFlags() & (CVertexBuffer::TouchedReserve|CVertexBuffer::TouchedVertexFormat)) != 0; CVBDrvInfosD3D *info = static_cast(static_cast(VB.DrvInfos)); // Volatile buffers must be filled at each pass nlassertex (!info || !info->Volatile || VB.getKeepLocalMemory() || (info->VolatileLockTime == _CurrentRenderPass), ("Volatile buffers must be filled at each pass")); // Build the driver info if (touched) { // Delete previous vertex buffer info if (VB.DrvInfos) { delete VB.DrvInfos; nlassert (VB.DrvInfos == NULL); } // Force the vertex color format to BGRA VB.setVertexColorFormat (CVertexBuffer::TBGRA); // Rebuild it _VBDrvInfos.push_front (NULL); ItVBDrvInfoPtrList ite = _VBDrvInfos.begin(); info = new CVBDrvInfosD3D(this, ite, &VB); *ite = info; // Use vertex color ? info->UseVertexColor = (VB.getVertexFormat()&CVertexBuffer::PrimaryColorFlag) != 0; info->Stride = (uint8)VB.getVertexSize(); // Create the vertex declaration if (!createVertexDeclaration (VB.getVertexFormat(), VB.getValueTypePointer(), &(info->VertexDecl), info->ColorOffset, false, false)) return false; info->VertexDeclAliasDiffuseToSpecular = NULL; info->VertexDeclNoDiffuse = NULL; if (VB.hasValueEx(CVertexBuffer::PrimaryColor) && !VB.hasValueEx(CVertexBuffer::SecondaryColor)) { uint colorOffset2; if (!createVertexDeclaration (VB.getVertexFormat(), VB.getValueTypePointer(), &(info->VertexDeclAliasDiffuseToSpecular), colorOffset2, true, false)) return false; nlassert(colorOffset2 == info->ColorOffset); // should be the same value } if (_NbNeLTextureStages == 3 && info->UseVertexColor) { // Fix for radeon 7xxx -> if vertex color is not used it should not be declared (example : lighted material + vertex color but, no vertexColorLighted) uint colorOffset2; if (!createVertexDeclaration (VB.getVertexFormat(), VB.getValueTypePointer(), &(info->VertexDeclNoDiffuse), colorOffset2, false, true)) return false; } // Create the vertex buffer const uint size = VB.capacity()*VB.getVertexSize(); uint preferredMemory = 0; if (_DisableHardwareVertexArrayAGP) { preferredMemory = CVertexBuffer::RAMResident; info->Volatile = false; } else { switch (VB.getPreferredMemory ()) { case CVertexBuffer::RAMPreferred: preferredMemory = CVertexBuffer::RAMResident; info->Volatile = false; break; case CVertexBuffer::AGPPreferred: preferredMemory = CVertexBuffer::AGPResident; info->Volatile = false; break; case CVertexBuffer::StaticPreferred: if (getStaticMemoryToVRAM()) preferredMemory = CVertexBuffer::VRAMResident; else preferredMemory = CVertexBuffer::AGPResident; info->Volatile = false; break; case CVertexBuffer::RAMVolatile: preferredMemory = CVertexBuffer::RAMResident; info->Volatile = true; break; case CVertexBuffer::AGPVolatile: preferredMemory = CVertexBuffer::AGPResident; info->Volatile = true; break; } } // Volatile vertex buffer if (info->Volatile) { nlassert (info->VertexBuffer == NULL); info->Hardware = false; info->VolatileRAM = preferredMemory == CVertexBuffer::RAMResident; } else { // Offset will be 0 info->Offset = 0; bool success; do { success = _DeviceInterface->CreateVertexBuffer(size, RemapVertexBufferUsage[preferredMemory], 0, RemapVertexBufferPool[preferredMemory], &(info->VertexBuffer), NULL) == D3D_OK; if (success) break; } while (preferredMemory--); if (!success) return false; ++vertexCount; // Hardware ? info->Hardware = preferredMemory != CVertexBuffer::RAMResident; // Stats if (info->Hardware) _VertexBufferHardSet.insert(info); } // Release the local vertex buffer VB.DrvInfos = info; VB.setLocation ((CVertexBuffer::TLocation)preferredMemory); // Force the vertex buffer update touchRenderVariable (&_VertexDeclCache); touchRenderVariable (&_VertexBufferCache); } // Set the current vertex buffer nlassert (info); // Fill the buffer if in local memory VB.fillBuffer (); setVertexDecl (info->VertexDecl, info->VertexDeclAliasDiffuseToSpecular, info->VertexDeclNoDiffuse, info->Stride); //setVertexBuffer (info->VertexBuffer, info->Offset, info->Stride, info->UseVertexColor, VB.getNumVertices(), VB.getPreferredMemory(), info->Usage, info->ColorOffset); setVertexBuffer (info->VertexBuffer, info->Offset, info->Stride, info->UseVertexColor, VB.getNumVertices(), VB.getPreferredMemory(), info->Usage, info->ColorOffset); // Set UVRouting const uint8 *uvRouting = VB.getUVRouting(); uint i; for (i=0; i(static_cast(VB.DrvInfos)); static vector temp; uint size = VB.capacity()*VB.getVertexSize(); // No special flag for the lock, the driver should return a valid vertex buffer pointer with previous values. uint8 *out = info->lock (0, 0, false); nlassert (out); { temp.resize (size); memcpy (&(temp[0]), out, size); } info->unlock (0, 0); out = info->lock (0, 0, false); nlassert (out); { memcpy (out, &(temp[0]), size); } info->unlock (0, 0); */ return true; } // *************************************************************************** bool CDriverD3D::createVertexDeclaration (uint16 vertexFormat, const uint8 *typeArray, IDirect3DVertexDeclaration9 **vertexDecl, uint &colorOffset, bool aliasDiffuseToSpecular, bool bypassDiffuse, uint *stride) { H_AUTO_D3D(CDriverD3D_createVertexDeclaration) CVertexDeclaration declaration; if (aliasDiffuseToSpecular) { // there should be a single color stream : diffuse nlassert(vertexFormat & CVertexBuffer::PrimaryColorFlag); // diffuse required nlassert(!(vertexFormat & CVertexBuffer::SecondaryColorFlag)); // specular should not be used } // Set the vertex format uint i; uint j = 0; uint offset = 0; colorOffset = 0; for (i=0; i this free PrimaryColor to build a constant // Ueful to emulate per stage constant (which we can do on 2 stages only) } else { vertexElement.UsageIndex = BYTE(RemapVertexBufferIndexNeL2D3D[(uint)i]); } // nico : Fix for Radeon 7xxx series // Vertex declaration doesn't work when the vertex layout has vertex color defined after tex coord. // For example, the following layout (Position/TexCoord0/Diffuse) will silently be converted into (Position/Diffuse/TexCoord0) // It seems that the driver tries to map the vertex declaration to the matching FVF. FVF has a prefined order and requires Diffuse to appear // before texture coordinates in the vertex. Don't know if it is a limitation of D3D related to the 7xxx sries of if it is a driver bug. // The D3D debug dll doesn't issue a warning about it. // To solve this 2 vertex streams are declared : // - First streams contains Position/Normal/Texcoord // - When vertex color are used, second stream contains Diffuse/Specular vertex component(s) // In fact the 2 streams map to the same vertex buffer, but the 2nd stream has an added offset to point on the color component // I tried to add this offset directly into the vertex declaration, but D3D complains about it... // If the following field contains a non 0 value, then a second stream must be used for diffuse/specular with the given offset if (_NbNeLTextureStages == 3) { if (vertexElement.Usage == D3DDECLUSAGE_COLOR) { if (bypassDiffuse) { continue; } vertexElement.Stream = 1; if (colorOffset == 0) { vertexElement.Offset = 0; colorOffset = offset; } else { vertexElement.Offset = 4; } } } j++; } offset += CVertexBuffer::SizeType[typeArray[i]]; } } // Set the stride ? if (stride) *stride = offset; // End D3DVERTEXELEMENT9 end = D3DDECL_END(); declaration.VertexElements[j] = end; // Look for the same vertex declaration std::list::iterator ite = _VertexDeclarationList.begin(); while (ite != _VertexDeclarationList.end()) { for (i=0; i<=j; i++) { const D3DVERTEXELEMENT9 &vertexElementNew = declaration.VertexElements[i]; const D3DVERTEXELEMENT9 &vertexElementOld = ite->VertexElements[i]; if ( (vertexElementNew.Stream != vertexElementOld.Stream) || (vertexElementNew.Type != vertexElementOld.Type) || (vertexElementNew.Offset != vertexElementOld.Offset) || (vertexElementNew.Method != vertexElementOld.Method) || (vertexElementNew.Usage != vertexElementOld.Usage) || (vertexElementNew.UsageIndex != vertexElementOld.UsageIndex)) { break; } } // All is good ? if (i == (j+1)) { // It is the same vertex declaration *vertexDecl = ite->VertexDecl; return true; } ite++; } // Not found, create the vertex declaration if (_DeviceInterface->CreateVertexDeclaration (declaration.VertexElements, &(declaration.VertexDecl)) != D3D_OK) { return false; } // Add the vertex declaration _VertexDeclarationList.push_back (declaration); // Set the final declaration pointer *vertexDecl = declaration.VertexDecl; return true; } // *************************************************************************** bool CDriverD3D::supportVertexBufferHard() const { H_AUTO_D3D(CDriverD3D_supportVertexBufferHard) return !_DisableHardwareVertexArrayAGP; } // *************************************************************************** bool CDriverD3D::supportVolatileVertexBuffer() const { H_AUTO_D3D(CDriverD3D_supportVolatileVertexBuffer) return true; } // *************************************************************************** void CDriverD3D::disableHardwareVertexArrayAGP() { H_AUTO_D3D(CDriverD3D_disableHardwareVertexArrayAGP) _DisableHardwareVertexArrayAGP = true; } // *************************************************************************** uint CDriverD3D::getMaxVerticesByVertexBufferHard() const { H_AUTO_D3D(CDriverD3D_getMaxVerticesByVertexBufferHard) return _MaxVerticesByVertexBufferHard; } // *************************************************************************** uint32 CDriverD3D::getAvailableVertexAGPMemory () { H_AUTO_D3D(CDriverD3D_getAvailableVertexAGPMemory ) return _AGPMemoryAllocated; } // *************************************************************************** uint32 CDriverD3D::getAvailableVertexVRAMMemory () { H_AUTO_D3D(CDriverD3D_getAvailableVertexVRAMMemory ) return _VRAMMemoryAllocated; } // *************************************************************************** bool CDriverD3D::initVertexBufferHard(uint agpMem, uint vramMem) { H_AUTO_D3D(CDriverD3D_initVertexBufferHard) if(!supportVertexBufferHard()) return false; // First, reset any VBHard created. bool ok= true; // Try to allocate AGPMemory. _AGPMemoryAllocated = agpMem; if(_AGPMemoryAllocated>0) { _AGPMemoryAllocated&= ~15; // ensure 16-bytes aligned mem count (maybe useful :) ). _AGPMemoryAllocated= max(_AGPMemoryAllocated, (uint32)NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE); while(_AGPMemoryAllocated >= NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE) { IDirect3DVertexBuffer9 *vb; if (_DeviceInterface->CreateVertexBuffer (_AGPMemoryAllocated, D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &vb, NULL) == D3D_OK) { D3DVERTEXBUFFER_DESC desc; nlverify (vb->GetDesc (&desc) == D3D_OK); if (((desc.Usage&(D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC)) == (D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC)) && (desc.Pool == D3DPOOL_DEFAULT)) { nlinfo("%.d vertices supported", _MaxVerticesByVertexBufferHard); nlinfo("Success to allocate %.1f Mo of AGP VAR Ram", _AGPMemoryAllocated / 1000000.f); vb->Release(); break; } else vb->Release(); } else { _AGPMemoryAllocated/=2; _AGPMemoryAllocated &=~15; } } if(_AGPMemoryAllocated< NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE) { nlwarning("%.d vertices supported", _MaxVerticesByVertexBufferHard); nlwarning("Failed to allocate %.1f Mo of AGP VAR Ram", NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE / 1000000.f); ok= false; } } // Try to allocate VRAMMemory. _VRAMMemoryAllocated = vramMem; if(_VRAMMemoryAllocated>0) { _VRAMMemoryAllocated&= ~15; // ensure 16-bytes aligned mem count (maybe useful :) ). _VRAMMemoryAllocated= max(_VRAMMemoryAllocated, (uint32)NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE); while(_VRAMMemoryAllocated>= NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE) { IDirect3DVertexBuffer9 *vb; if (_DeviceInterface->CreateVertexBuffer (_VRAMMemoryAllocated, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &vb, NULL) == D3D_OK) { vb->Release(); break; } else { _VRAMMemoryAllocated/=2; _VRAMMemoryAllocated &=~15; } } if(_VRAMMemoryAllocated< NL3D_DRV_VERTEXARRAY_MINIMUM_SIZE) { ok= false; } } return ok; } // *************************************************************************** void CDriverD3D::mapTextureStageToUV(uint stage, uint uv) { H_AUTO_D3D(CDriverD3D_mapTextureStageToUV) setTextureIndexUV (stage, uv); } // *************************************************************************** // CVolatileVertexBuffer // *************************************************************************** CVolatileVertexBuffer::CVolatileVertexBuffer() { H_AUTO_D3D(CVolatileVertexBuffer_CVolatileVertexBuffer) VertexBuffer = NULL; Locked = false; } // *************************************************************************** CVolatileVertexBuffer::~CVolatileVertexBuffer() { H_AUTO_D3D(CVolatileVertexBuffer_CVolatileVertexBufferDtor) release (); } // *************************************************************************** void CVolatileVertexBuffer::release () { H_AUTO_D3D(CVolatileVertexBuffer_release ) if (VertexBuffer) VertexBuffer->Release(); VertexBuffer = NULL; } // *************************************************************************** void CVolatileVertexBuffer::init (CVertexBuffer::TLocation location, uint size, uint maxSize, CDriverD3D *driver) { H_AUTO_D3D(CVolatileVertexBuffer_init ) release(); if (maxSize < size) maxSize = size; MaxSize = maxSize; // Init the buffer Location = location; Size = size; Driver = driver; // Allocate the vertex buffer if (Driver->_DeviceInterface->CreateVertexBuffer(size, RemapVertexBufferUsage[location], 0, RemapVertexBufferPool[location], &VertexBuffer, NULL) != D3D_OK) { // Location in RAM must not failed nlassert (location != CVertexBuffer::RAMResident); // Allocate in RAM nlverify (Driver->_DeviceInterface->CreateVertexBuffer(size, RemapVertexBufferUsage[CVertexBuffer::RAMResident], 0, RemapVertexBufferPool[CVertexBuffer::RAMResident], &VertexBuffer, NULL) != D3D_OK); Location = CVertexBuffer::RAMResident; } } // *************************************************************************** //volatile int callCount = 0; //volatile int callStop = 17700; void *CVolatileVertexBuffer::lock (uint size, uint stride, uint &offset) { nlassertex(!Locked, ("Volatile buffer usage should follow an atomic lock/unlock/render sequence")); H_AUTO_D3D(CVolatileVertexBuffer_lock) /* If not enough room to allocate this buffer, resise the buffer to Size+Size/2 but do not reset CurrentIndex * to be sure the buffer will be large enough next pass. */ //if (callCount == callStop) // nlstop; //callCount++; // Align the index uint mod = CurrentIndex / stride; if (CurrentIndex != (mod*stride)) CurrentIndex = (mod+1)*stride; // Enough room for this vertex ? if (CurrentIndex+size+stride > Size) { if (CurrentIndex+size > MaxSize && CurrentIndex != 0) { reset(); if (size > MaxSize) { init (Location, std::max (std::min(Size+Size/2, MaxSize), size), MaxSize, Driver); } } else { // Max size not reached, so reallocate init (Location, std::max (std::min(Size+Size/2, MaxSize), CurrentIndex+size), MaxSize, Driver); reset(); // reallocate will cause a cpu stall anyway ... } } // Lock Profile? TTicks beforeLock = 0; if(Driver->_VBHardProfiling) { beforeLock= CTime::getPerformanceTime(); } // Lock the buffer, noblocking lock here if not the first allocation since a reset VOID *pbData; if (CurrentIndex==0) { nlverify (VertexBuffer->Lock (0, Size, &pbData, D3DLOCK_DISCARD) == D3D_OK); } else { nlverify (VertexBuffer->Lock (CurrentIndex, size, &pbData, D3DLOCK_NOOVERWRITE) == D3D_OK); } if(Driver->_VBHardProfiling) { TTicks afterLock; afterLock= CTime::getPerformanceTime(); Driver->_VolatileVBLockTime += afterLock - beforeLock; } // Old buffer position offset = CurrentIndex/stride; // New buffer position CurrentIndex += size; Locked = true; return pbData; } // *************************************************************************** void CVolatileVertexBuffer::unlock () { H_AUTO_D3D(CVolatileVertexBuffer_unlock ) nlassertex(Locked, ("Volatile buffer usage should follow an atomic lock/unlock/render sequence")); nlverify (VertexBuffer->Unlock () == D3D_OK); Locked = false; } // *************************************************************************** void CVolatileVertexBuffer::reset () { H_AUTO_D3D(CVolatileVertexBuffer_reset ) CurrentIndex = 0; // callCount = 0; } // *************************************************************************** } // NL3D