windows-nt/Source/XPSP1/NT/multimedia/directx/dxg/d3d8/fw/d3dobj.cpp
2020-09-26 16:20:57 +08:00

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