/*==========================================================================; * * 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(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(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(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(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(static_cast(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(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(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(Device())->BufBlt(static_cast(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(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(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(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