1363 lines
43 KiB
C++
1363 lines
43 KiB
C++
|
/*==========================================================================;
|
||
|
*
|
||
|
* Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
|
||
|
*
|
||
|
* File: vbuffer.cpp
|
||
|
* Content: Implementation of the CVertexBuffer class.
|
||
|
*
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
#include "ddrawpr.h"
|
||
|
#include "d3di.hpp"
|
||
|
#include "ddi.h"
|
||
|
#include "drawprim.hpp"
|
||
|
#include "vbuffer.hpp"
|
||
|
#include "resource.inl"
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::Create"
|
||
|
|
||
|
// Static class function for creating a VertexBuffer object.
|
||
|
// (Because it is static; it doesn't have a this pointer.)
|
||
|
//
|
||
|
// We do all parameter checking here to reduce the overhead
|
||
|
// in the constructor which is called by the internal Clone
|
||
|
// method which is used by resource management as part of the
|
||
|
// performance critical download operation.
|
||
|
|
||
|
// Creation function for Vertex Buffers
|
||
|
HRESULT CVertexBuffer::Create(CBaseDevice *pDevice,
|
||
|
DWORD cbLength,
|
||
|
DWORD Usage,
|
||
|
DWORD dwFVF,
|
||
|
D3DPOOL Pool,
|
||
|
REF_TYPE refType,
|
||
|
IDirect3DVertexBuffer8 **ppVertexBuffer)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Do parameter checking here
|
||
|
if (!VALID_PTR_PTR(ppVertexBuffer))
|
||
|
{
|
||
|
DPF_ERR("Bad parameter passed for ppVertexBuffer for creating a vertex buffer");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
// Zero-out return parameter
|
||
|
*ppVertexBuffer = NULL;
|
||
|
|
||
|
if (cbLength == 0)
|
||
|
{
|
||
|
DPF_ERR("Vertex buffer cannot be of zero size");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
if (Pool != D3DPOOL_DEFAULT && Pool != D3DPOOL_MANAGED && Pool != D3DPOOL_SYSTEMMEM)
|
||
|
{
|
||
|
DPF_ERR("Vertex buffer pool should be default, managed or sysmem");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
// Usage flag allowed for only mixed mode or software device
|
||
|
if ((Usage & D3DUSAGE_SOFTWAREPROCESSING) != 0 &&
|
||
|
(pDevice->BehaviorFlags() & D3DCREATE_MIXED_VERTEXPROCESSING) == 0 &&
|
||
|
(pDevice->BehaviorFlags() & D3DCREATE_SOFTWARE_VERTEXPROCESSING) == 0)
|
||
|
{
|
||
|
DPF_ERR("D3DUSAGE_SOFTWAREPROCESSING can be set only when device is mixed or software mode. CreateVertexBuffer fails.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
// USAGE_DYNAMIC not allowed with management
|
||
|
if ((Usage & D3DUSAGE_DYNAMIC) != 0 && Pool == D3DPOOL_MANAGED)
|
||
|
{
|
||
|
DPF_ERR("D3DUSAGE_DYNAMIC cannot be used with managed vertex buffers");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
// Validate FVF
|
||
|
if (dwFVF != 0 && cbLength < ComputeVertexSizeFVF(dwFVF))
|
||
|
{
|
||
|
DPF_ERR("Vertex buffer size needs to enough to hold one vertex");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
D3DPOOL ActualPool = Pool;
|
||
|
DWORD ActualUsage = Usage;
|
||
|
|
||
|
// Infer Lock from absence of LoadOnce
|
||
|
if (!(Usage & D3DUSAGE_LOADONCE))
|
||
|
{
|
||
|
ActualUsage |= D3DUSAGE_LOCK;
|
||
|
}
|
||
|
|
||
|
// On a mixed device, POOL_SYSTEMMEM means the same as D3DUSAGE_SOFTWAREPROCESSING
|
||
|
if ((pDevice->BehaviorFlags() & D3DCREATE_MIXED_VERTEXPROCESSING) != 0 &&
|
||
|
Pool == D3DPOOL_SYSTEMMEM)
|
||
|
{
|
||
|
ActualUsage |= D3DUSAGE_SOFTWAREPROCESSING;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Put a VB in system memory if the following conditions are TRUE
|
||
|
* 1. (USAGE_SOFTWAREPROCESSING is set indicating app. wants to use software pipeline or if it is a software device) except if the vertices are pre-clipped TLVERTEX
|
||
|
* 2. USAGE_POINTS is set and we might do emulation of point sprites except if it is a managed VB on a mixed device
|
||
|
* 3. The driver does not support vidmem VBs
|
||
|
* 4. Usage NPathes and driver does not support NPatches
|
||
|
*/
|
||
|
if (!pDevice->DriverSupportsVidmemVBs())
|
||
|
{
|
||
|
ActualPool = D3DPOOL_SYSTEMMEM; // We don't set D3DUSAGE_SOFTWAREPROCESSING to ensure proper validation in fe code
|
||
|
}
|
||
|
if (((pDevice->BehaviorFlags() & D3DCREATE_SOFTWARE_VERTEXPROCESSING) != 0 || (ActualUsage & D3DUSAGE_SOFTWAREPROCESSING) != 0) &&
|
||
|
!((dwFVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW && (ActualUsage & D3DUSAGE_DONOTCLIP) != 0))
|
||
|
{
|
||
|
if((ActualUsage & D3DUSAGE_INTERNALBUFFER) == 0)
|
||
|
{
|
||
|
if ((pDevice->BehaviorFlags() & D3DCREATE_SOFTWARE_VERTEXPROCESSING) != 0 ||
|
||
|
ActualPool == D3DPOOL_DEFAULT)
|
||
|
{
|
||
|
ActualPool = D3DPOOL_SYSTEMMEM; // For software processing, pool can be only sysmem (POOLMANAGED is overwritten)
|
||
|
}
|
||
|
ActualUsage |= D3DUSAGE_SOFTWAREPROCESSING;
|
||
|
}
|
||
|
}
|
||
|
if ((ActualUsage & D3DUSAGE_NPATCHES) != 0 &&
|
||
|
(pDevice->GetD3DCaps()->DevCaps & D3DDEVCAPS_NPATCHES) == 0)
|
||
|
{
|
||
|
ActualPool = D3DPOOL_SYSTEMMEM;
|
||
|
ActualUsage |= D3DUSAGE_SOFTWAREPROCESSING;
|
||
|
}
|
||
|
|
||
|
if ((ActualUsage & D3DUSAGE_POINTS) != 0 &&
|
||
|
(static_cast<LPD3DBASE>(pDevice)->m_dwRuntimeFlags & D3DRT_DOPOINTSPRITEEMULATION) != 0)
|
||
|
{
|
||
|
if ((pDevice->BehaviorFlags() & D3DCREATE_SOFTWARE_VERTEXPROCESSING) != 0 ||
|
||
|
ActualPool == D3DPOOL_DEFAULT)
|
||
|
{
|
||
|
ActualPool = D3DPOOL_SYSTEMMEM; // For software processing, pool can be only sysmem (POOLMANAGED is overwritten)
|
||
|
}
|
||
|
ActualUsage |= D3DUSAGE_SOFTWAREPROCESSING;
|
||
|
}
|
||
|
|
||
|
CVertexBuffer *pVertexBuffer;
|
||
|
|
||
|
if (ActualPool == D3DPOOL_SYSTEMMEM ||
|
||
|
IsTypeD3DManaged(pDevice, D3DRTYPE_VERTEXBUFFER, ActualPool))
|
||
|
{
|
||
|
hr = CreateSysmemVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&pVertexBuffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (IsTypeDriverManaged(pDevice, D3DRTYPE_VERTEXBUFFER, ActualPool))
|
||
|
{
|
||
|
// If the vertex buffer is driver managed, but the usage is softwareprocessing, then
|
||
|
// we turn off writeonly since the fe pipe WILL read from the sysmem backup (which
|
||
|
// actually lives in the driver). It follows that when a driver manages a VB/IB without
|
||
|
// writeonly, it MUST have a sysmem backup. (snene - 12/00)
|
||
|
if ((ActualUsage & D3DUSAGE_SOFTWAREPROCESSING) != 0)
|
||
|
{
|
||
|
ActualUsage &= ~D3DUSAGE_WRITEONLY;
|
||
|
}
|
||
|
hr = CreateDriverManagedVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&pVertexBuffer);
|
||
|
// Driver managed vertex buffer creates can NEVER fail, except for catastrophic reasons so
|
||
|
// we don't fallback to sysmem. Even if we do fallback to sysmem here, there is no way
|
||
|
// deferred creates are going to fallback, so no point.
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = CreateDriverVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&pVertexBuffer);
|
||
|
}
|
||
|
if (FAILED(hr) && (hr != D3DERR_OUTOFVIDEOMEMORY || (ActualUsage & D3DUSAGE_INTERNALBUFFER) != 0))
|
||
|
{
|
||
|
if (hr == D3DERR_OUTOFVIDEOMEMORY)
|
||
|
{
|
||
|
DPF(2, "Out of video memory creating internal buffer");
|
||
|
}
|
||
|
if (pDevice->VBFailOversDisabled())
|
||
|
{
|
||
|
DPF_ERR("Cannot create Vidmem or Driver managed vertex buffer. Will ***NOT*** failover to Sysmem.");
|
||
|
return hr;
|
||
|
}
|
||
|
ActualPool = D3DPOOL_SYSTEMMEM;
|
||
|
hr = CreateSysmemVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&pVertexBuffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// We're done; just return the object
|
||
|
*ppVertexBuffer = pVertexBuffer;
|
||
|
|
||
|
return hr;
|
||
|
} // static Create
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::CreateDriverVertexBuffer"
|
||
|
|
||
|
HRESULT CVertexBuffer::CreateDriverVertexBuffer(CBaseDevice *pDevice,
|
||
|
DWORD cbLength,
|
||
|
DWORD dwFVF,
|
||
|
DWORD Usage,
|
||
|
DWORD ActualUsage,
|
||
|
D3DPOOL Pool,
|
||
|
D3DPOOL ActualPool,
|
||
|
REF_TYPE refType,
|
||
|
CVertexBuffer **pVB)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CDriverVertexBuffer *pVertexBuffer;
|
||
|
|
||
|
// Zero out return
|
||
|
*pVB = 0;
|
||
|
|
||
|
if((pDevice->BehaviorFlags() & D3DCREATE_MULTITHREADED) != 0)
|
||
|
{
|
||
|
pVertexBuffer = new CDriverVertexBufferMT(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&hr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pVertexBuffer = new CDriverVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&hr);
|
||
|
}
|
||
|
if (pVertexBuffer == 0)
|
||
|
{
|
||
|
DPF_ERR("Out of Memory creating vertex buffer");
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
if (refType == REF_EXTERNAL)
|
||
|
{
|
||
|
// External objects get released
|
||
|
pVertexBuffer->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Internal and intrinsic objects get decremented
|
||
|
DXGASSERT(refType == REF_INTERNAL || refType == REF_INTRINSIC);
|
||
|
pVertexBuffer->DecrementUseCount();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
*pVB = static_cast<CVertexBuffer*>(pVertexBuffer);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::CreateSysmemVertexBuffer"
|
||
|
|
||
|
HRESULT CVertexBuffer::CreateSysmemVertexBuffer(CBaseDevice *pDevice,
|
||
|
DWORD cbLength,
|
||
|
DWORD dwFVF,
|
||
|
DWORD Usage,
|
||
|
DWORD ActualUsage,
|
||
|
D3DPOOL Pool,
|
||
|
D3DPOOL ActualPool,
|
||
|
REF_TYPE refType,
|
||
|
CVertexBuffer **pVB)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CVertexBuffer *pVertexBuffer;
|
||
|
|
||
|
// Zero out return
|
||
|
*pVB = 0;
|
||
|
|
||
|
if((pDevice->BehaviorFlags() & D3DCREATE_MULTITHREADED) != 0)
|
||
|
{
|
||
|
pVertexBuffer = new CVertexBufferMT(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&hr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pVertexBuffer = new CVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&hr);
|
||
|
}
|
||
|
if (pVertexBuffer == 0)
|
||
|
{
|
||
|
DPF_ERR("Out of Memory creating vertex buffer");
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
if (refType == REF_EXTERNAL)
|
||
|
{
|
||
|
// External objects get released
|
||
|
pVertexBuffer->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Internal and intrinsic objects get decremented
|
||
|
DXGASSERT(refType == REF_INTERNAL || refType == REF_INTRINSIC);
|
||
|
pVertexBuffer->DecrementUseCount();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
*pVB = pVertexBuffer;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::CreateDriverManagedVertexBuffer"
|
||
|
|
||
|
HRESULT CVertexBuffer::CreateDriverManagedVertexBuffer(CBaseDevice *pDevice,
|
||
|
DWORD cbLength,
|
||
|
DWORD dwFVF,
|
||
|
DWORD Usage,
|
||
|
DWORD ActualUsage,
|
||
|
D3DPOOL Pool,
|
||
|
D3DPOOL ActualPool,
|
||
|
REF_TYPE refType,
|
||
|
CVertexBuffer **pVB)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CDriverManagedVertexBuffer *pVertexBuffer;
|
||
|
|
||
|
// Zero out return
|
||
|
*pVB = 0;
|
||
|
|
||
|
if((pDevice->BehaviorFlags() & D3DCREATE_MULTITHREADED) != 0)
|
||
|
{
|
||
|
pVertexBuffer = new CDriverManagedVertexBufferMT(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&hr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pVertexBuffer = new CDriverManagedVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
&hr);
|
||
|
}
|
||
|
if (pVertexBuffer == 0)
|
||
|
{
|
||
|
DPF_ERR("Out of Memory creating vertex buffer");
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
if (refType == REF_EXTERNAL)
|
||
|
{
|
||
|
// External objects get released
|
||
|
pVertexBuffer->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Internal and intrinsic objects get decremented
|
||
|
DXGASSERT(refType == REF_INTERNAL || refType == REF_INTRINSIC);
|
||
|
pVertexBuffer->DecrementUseCount();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
*pVB = static_cast<CVertexBuffer*>(pVertexBuffer);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::CVertexBuffer"
|
||
|
|
||
|
// Constructor the CVertexBuffer class
|
||
|
CVertexBuffer::CVertexBuffer(CBaseDevice *pDevice,
|
||
|
DWORD cbLength,
|
||
|
DWORD dwFVF,
|
||
|
DWORD Usage,
|
||
|
DWORD ActualUsage,
|
||
|
D3DPOOL Pool,
|
||
|
D3DPOOL ActualPool,
|
||
|
REF_TYPE refType,
|
||
|
HRESULT *phr
|
||
|
) :
|
||
|
CBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
D3DFMT_VERTEXDATA,
|
||
|
D3DRTYPE_VERTEXBUFFER,
|
||
|
Usage, // UserUsage
|
||
|
ActualUsage,
|
||
|
Pool, // UserPool
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
phr)
|
||
|
{
|
||
|
if (FAILED(*phr))
|
||
|
return;
|
||
|
|
||
|
// Initialize basic structures
|
||
|
m_desc.Format = D3DFMT_VERTEXDATA;
|
||
|
m_desc.Pool = ActualPool;
|
||
|
m_desc.Usage = ActualUsage;
|
||
|
m_desc.Type = D3DRTYPE_VERTEXBUFFER;
|
||
|
m_desc.Size = cbLength;
|
||
|
m_desc.FVF = dwFVF;
|
||
|
m_usageUser = Usage;
|
||
|
|
||
|
if (dwFVF != 0)
|
||
|
{
|
||
|
m_vertsize = ComputeVertexSizeFVF(dwFVF);
|
||
|
DXGASSERT(m_vertsize != 0);
|
||
|
m_numverts = cbLength / m_vertsize;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_vertsize = 0;
|
||
|
m_numverts = 0;
|
||
|
}
|
||
|
|
||
|
m_pClipCodes = 0;
|
||
|
|
||
|
// If this is a D3D managed buffer then we need
|
||
|
// to tell the Resource Manager to remember us. This has to happen
|
||
|
// at the very end of the constructor so that the important data
|
||
|
// members are built up correctly
|
||
|
if (CResource::IsTypeD3DManaged(Device(), D3DRTYPE_VERTEXBUFFER, ActualPool))
|
||
|
{
|
||
|
*phr = InitializeRMHandle();
|
||
|
}
|
||
|
} // CVertexBuffer::CVertexBuffer
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::Clone"
|
||
|
HRESULT CVertexBuffer::Clone(D3DPOOL Pool,
|
||
|
CResource **ppResource) const
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CVertexBuffer *pVertexBuffer;
|
||
|
// Note: we treat clones the same as internal; because
|
||
|
// they are owned by the resource manager which
|
||
|
// is owned by the device.
|
||
|
hr = CreateDriverVertexBuffer(Device(),
|
||
|
m_desc.Size,
|
||
|
m_desc.FVF,
|
||
|
m_desc.Usage,
|
||
|
(m_desc.Usage | D3DUSAGE_WRITEONLY) & ~D3DUSAGE_SOFTWAREPROCESSING, // never seen by API!
|
||
|
Pool,
|
||
|
Pool, // never seen by API!
|
||
|
REF_INTERNAL,
|
||
|
&pVertexBuffer);
|
||
|
*ppResource = static_cast<CResource*>(pVertexBuffer);
|
||
|
return hr;
|
||
|
} // CVertexBuffer::Clone
|
||
|
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::GetBufferDesc"
|
||
|
const D3DBUFFER_DESC* CVertexBuffer::GetBufferDesc() const
|
||
|
{
|
||
|
return (const D3DBUFFER_DESC*)&m_desc;
|
||
|
} // CVertexBuffer::GetBufferDesc
|
||
|
|
||
|
// IUnknown methods
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::QueryInterface"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::QueryInterface(REFIID riid,
|
||
|
LPVOID FAR * ppvObj)
|
||
|
{
|
||
|
API_ENTER(Device());
|
||
|
|
||
|
if (!VALID_PTR_PTR(ppvObj))
|
||
|
{
|
||
|
DPF_ERR("Invalid ppvObj parameter passed to CVertexBuffer::QueryInterface");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
if (!VALID_PTR(&riid, sizeof(GUID)))
|
||
|
{
|
||
|
DPF_ERR("Invalid guid memory address to QueryInterface for VertexBuffer");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
if (riid == IID_IDirect3DVertexBuffer8 ||
|
||
|
riid == IID_IDirect3DResource8 ||
|
||
|
riid == IID_IUnknown)
|
||
|
{
|
||
|
*ppvObj = static_cast<void*>(static_cast<IDirect3DVertexBuffer8 *>(this));
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
DPF_ERR("Unsupported Interface identifier passed to QueryInterface for VertexBuffer");
|
||
|
|
||
|
// Null out param
|
||
|
*ppvObj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
} // QueryInterface
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::AddRef"
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CVertexBuffer::AddRef()
|
||
|
{
|
||
|
API_ENTER_NO_LOCK(Device());
|
||
|
|
||
|
return AddRefImpl();
|
||
|
} // AddRef
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::Release"
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CVertexBuffer::Release()
|
||
|
{
|
||
|
API_ENTER_SUBOBJECT_RELEASE(Device());
|
||
|
|
||
|
return ReleaseImpl();
|
||
|
} // Release
|
||
|
|
||
|
// IDirect3DResource methods
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::GetDevice"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::GetDevice(IDirect3DDevice8 ** ppObj)
|
||
|
{
|
||
|
API_ENTER(Device());
|
||
|
|
||
|
return GetDeviceImpl(ppObj);
|
||
|
} // GetDevice
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::SetPrivateData"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::SetPrivateData(REFGUID riid,
|
||
|
CONST VOID* pvData,
|
||
|
DWORD cbData,
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
API_ENTER(Device());
|
||
|
|
||
|
// We use level zero for our data
|
||
|
return SetPrivateDataImpl(riid, pvData, cbData, dwFlags, 0);
|
||
|
} // SetPrivateData
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::GetPrivateData"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::GetPrivateData(REFGUID riid,
|
||
|
LPVOID pvData,
|
||
|
LPDWORD pcbData)
|
||
|
{
|
||
|
API_ENTER(Device());
|
||
|
|
||
|
// We use level zero for our data
|
||
|
return GetPrivateDataImpl(riid, pvData, pcbData, 0);
|
||
|
} // GetPrivateData
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::FreePrivateData"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::FreePrivateData(REFGUID riid)
|
||
|
{
|
||
|
API_ENTER(Device());
|
||
|
|
||
|
// We use level zero for our data
|
||
|
return FreePrivateDataImpl(riid, 0);
|
||
|
} // FreePrivateData
|
||
|
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::GetPriority"
|
||
|
|
||
|
STDMETHODIMP_(DWORD) CVertexBuffer::GetPriority()
|
||
|
{
|
||
|
API_ENTER_RET(Device(), DWORD);
|
||
|
|
||
|
return GetPriorityImpl();
|
||
|
} // GetPriority
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::SetPriority"
|
||
|
|
||
|
STDMETHODIMP_(DWORD) CVertexBuffer::SetPriority(DWORD dwPriority)
|
||
|
{
|
||
|
API_ENTER_RET(Device(), DWORD);
|
||
|
|
||
|
return SetPriorityImpl(dwPriority);
|
||
|
} // SetPriority
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::PreLoad"
|
||
|
|
||
|
STDMETHODIMP_(void) CVertexBuffer::PreLoad(void)
|
||
|
{
|
||
|
API_ENTER_VOID(Device());
|
||
|
|
||
|
PreLoadImpl();
|
||
|
return;
|
||
|
} // PreLoad
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::GetType"
|
||
|
STDMETHODIMP_(D3DRESOURCETYPE) CVertexBuffer::GetType(void)
|
||
|
{
|
||
|
API_ENTER_RET(Device(), D3DRESOURCETYPE);
|
||
|
|
||
|
return m_desc.Type;
|
||
|
} // GetType
|
||
|
|
||
|
// Vertex Buffer Methods
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::GetDesc"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::GetDesc(D3DVERTEXBUFFER_DESC *pDesc)
|
||
|
{
|
||
|
API_ENTER(Device());
|
||
|
|
||
|
if (!VALID_WRITEPTR(pDesc, sizeof(D3DVERTEXBUFFER_DESC)))
|
||
|
{
|
||
|
DPF_ERR("bad pointer for pDesc passed to GetDesc for VertexBuffer");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
*pDesc = m_desc;
|
||
|
|
||
|
// Need to return pool/usage that the user specified
|
||
|
pDesc->Pool = GetUserPool();
|
||
|
pDesc->Usage = m_usageUser;
|
||
|
|
||
|
return S_OK;
|
||
|
} // GetDesc
|
||
|
|
||
|
#if DBG
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::ValidateLockParams"
|
||
|
HRESULT CVertexBuffer::ValidateLockParams(UINT cbOffsetToLock,
|
||
|
UINT SizeToLock,
|
||
|
BYTE **ppbData,
|
||
|
DWORD dwFlags) const
|
||
|
{
|
||
|
if (!VALID_PTR_PTR(ppbData))
|
||
|
{
|
||
|
DPF_ERR("Bad parameter passed for ppbData for locking a vertexbuffer");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
if ((cbOffsetToLock != 0) && (SizeToLock == 0))
|
||
|
{
|
||
|
DPF_ERR("Cannot lock zero bytes. Vertex Buffer Lock fails.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
if (dwFlags & ~(D3DLOCK_VALID & ~D3DLOCK_NO_DIRTY_UPDATE)) // D3DLOCK_NO_DIRTY_UPDATE not valid for VBs
|
||
|
{
|
||
|
DPF_ERR("Invalid flags specified. Vertex Buffer Lock fails.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
// Can it be locked?
|
||
|
if (!m_isLockable)
|
||
|
{
|
||
|
DPF_ERR("Vertex buffer with D3DUSAGE_LOADONCE can only be locked once");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
if ((dwFlags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)) != 0 && (m_usageUser & D3DUSAGE_DYNAMIC) == 0)
|
||
|
{
|
||
|
DPF_ERR("Can specify D3DLOCK_DISCARD or D3DLOCK_NOOVERWRITE for only Vertex Buffers created with D3DUSAGE_DYNAMIC");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
if ((dwFlags & (D3DLOCK_READONLY | D3DLOCK_DISCARD)) == (D3DLOCK_READONLY | D3DLOCK_DISCARD))
|
||
|
{
|
||
|
DPF_ERR("Should not specify D3DLOCK_DISCARD along with D3DLOCK_READONLY. Vertex Buffer Lock fails.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
if ((dwFlags & D3DLOCK_READONLY) != 0 && (m_usageUser & D3DUSAGE_WRITEONLY) != 0)
|
||
|
{
|
||
|
DPF_ERR("Cannot do READ_ONLY lock on a WRITE_ONLY buffer. Vertex Buffer Lock fails.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
if (ULONGLONG(cbOffsetToLock) + ULONGLONG(SizeToLock) > ULONGLONG(m_desc.Size))
|
||
|
{
|
||
|
DPF_ERR("Lock failed: Locked area exceeds size of buffer. Vertex Buffer Lock fails.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
|
||
|
if (m_LockCount == 0)
|
||
|
{
|
||
|
if ((m_usageUser & D3DUSAGE_DYNAMIC) == 0)
|
||
|
{
|
||
|
if (static_cast<CD3DBase*>(Device())->m_SceneStamp == m_SceneStamp &&
|
||
|
(m_usageUser & D3DUSAGE_WRITEONLY) != 0 &&
|
||
|
GetUserPool() != D3DPOOL_SYSTEMMEM)
|
||
|
{
|
||
|
DPF(1, "Static vertex buffer locked more than once per frame. Could have severe performance penalty.");
|
||
|
}
|
||
|
((CVertexBuffer*)this)->m_SceneStamp = static_cast<CD3DBase*>(Device())->m_SceneStamp;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((dwFlags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)) == 0)
|
||
|
{
|
||
|
if (m_TimesLocked > 0 &&
|
||
|
(m_usageUser & D3DUSAGE_WRITEONLY) != 0 &&
|
||
|
GetUserPool() != D3DPOOL_SYSTEMMEM)
|
||
|
{
|
||
|
DPF(3, "Dynamic vertex buffer locked twice or more in a row without D3DLOCK_NOOVERWRITE or D3DLOCK_DISCARD. Could have severe performance penalty.");
|
||
|
}
|
||
|
++(((CVertexBuffer*)this)->m_TimesLocked);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
((CVertexBuffer*)this)->m_TimesLocked = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DXGASSERT(m_LockCount < 0x80000000);
|
||
|
|
||
|
return S_OK;
|
||
|
} // ValidateLockParams
|
||
|
#endif //DBG
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::Lock"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::Lock(UINT cbOffsetToLock,
|
||
|
UINT SizeToLock,
|
||
|
BYTE **ppbData,
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
// We do not take the API lock here since the MT class will take it for
|
||
|
// a multithreaded device. For a non-multithreaded device, there is no
|
||
|
// MT class nor do we bother to take the API lock. We still need to
|
||
|
// call API_ENTER_NO_LOCK_HR however for validation of the THIS pointer in
|
||
|
// Debug builds
|
||
|
API_ENTER_NO_LOCK_HR(Device());
|
||
|
|
||
|
#if DBG
|
||
|
HRESULT hr = ValidateLockParams(cbOffsetToLock, SizeToLock, ppbData, dwFlags);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
// Sanity check
|
||
|
#if DBG
|
||
|
if (m_LockCount != 0)
|
||
|
{
|
||
|
DXGASSERT(GetPrivateDataPointer() != 0);
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
// Increment our lock count
|
||
|
++m_LockCount;
|
||
|
|
||
|
if ((dwFlags & (D3DLOCK_READONLY | D3DLOCK_NOOVERWRITE)) == 0 && m_LockCount == 1) // for repeat locks, no syncing
|
||
|
{
|
||
|
Sync(); // Sync with device command queue
|
||
|
}
|
||
|
|
||
|
LockImpl(cbOffsetToLock,
|
||
|
SizeToLock,
|
||
|
ppbData,
|
||
|
dwFlags,
|
||
|
m_desc.Size);
|
||
|
|
||
|
return S_OK;
|
||
|
} // Lock
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::Unlock"
|
||
|
|
||
|
STDMETHODIMP CVertexBuffer::Unlock()
|
||
|
{
|
||
|
// We do not take the API lock here since the MT class will take it for
|
||
|
// a multithreaded device. For a non-multithreaded device, there is no
|
||
|
// MT class nor do we bother to take the API lock. We still need to
|
||
|
// call API_ENTER_NO_LOCK however for validation of the THIS pointer in
|
||
|
// Debug builds
|
||
|
API_ENTER_NO_LOCK_HR(Device());
|
||
|
|
||
|
#if DBG
|
||
|
// If we aren't locked; then something is wrong
|
||
|
if (m_LockCount == 0)
|
||
|
{
|
||
|
DPF_ERR("Unlock failed on a buffer; vertex buffer wasn't locked.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
// Decrement our lock count
|
||
|
--m_LockCount;
|
||
|
|
||
|
#if DBG
|
||
|
if ((m_usageUser & D3DUSAGE_LOADONCE) != 0 && m_LockCount == 0)
|
||
|
{
|
||
|
m_isLockable = FALSE;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
return S_OK;
|
||
|
} // Unlock
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::AllocateClipCodes"
|
||
|
|
||
|
void CVertexBuffer::AllocateClipCodes()
|
||
|
{
|
||
|
if (m_pClipCodes == 0)
|
||
|
{
|
||
|
DXGASSERT(m_numverts != 0);
|
||
|
m_pClipCodes = new WORD[m_numverts];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CVertexBuffer::UpdateDirtyPortion"
|
||
|
|
||
|
HRESULT CVertexBuffer::UpdateDirtyPortion(CResource *pResourceTarget)
|
||
|
{
|
||
|
if (IsDirty())
|
||
|
{
|
||
|
if (Device()->CanBufBlt())
|
||
|
{
|
||
|
D3DRANGE range;
|
||
|
if(m_cbDirtyMin == 0 && m_cbDirtyMax == 0)
|
||
|
{
|
||
|
range.Offset = 0;
|
||
|
range.Size = m_desc.Size;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
range.Offset = m_cbDirtyMin;
|
||
|
range.Size = m_cbDirtyMax - m_cbDirtyMin;
|
||
|
}
|
||
|
HRESULT hr = static_cast<LPD3DBASE>(Device())->BufBlt(static_cast<CBuffer*>(pResourceTarget), this, m_cbDirtyMin, &range);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Failed to copy vertex buffer");
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DXGASSERT(pResourceTarget->GetBufferDesc()->Pool == D3DPOOL_DEFAULT); // make sure that it is safe to assume that this is a driver VB
|
||
|
CDriverVertexBuffer *pBufferTarget = static_cast<CDriverVertexBuffer *>(pResourceTarget);
|
||
|
|
||
|
DXGASSERT((pBufferTarget->m_desc.Usage & D3DUSAGE_DYNAMIC) == 0); // Target can never be dynamic
|
||
|
DXGASSERT(pBufferTarget->m_pbData == 0); // Target can never be locked
|
||
|
|
||
|
HRESULT hr = pBufferTarget->LockI(D3DLOCK_NOSYSLOCK);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Failed to lock driver vertex buffer");
|
||
|
return hr;
|
||
|
}
|
||
|
DXGASSERT(pBufferTarget->m_pbData != 0);
|
||
|
|
||
|
if(m_cbDirtyMin == 0 && m_cbDirtyMax == 0)
|
||
|
{
|
||
|
memcpy(pBufferTarget->m_pbData, GetPrivateDataPointer(), m_desc.Size);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
memcpy(pBufferTarget->m_pbData + m_cbDirtyMin, GetPrivateDataPointer() + m_cbDirtyMin, m_cbDirtyMax - m_cbDirtyMin);
|
||
|
}
|
||
|
|
||
|
hr = pBufferTarget->UnlockI();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Failed to unlock driver vertex buffer");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
DXGASSERT(pBufferTarget->m_pbData == 0); // Target must be unlocked
|
||
|
}
|
||
|
|
||
|
// Mark ourselves as all clean now.
|
||
|
OnResourceClean();
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
} // CVertexBuffer::UpdateDirtyPortion
|
||
|
|
||
|
//=============================================
|
||
|
// Methods for the CDriverVertexBuffer class
|
||
|
//=============================================
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverVertexBuffer::CDriverVertexBuffer"
|
||
|
CDriverVertexBuffer::CDriverVertexBuffer(CBaseDevice *pDevice,
|
||
|
DWORD cbLength,
|
||
|
DWORD dwFVF,
|
||
|
DWORD Usage,
|
||
|
DWORD ActualUsage,
|
||
|
D3DPOOL Pool,
|
||
|
D3DPOOL ActualPool,
|
||
|
REF_TYPE refType,
|
||
|
HRESULT *phr
|
||
|
) :
|
||
|
CVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
phr),
|
||
|
m_pbData(0)
|
||
|
{
|
||
|
if (FAILED(*phr))
|
||
|
{
|
||
|
// We want to allow drivers to fail creation of driver vbs. In this
|
||
|
// case we will fail-over to system memory. However, if we
|
||
|
// DPF an error here, it will be misunderstood. So don't DPF.
|
||
|
return;
|
||
|
}
|
||
|
} // CDriverVertexBuffer::CDriverVertexBuffer
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverVertexBuffer::~CDriverVertexBuffer"
|
||
|
CDriverVertexBuffer::~CDriverVertexBuffer()
|
||
|
{
|
||
|
if (m_pbData != 0)
|
||
|
{
|
||
|
HRESULT hr = UnlockI();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Failed to unlock driver vertex buffer");
|
||
|
}
|
||
|
}
|
||
|
} // CDriverVertexBuffer::~CDriverVertexBuffer
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverVertexBuffer::LockI"
|
||
|
HRESULT CDriverVertexBuffer::LockI(DWORD dwFlags)
|
||
|
{
|
||
|
// We sync first to make sure that the
|
||
|
// driver has already processed any data that
|
||
|
// it needs. LockI only gets called if for
|
||
|
// cases where we need the interlock i.e.
|
||
|
// not readonly and not nooverwrite.
|
||
|
Sync();
|
||
|
|
||
|
// Prepare a LockData structure for the HAL call
|
||
|
D3D8_LOCKDATA lockData;
|
||
|
ZeroMemory(&lockData, sizeof lockData);
|
||
|
|
||
|
lockData.hDD = Device()->GetHandle();
|
||
|
lockData.hSurface = BaseKernelHandle();
|
||
|
lockData.bHasRange = FALSE;
|
||
|
lockData.dwFlags = dwFlags;
|
||
|
|
||
|
HRESULT hr = Device()->GetHalCallbacks()->Lock(&lockData);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Failed to lock driver vertex buffer");
|
||
|
}
|
||
|
|
||
|
// Return value
|
||
|
m_pbData = (BYTE*)lockData.lpSurfData;
|
||
|
|
||
|
return hr;
|
||
|
} // LockI
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverVertexBuffer::UnlockI"
|
||
|
HRESULT CDriverVertexBuffer::UnlockI()
|
||
|
{
|
||
|
// It is sometimes possible for the pre-DX8 DDI FlushStates to call
|
||
|
// Unlock twice. We safely filter this case.
|
||
|
if (m_pbData == 0)
|
||
|
{
|
||
|
DXGASSERT(!IS_DX8HAL_DEVICE(Device()));
|
||
|
return D3D_OK;
|
||
|
}
|
||
|
|
||
|
// Call the driver to perform the unlock
|
||
|
D3D8_UNLOCKDATA unlockData = {
|
||
|
Device()->GetHandle(),
|
||
|
BaseKernelHandle()
|
||
|
};
|
||
|
|
||
|
HRESULT hr = Device()->GetHalCallbacks()->Unlock(&unlockData);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Driver vertex buffer failed to unlock");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
m_pbData = 0;
|
||
|
|
||
|
return hr;
|
||
|
|
||
|
} // UnlockI
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverVertexBuffer::Lock"
|
||
|
|
||
|
STDMETHODIMP CDriverVertexBuffer::Lock(UINT cbOffsetToLock,
|
||
|
UINT SizeToLock,
|
||
|
BYTE **ppbData,
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
// We do not take the API lock here since the MT class will take it for
|
||
|
// a multithreaded device. For a non-multithreaded device, there is no
|
||
|
// MT class nor do we bother to take the API lock. We still need to
|
||
|
// call API_ENTER_NO_LOCK however for validation of the THIS pointer in
|
||
|
// Debug builds
|
||
|
API_ENTER_NO_LOCK_HR(Device());
|
||
|
|
||
|
HRESULT hr;
|
||
|
#if DBG
|
||
|
hr = ValidateLockParams(cbOffsetToLock, SizeToLock, ppbData, dwFlags);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
// Sanity check
|
||
|
#if DBG
|
||
|
if (m_LockCount != 0)
|
||
|
{
|
||
|
DXGASSERT(m_pbData != 0);
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
// Increment our lock count
|
||
|
// This MUST be done first. DO NOT MOVE THIS LINE.
|
||
|
++m_LockCount;
|
||
|
|
||
|
if(((dwFlags & (D3DLOCK_READONLY | D3DLOCK_NOOVERWRITE)) == 0 || m_pbData == 0) && m_LockCount == 1) // Repeat locks need no work
|
||
|
{
|
||
|
hr = static_cast<LPD3DBASE>(Device())->m_pDDI->LockVB(this, dwFlags);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Failed to lock driver vertex buffer");
|
||
|
*ppbData = 0;
|
||
|
--m_LockCount;
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*ppbData = m_pbData + cbOffsetToLock;
|
||
|
|
||
|
// Done
|
||
|
return S_OK;
|
||
|
} // Lock
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverVertexBuffer::Unlock"
|
||
|
|
||
|
STDMETHODIMP CDriverVertexBuffer::Unlock()
|
||
|
{
|
||
|
// We do not take the API lock here since the MT class will take it for
|
||
|
// a multithreaded device. For a non-multithreaded device, there is no
|
||
|
// MT class nor do we bother to take the API lock. We still need to
|
||
|
// call API_ENTER_NO_LOCK however for validation of the THIS pointer in
|
||
|
// Debug builds
|
||
|
API_ENTER_NO_LOCK_HR(Device());
|
||
|
|
||
|
#if DBG
|
||
|
// If we aren't locked; then something is wrong
|
||
|
if (m_LockCount == 0)
|
||
|
{
|
||
|
DPF_ERR("Unlock failed on a vertex buffer; buffer wasn't locked.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
if ((m_desc.Usage & D3DUSAGE_DYNAMIC) == 0 && m_LockCount == 1) // do work only for the last unlock
|
||
|
{
|
||
|
HRESULT hr = static_cast<LPD3DBASE>(Device())->m_pDDI->UnlockVB(this);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Driver failed to unlock vertex buffer");
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Decrement our lock count
|
||
|
--m_LockCount;
|
||
|
|
||
|
#if DBG
|
||
|
if ((m_usageUser & D3DUSAGE_LOADONCE) != 0 && m_LockCount == 0)
|
||
|
{
|
||
|
m_isLockable = FALSE;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
// Done
|
||
|
return S_OK;
|
||
|
} // Unlock
|
||
|
|
||
|
//=================================================
|
||
|
// Methods for the CDriverManagedVertexBuffer class
|
||
|
//=================================================
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverManagedVertexBuffer::CDriverManagedVertexBuffer"
|
||
|
CDriverManagedVertexBuffer::CDriverManagedVertexBuffer(CBaseDevice *pDevice,
|
||
|
DWORD cbLength,
|
||
|
DWORD dwFVF,
|
||
|
DWORD Usage,
|
||
|
DWORD ActualUsage,
|
||
|
D3DPOOL Pool,
|
||
|
D3DPOOL ActualPool,
|
||
|
REF_TYPE refType,
|
||
|
HRESULT *phr
|
||
|
) :
|
||
|
CVertexBuffer(pDevice,
|
||
|
cbLength,
|
||
|
dwFVF,
|
||
|
Usage,
|
||
|
ActualUsage,
|
||
|
Pool,
|
||
|
ActualPool,
|
||
|
refType,
|
||
|
phr),
|
||
|
m_pbData(0),
|
||
|
m_bDriverCalled(FALSE)
|
||
|
{
|
||
|
if (FAILED(*phr))
|
||
|
return;
|
||
|
// If writeonly is not set, we assume that the vertex/index buffer is going
|
||
|
// to be read from from time to time. Hence, for optimizing the readonly
|
||
|
// locks, we lock and cache the pointer. (snene - 12/00)
|
||
|
if ((ActualUsage & D3DUSAGE_WRITEONLY) == 0)
|
||
|
{
|
||
|
*phr = UpdateCachedPointer(pDevice);
|
||
|
if (FAILED(*phr))
|
||
|
return;
|
||
|
}
|
||
|
} // CDriverManagedVertexBuffer::CDriverManagedVertexBuffer
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverManagedVertexBuffer::UpdateCachedPointer"
|
||
|
|
||
|
HRESULT CDriverManagedVertexBuffer::UpdateCachedPointer(CBaseDevice *pDevice)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Prepare a LockData structure for the HAL call
|
||
|
D3D8_LOCKDATA lockData;
|
||
|
ZeroMemory(&lockData, sizeof lockData);
|
||
|
|
||
|
lockData.hDD = pDevice->GetHandle();
|
||
|
lockData.hSurface = BaseKernelHandle();
|
||
|
lockData.bHasRange = FALSE;
|
||
|
lockData.range.Offset = 0;
|
||
|
lockData.range.Size = 0;
|
||
|
lockData.dwFlags = D3DLOCK_READONLY;
|
||
|
|
||
|
hr = pDevice->GetHalCallbacks()->Lock(&lockData);
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
// Call the driver to perform the unlock
|
||
|
D3D8_UNLOCKDATA unlockData = {
|
||
|
pDevice->GetHandle(),
|
||
|
BaseKernelHandle()
|
||
|
};
|
||
|
|
||
|
hr = pDevice->GetHalCallbacks()->Unlock(&unlockData);
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
m_pbData = (BYTE*)lockData.lpSurfData;
|
||
|
|
||
|
return S_OK;
|
||
|
} // CDriverManagedVertexBuffer::UpdateCachedPointer
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverManagedVertexBuffer::Lock"
|
||
|
|
||
|
STDMETHODIMP CDriverManagedVertexBuffer::Lock(UINT cbOffsetToLock,
|
||
|
UINT SizeToLock,
|
||
|
BYTE **ppbData,
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
// We do not take the API lock here since the MT class will take it for
|
||
|
// a multithreaded device. For a non-multithreaded device, there is no
|
||
|
// MT class nor do we bother to take the API lock. We still need to
|
||
|
// call API_ENTER_NO_LOCK however for validation of the THIS pointer in
|
||
|
// Debug builds
|
||
|
API_ENTER_NO_LOCK_HR(Device());
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
#if DBG
|
||
|
hr = ValidateLockParams(cbOffsetToLock, SizeToLock, ppbData, dwFlags);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
// Increment our lock count
|
||
|
++m_LockCount;
|
||
|
|
||
|
if((dwFlags & D3DLOCK_READONLY) == 0)
|
||
|
{
|
||
|
// Sync with device command queue
|
||
|
Sync();
|
||
|
|
||
|
// Prepare a LockData structure for the HAL call
|
||
|
D3D8_LOCKDATA lockData;
|
||
|
ZeroMemory(&lockData, sizeof lockData);
|
||
|
|
||
|
lockData.hDD = Device()->GetHandle();
|
||
|
lockData.hSurface = BaseKernelHandle();
|
||
|
lockData.bHasRange = (SizeToLock != 0);
|
||
|
lockData.range.Offset = cbOffsetToLock;
|
||
|
lockData.range.Size = SizeToLock;
|
||
|
lockData.dwFlags = dwFlags;
|
||
|
|
||
|
hr = Device()->GetHalCallbacks()->Lock(&lockData);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
*ppbData = 0;
|
||
|
DPF_ERR("Failed to lock driver managed vertex buffer");
|
||
|
return hr;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Update cached pointer
|
||
|
m_pbData = (BYTE*)lockData.lpSurfData - cbOffsetToLock;
|
||
|
m_bDriverCalled = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*ppbData = m_pbData + cbOffsetToLock;
|
||
|
|
||
|
return hr;
|
||
|
|
||
|
} // Lock
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "CDriverManagedVertexBuffer::Unlock"
|
||
|
|
||
|
STDMETHODIMP CDriverManagedVertexBuffer::Unlock()
|
||
|
{
|
||
|
// We do not take the API lock here since the MT class will take it for
|
||
|
// a multithreaded device. For a non-multithreaded device, there is no
|
||
|
// MT class nor do we bother to take the API lock. We still need to
|
||
|
// call API_ENTER_NO_LOCK however for validation of the THIS pointer in
|
||
|
// Debug builds
|
||
|
API_ENTER_NO_LOCK_HR(Device());
|
||
|
|
||
|
#if DBG
|
||
|
// If we aren't locked; then something is wrong
|
||
|
if (m_LockCount == 0)
|
||
|
{
|
||
|
DPF_ERR("Unlock failed on a vertex buffer; buffer wasn't locked.");
|
||
|
return D3DERR_INVALIDCALL;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
if (m_bDriverCalled)
|
||
|
{
|
||
|
// Call the driver to perform the unlock
|
||
|
D3D8_UNLOCKDATA unlockData = {
|
||
|
Device()->GetHandle(),
|
||
|
BaseKernelHandle()
|
||
|
};
|
||
|
|
||
|
HRESULT hr = Device()->GetHalCallbacks()->Unlock(&unlockData);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("Driver vertex buffer failed to unlock");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
m_bDriverCalled = FALSE;
|
||
|
}
|
||
|
|
||
|
// Decrement our lock count
|
||
|
--m_LockCount;
|
||
|
|
||
|
#if DBG
|
||
|
if ((m_usageUser & D3DUSAGE_LOADONCE) != 0 && m_LockCount == 0)
|
||
|
{
|
||
|
m_isLockable = FALSE;
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
return S_OK;
|
||
|
} // Unlock
|
||
|
|
||
|
// End of file : vbuffer.cpp
|