443 lines
12 KiB
C++
443 lines
12 KiB
C++
/*==========================================================================;
|
|
*
|
|
* Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: d3dobj.cpp
|
|
* Content: Base class implementation for resources and buffers
|
|
*
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "ddrawpr.h"
|
|
#include "d3dobj.hpp"
|
|
|
|
// Declare static data for CLockD3D
|
|
#ifdef DEBUG
|
|
DWORD CLockD3D::m_Count = 0;
|
|
#endif // DEBUG
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CBaseObject::AddRefImpl"
|
|
|
|
// Internal Implementations of AddRef and Release
|
|
DWORD CBaseObject::AddRefImpl()
|
|
{
|
|
// Internal objects should never be add-ref'ed
|
|
// or released
|
|
DXGASSERT(m_refType != REF_INTERNAL);
|
|
|
|
// Only Intrinsic objects should have a ref
|
|
// count of zero. (Internal objects also have
|
|
// a ref count of zero; but AddRef shouldn't
|
|
// be called for those.)
|
|
DXGASSERT(m_cRef > 0 || m_refType == REF_INTRINSIC);
|
|
|
|
// The first extra ref for an intrinsic
|
|
// object causes an add-ref to the device
|
|
if (m_cRef == 0)
|
|
{
|
|
DXGASSERT(m_refType == REF_INTRINSIC);
|
|
|
|
UINT crefDevice = m_pDevice->AddRef();
|
|
DXGASSERT(crefDevice > 1);
|
|
}
|
|
|
|
// InterlockedIncrement requires the memory
|
|
// to be aligned on DWORD boundary
|
|
DXGASSERT(((ULONG_PTR)(&m_cRef) & 3) == 0);
|
|
InterlockedIncrement((LONG *)&m_cRef);
|
|
|
|
return m_cRef;
|
|
} // AddRefImpl
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CBaseObject::AddRefImpl"
|
|
|
|
DWORD CBaseObject::ReleaseImpl()
|
|
{
|
|
// Internal objects should never be add-ref'ed
|
|
// or released
|
|
DXGASSERT(m_refType != REF_INTERNAL);
|
|
|
|
// Assert that we are not being
|
|
// over-released.
|
|
DXGASSERT(m_cRef > 0);
|
|
|
|
if (m_cRef == 0)
|
|
{
|
|
// This heinous state can happen if a texture
|
|
// was being held by the device; but not
|
|
// only has the app held onto a pointer to
|
|
// the texture, but they have released
|
|
// their own pointer.
|
|
|
|
// For this case, the safest thing to do
|
|
// is return zero instead of crashing.
|
|
return 0;
|
|
}
|
|
|
|
// InterlockedDecrement requires the memory
|
|
// to be aligned on DWORD boundary
|
|
DXGASSERT(((ULONG_PTR)(&m_cRef) & 3) == 0);
|
|
InterlockedDecrement((LONG *)&m_cRef);
|
|
if (m_cRef != 0)
|
|
{
|
|
// For a non-zero ref count,
|
|
// just return the value
|
|
return m_cRef;
|
|
}
|
|
|
|
// If we are not in use, then we delete ourselves
|
|
// otherwise we wait until we are no longer marked
|
|
// in use
|
|
if (m_cUseCount == 0)
|
|
{
|
|
DXGASSERT(m_cRef == 0);
|
|
|
|
// Before deleting a BaseObject,
|
|
// we need to call OnDestroy to make sure that
|
|
// there is nothing pending in the command
|
|
// stream that uses this object
|
|
OnDestroy();
|
|
|
|
delete this;
|
|
}
|
|
else
|
|
{
|
|
// To make sure that we don't again release the
|
|
// device at our destructor, we mark the object
|
|
// as being non-external (refcount is zero and usecount is
|
|
// non-zero). At this point, we know the object
|
|
// is not an internal one: hence it was either
|
|
// external or intrinsic. In either case, it could
|
|
// potentially be handed out again (through GetBackBuffer or
|
|
// GetTexture) and so we need to handle the case
|
|
// where AddRef might be called. We mark the object as
|
|
// INTRINSIC to indicate that that even though we don't
|
|
// have a Ref on the device (as soon as we release it below),
|
|
// we may need to acquire one if it gets add-ref'ed.
|
|
DXGASSERT(m_refType != REF_INTERNAL);
|
|
m_refType = REF_INTRINSIC;
|
|
|
|
// We are still in use by the device; but we don't
|
|
// have any external references; so we can
|
|
// release our reference on the device. (Note that
|
|
// even though this should be done before
|
|
// changing the reftype, this must be the LAST
|
|
// thing we do in this function.)
|
|
m_pDevice->Release();
|
|
|
|
// But this could have been the LAST reference to the
|
|
// device; which means that device will now have
|
|
// freed us; and the current object has now been
|
|
// deleted. So don't access any member data!!
|
|
//
|
|
// How can this happen? Imagine an app that releases
|
|
// everything except an external vb that is the current
|
|
// stream source: in this case, the only device ref is from
|
|
// the external object; but that extrenal object has a use
|
|
// count of 1; when the app calls release on the
|
|
// the vb, we end up here; calling release on the device
|
|
// causes in turn a call to DecrementUseCount on the
|
|
// current object; which causes the object to be freed.
|
|
}
|
|
|
|
// DO NOT PUT CODE HERE (see comment above)
|
|
|
|
return 0;
|
|
} // ReleaseImpl
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CBaseObject::SetPrivateDataImpl"
|
|
|
|
// Internal function that adds some private data
|
|
// information to an object. Supports having private
|
|
// data for multiple levels through the optional
|
|
// iLevel parameter
|
|
HRESULT CBaseObject::SetPrivateDataImpl(REFGUID refguidTag,
|
|
CONST VOID* pvData,
|
|
DWORD cbSize,
|
|
DWORD dwFlags,
|
|
BYTE iLevel)
|
|
{
|
|
if (cbSize > 0 &&
|
|
!VALID_PTR(pvData, cbSize))
|
|
{
|
|
DPF_ERR("Invalid pvData pointer to SetPrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
if (!VALID_PTR(&refguidTag, sizeof(GUID)))
|
|
{
|
|
DPF_ERR("Invalid guid to SetPrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
if (dwFlags & ~D3DSPD_IUNKNOWN)
|
|
{
|
|
DPF_ERR("Invalid flags to SetPrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
if (dwFlags & D3DSPD_IUNKNOWN)
|
|
{
|
|
if (cbSize != sizeof(LPVOID))
|
|
{
|
|
DPF_ERR("Invalid size for IUnknown to SetPrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
}
|
|
|
|
// Remember if we allocated a new node or
|
|
// not for error handling
|
|
BOOL fNewNode;
|
|
|
|
// Find the node in our list (if there)
|
|
CPrivateDataNode *pNode = Find(refguidTag, iLevel);
|
|
if (pNode)
|
|
{
|
|
// Clean up whatever it has already
|
|
pNode->Cleanup();
|
|
fNewNode = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Allocate a new node
|
|
pNode = new CPrivateDataNode;
|
|
if (pNode == NULL)
|
|
{
|
|
DPF_ERR("SetPrivateData failed a memory allocation");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Initialize a few fields
|
|
fNewNode = TRUE;
|
|
pNode->m_guid = refguidTag;
|
|
pNode->m_iLevel = iLevel;
|
|
}
|
|
|
|
// Initialize the other fields
|
|
pNode->m_dwFlags = dwFlags;
|
|
pNode->m_cbSize = cbSize;
|
|
|
|
// Copy the data portion over
|
|
if (dwFlags & D3DSPD_IUNKNOWN)
|
|
{
|
|
// We add-ref the object while we
|
|
// keep a pointer to it
|
|
pNode->m_pUnknown = (IUnknown *)pvData;
|
|
pNode->m_pUnknown->AddRef();
|
|
}
|
|
else
|
|
{
|
|
// Allocate a buffer to store our data
|
|
// into
|
|
pNode->m_pvData = new BYTE[cbSize];
|
|
if (pNode->m_pvData == NULL)
|
|
{
|
|
DPF_ERR("SetPrivateData failed a memory allocation");
|
|
// If memory allocation failed,
|
|
// then we may need to free the Node
|
|
if (fNewNode)
|
|
{
|
|
delete pNode;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
memcpy(pNode->m_pvData, pvData, cbSize);
|
|
}
|
|
|
|
// If we allocated a new Node then
|
|
// we need to put it into our list somewhere
|
|
if (fNewNode)
|
|
{
|
|
// Stuff it at the beginning
|
|
pNode->m_pNodeNext = m_pPrivateDataHead;
|
|
m_pPrivateDataHead = pNode;
|
|
}
|
|
|
|
return S_OK;
|
|
} // CBaseObject::SetPrivateDataImpl
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CBaseObject::GetPrivateDataImpl"
|
|
|
|
// Internal function that searches the private data list
|
|
// for a match. This supports a single list for a container
|
|
// and all of its children by using the iLevel parameter.
|
|
HRESULT CBaseObject::GetPrivateDataImpl(REFGUID refguidTag,
|
|
LPVOID pvBuffer,
|
|
LPDWORD pcbSize,
|
|
BYTE iLevel) const
|
|
{
|
|
if (!VALID_WRITEPTR(pcbSize, sizeof(DWORD)))
|
|
{
|
|
DPF_ERR("Invalid pcbSize pointer to GetPrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
if (pvBuffer)
|
|
{
|
|
if (*pcbSize > 0 &&
|
|
!VALID_WRITEPTR(pvBuffer, *pcbSize))
|
|
{
|
|
DPF_ERR("Invalid pvData pointer to GetPrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
}
|
|
|
|
if (!VALID_PTR(&refguidTag, sizeof(GUID)))
|
|
{
|
|
DPF_ERR("Invalid guid to GetPrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
// Find the node in our list
|
|
CPrivateDataNode *pNode = Find(refguidTag, iLevel);
|
|
if (pNode == NULL)
|
|
{
|
|
DPF_ERR("GetPrivateData failed to find a match.");
|
|
return D3DERR_NOTFOUND;
|
|
}
|
|
|
|
// Is the user just asking for the size?
|
|
if (pvBuffer == NULL)
|
|
{
|
|
// Return the amount of buffer that was used
|
|
*pcbSize = pNode->m_cbSize;
|
|
|
|
// Return Ok in this case.
|
|
return S_OK;
|
|
}
|
|
|
|
// Check if we were given a large enough buffer
|
|
if (*pcbSize < pNode->m_cbSize)
|
|
{
|
|
DPF(2, "GetPrivateData called with insufficient buffer.");
|
|
|
|
// If the buffer is insufficient, return
|
|
// the necessary size in the out parameter
|
|
*pcbSize = pNode->m_cbSize;
|
|
|
|
// An error is returned since pvBuffer != NULL and
|
|
// no data was actually returned.
|
|
return D3DERR_MOREDATA;
|
|
}
|
|
|
|
// There is enough room; so just overwrite with
|
|
// the right size
|
|
*pcbSize = pNode->m_cbSize;
|
|
|
|
// Handle the IUnknown case
|
|
if (pNode->m_dwFlags & D3DSPD_IUNKNOWN)
|
|
{
|
|
*(IUnknown**)pvBuffer = pNode->m_pUnknown;
|
|
|
|
// We Add-Ref the returned object
|
|
pNode->m_pUnknown->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
memcpy(pvBuffer, pNode->m_pvData, pNode->m_cbSize);
|
|
return S_OK;
|
|
|
|
} // CBaseObject::GetPrivateDataImpl
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CBaseObject::FreePrivateDataImpl"
|
|
|
|
HRESULT CBaseObject::FreePrivateDataImpl(REFGUID refguidTag,
|
|
BYTE iLevel)
|
|
{
|
|
if (!VALID_PTR(&refguidTag, sizeof(GUID)))
|
|
{
|
|
DPF_ERR("Invalid guid to FreePrivateData");
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
// Keep track of the address of the pointer
|
|
// that points to our current node
|
|
CPrivateDataNode **ppPrev = &m_pPrivateDataHead;
|
|
|
|
// Keep track of our current node
|
|
CPrivateDataNode *pNode = m_pPrivateDataHead;
|
|
|
|
// Find the node in our list
|
|
while (pNode)
|
|
{
|
|
// A match means that iLevel AND the guid
|
|
// match up
|
|
if (pNode->m_iLevel == iLevel &&
|
|
pNode->m_guid == refguidTag)
|
|
{
|
|
// If found, update the pointer
|
|
// the points to the current node to
|
|
// point to our Next
|
|
*ppPrev = pNode->m_pNodeNext;
|
|
|
|
// Delete the current node
|
|
delete pNode;
|
|
|
|
// We're done
|
|
return S_OK;
|
|
}
|
|
|
|
// Update our previous pointer address
|
|
ppPrev = &pNode->m_pNodeNext;
|
|
|
|
// Update our current node to point
|
|
// to the next node
|
|
pNode = pNode->m_pNodeNext;
|
|
}
|
|
|
|
DPF_ERR("FreePrivateData called but failed to find a match");
|
|
return D3DERR_NOTFOUND;
|
|
} // CBaseObject::FreePrivateDataImpl
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CBaseObject::Find"
|
|
|
|
// Helper function to iterate through the list of
|
|
// data members
|
|
CBaseObject::CPrivateDataNode * CBaseObject::Find(REFGUID refguidTag,
|
|
BYTE iLevel) const
|
|
{
|
|
CPrivateDataNode *pNode = m_pPrivateDataHead;
|
|
while (pNode)
|
|
{
|
|
if (pNode->m_iLevel == iLevel &&
|
|
pNode->m_guid == refguidTag)
|
|
{
|
|
return pNode;
|
|
}
|
|
pNode = pNode->m_pNodeNext;
|
|
}
|
|
return NULL;
|
|
} // CBaseObject::Find
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CBaseObject::CPrivateDataNode::Cleanup"
|
|
|
|
void CBaseObject::CPrivateDataNode::Cleanup()
|
|
{
|
|
if (m_dwFlags & D3DSPD_IUNKNOWN)
|
|
{
|
|
DXGASSERT(m_cbSize == sizeof(IUnknown *));
|
|
m_pUnknown->Release();
|
|
}
|
|
else
|
|
{
|
|
delete [] m_pvData;
|
|
}
|
|
m_pvData = NULL;
|
|
m_cbSize = 0;
|
|
m_dwFlags &= ~D3DSPD_IUNKNOWN;
|
|
|
|
return;
|
|
} // CBaseObject::CPrivateDataNode::Cleanup
|
|
|
|
|
|
|
|
// End of file : d3dobj.cpp
|