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

590 lines
20 KiB
C++

#ifndef __D3DOBJ_HPP__
#define __D3DOBJ_HPP__
/*==========================================================================;
*
* Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
*
* File: d3dobj.hpp
* Content: Base class header for resources and buffers
*
*
***************************************************************************/
// Helper function for parameter checking
inline BOOL IsPowerOfTwo(DWORD dwNumber)
{
if (dwNumber == 0 || (dwNumber & (dwNumber-1)) != 0)
return FALSE;
else
return TRUE;
} // IsPowerOfTwo
// forward decls
class CBaseObject;
// Entry-points that are inside/part of a device/enum may need to
// take a critical section. They do this by putting this
// line of code at the beginning of the API.
//
// Use API_ENTER for APIs that return an HRESULT;
// Use API_ENTER_RET for APIs that return a struct (as an out param)
// Use API_ENTER_VOID for APIs that return void.
// Use API_ENTER_SUBOBJECT_RELEASE for Release methods on sub-objects.
// Use API_ENTER_NOLOCK for Release methods on the Device or Enum itself.
// Use API_ENTER_NOLOCK for AddRef on anything.
//
//
// (Release is special because the action of release may cause the
// destruction of the device/enum; which would free the critical section
// before we got a chance to call Unlock on that critical section.)
//
// The class CLockOwner is a common base class for both CBaseDevice
// and CEnum.
#ifdef DEBUG
#define API_ENTER(pLockOwner) \
if (IsBadWritePtr((void *)this, sizeof(*this)) || \
IsBadWritePtr((void *)pLockOwner, sizeof(*pLockOwner)) || \
!pLockOwner->IsValid()) \
{ \
DPF_ERR("Invalid 'this' parameter to D3D8 API."); \
return D3DERR_INVALIDCALL; \
} \
CLockD3D _lock(pLockOwner, DPF_MODNAME, __FILE__)
// Use this for API's that return something other than an HRESULT
#define API_ENTER_RET(pLockOwner, RetType) \
if (IsBadWritePtr((void *)this, sizeof(*this)) || \
IsBadWritePtr((void *)pLockOwner, sizeof(*pLockOwner)) || \
!pLockOwner->IsValid()) \
{ \
DPF_ERR("Invalid 'this' parameter to D3D8 API.."); \
/* We only allow DWORD types of returns for compat */ \
/* with C users. */ \
return (RetType)0; \
} \
CLockD3D _lock(pLockOwner, DPF_MODNAME, __FILE__)
// Use this for API's that return void
#define API_ENTER_VOID(pLockOwner) \
if (IsBadWritePtr((void *)this, sizeof(*this)) || \
IsBadWritePtr((void *)pLockOwner, sizeof(*pLockOwner)) || \
!pLockOwner->IsValid()) \
{ \
DPF_ERR("Invalid 'this' parameter to D3D8 API..."); \
return; \
} \
CLockD3D _lock(pLockOwner, DPF_MODNAME, __FILE__)
// Use this for Release API's of subobjects i.e. not Device or Enum
#define API_ENTER_SUBOBJECT_RELEASE(pLockOwner) \
if (IsBadWritePtr((void *)this, sizeof(*this)) || \
IsBadWritePtr((void *)pLockOwner, sizeof(*pLockOwner)) || \
!pLockOwner->IsValid()) \
{ \
DPF_ERR("Invalid 'this' parameter to D3D8 API...."); \
return 0; \
} \
CLockD3D _lock(pLockOwner, DPF_MODNAME, __FILE__, TRUE)
// Use this for API tracing for methods
// that don't need a crit-sec lock at-all in Retail.
#define API_ENTER_NO_LOCK_HR(pLockOwner) \
if (IsBadWritePtr((void *)this, sizeof(*this)) || \
IsBadWritePtr((void *)pLockOwner, sizeof(*pLockOwner)) || \
!pLockOwner->IsValid()) \
{ \
DPF_ERR("Invalid 'this' parameter to D3D8 API....."); \
return D3DERR_INVALIDCALL; \
} \
CNoLockD3D _noLock(DPF_MODNAME, __FILE__)
// Use this for API tracing for Release for the device or enum
// (which is special; see note above). Also for AddRef for anything
#define API_ENTER_NO_LOCK(pLockOwner) \
if (IsBadWritePtr((void *)this, sizeof(*this)) || \
IsBadWritePtr((void *)pLockOwner, sizeof(*pLockOwner)) || \
!pLockOwner->IsValid()) \
{ \
DPF_ERR("Invalid 'this' parameter to D3D8 API......"); \
return 0; \
} \
CNoLockD3D _noLock(DPF_MODNAME, __FILE__)
#else // !DEBUG
#define API_ENTER(pLockOwner) \
CLockD3D _lock(pLockOwner)
#define API_ENTER_RET(pLockOwner, RetType) \
CLockD3D _lock(pLockOwner)
#define API_ENTER_VOID(pLockOwner) \
CLockD3D _lock(pLockOwner)
#define API_ENTER_SUBOBJECT_RELEASE(pLockOwner) \
CLockD3D _lock(pLockOwner, TRUE)
#define API_ENTER_NO_LOCK(pLockOwner)
#define API_ENTER_NO_LOCK_HR(pLockOwner)
#endif // !DEBUG
// This is a locking object that is supposed to work with
// the device/enum to determine whether and which critical section needs to
// be taken. The use of a destructor means that "Unlock" will happen
// automatically as part of the destructor
class CLockD3D
{
public:
#ifdef DEBUG
CLockD3D(CLockOwner *pLockOwner, char *moduleName, char *fileName, BOOL bSubObjectRelease = FALSE)
#else // !DEBUG
CLockD3D(CLockOwner *pLockOwner, BOOL bSubObjectRelease = FALSE)
#endif // !DEBUG
{
// Remember the device
m_pLockOwner = pLockOwner;
// Add-ref the LockOwner if there is a risk that
// that we might cause the LockOwner to go away in processing
// the current function i.e. SubObject Release
if (bSubObjectRelease)
{
m_pLockOwner->AddRefOwner();
// Remember to unlock it
m_bNeedToReleaseLockOwner = TRUE;
}
else
{
// No need to AddRef/Release the device
m_bNeedToReleaseLockOwner = FALSE;
}
// Ask the LockOwner to take a lock for us
m_pLockOwner->Lock();
#ifdef DEBUG
m_Count++;
DPF(6, "*** LOCK_D3D: CNT = %ld %s %s", m_Count, moduleName, fileName);
#endif
} // CD3DLock
~CLockD3D()
{
#ifdef DEBUG
DPF(6, "*** UNLOCK_D3D: CNT = %ld", m_Count);
m_Count--;
#endif // DEBUG
m_pLockOwner->Unlock();
// Call Release if we need to
if (m_bNeedToReleaseLockOwner)
m_pLockOwner->ReleaseOwner();
} // ~CD3DLock
private:
#ifdef DEBUG
static DWORD m_Count;
#endif // DEBUG
CLockOwner *m_pLockOwner;
BOOL m_bNeedToReleaseLockOwner;
}; // class CLockD3D
#ifdef DEBUG
// Helper debug-only class for API tracing
class CNoLockD3D
{
public:
CNoLockD3D(char *moduleName, char *fileName)
{
DPF(6, "*** LOCK_D3D: Module= %s %s", moduleName, fileName);
} // CD3DLock
~CNoLockD3D()
{
DPF(6, "*** UNLOCK_D3D:");
} // ~CD3DLock
}; // CNoLockD3D
#endif // DEBUG
//
// This header file contains the base class for all resources and buffers x
// types of objects
//
// The CBaseObject class contains functionality for the following
// services which can be used by the derived classes:
// AddRefImpl/ReleaseImpl
// Get/SetPrivateDataImpl data
// OpenDeviceImpl
//
// Base objects should allocated with "new" which is means that they
// should be 32-byte aligned by our default allocator.
//
// Resources inherit from CBaseObject and add functionality for
// priority
//
// Add-ref semantics for these objects is complex; a constructor
// flag indicates how/when/if the object will add-ref the device.
typedef enum
{
// External objects add-ref the device; they are
// freed by calling Release()
REF_EXTERNAL = 0,
// Intrinsic objects don't add-ref the device
// except for additional add-refs. They are freed
// when the number of releases equals the number of
// addrefs AND the device has called DecrUseCount
// on the object.
REF_INTRINSIC = 1,
// Internal is like intrinsic except that we
// assert that no one should ever call AddRef or Release
// on this object at all. To free it, you have to
// call DecrUseCount
REF_INTERNAL = 2,
} REF_TYPE;
class CBaseObject
{
public:
// Provides access to the two handles represent
// this object to the DDI/TokenStream. Specifically,
// this represents the Real Sys-Mem data in the
// case of Managed Resources. (To find
// the vid-mem mapping for a managed resource;
// see resource.hpp)
DWORD BaseDrawPrimHandle() const
{
return D3D8GetDrawPrimHandle(m_hKernelHandle);
} // DrawPrimHandle
HANDLE BaseKernelHandle() const
{
return m_hKernelHandle;
} // KernelHandle
// NOTE: No internal object should ever add-ref another
// internal object; otherwise we may end up with ref-count
// cycles that prevent anything from ever going away.
// Instead, an internal object may mark another internal
// object as being "InUse" which will force it to be kept
// in memory until it is no-longer in use (and the ref-count
// is zero.)
// Internal Implementations of AddRef and Release
DWORD AddRefImpl();
DWORD ReleaseImpl();
DWORD IncrementUseCount()
{
DXGASSERT(m_refType != REF_INTERNAL || m_cRef == 0);
m_cUseCount++;
return m_cUseCount;
} // IncrUseCount
DWORD DecrementUseCount()
{
DXGASSERT(m_refType != REF_INTERNAL || m_cRef == 0);
DXGASSERT(m_cUseCount > 0);
m_cUseCount--;
if (m_cUseCount == 0 && 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();
// Ok; now safe to delete the object
delete this;
return 0;
}
return m_cUseCount;
} // DecrUseCount
// Internal implementation functions for
// the PrivateData set of methods
HRESULT SetPrivateDataImpl(REFGUID refguidTag,
CONST VOID* pvData,
DWORD cbSize,
DWORD dwFlags,
BYTE iLevel);
HRESULT GetPrivateDataImpl(REFGUID refguidTag,
LPVOID pvBuffer,
LPDWORD pcbSize,
BYTE iLevel) const;
HRESULT FreePrivateDataImpl(REFGUID refguidTag,
BYTE iLevel);
// Implements the OpenDevice method
HRESULT GetDeviceImpl(IDirect3DDevice8 ** ppvInterface) const
{
if (!VALID_PTR_PTR(ppvInterface))
{
DPF_ERR("Invalid ppvInterface parameter passed to GetDevice");
return D3DERR_INVALIDCALL;
}
return m_pDevice->QueryInterface(IID_IDirect3DDevice8, (void**) ppvInterface);
}; // OpenDeviceImpl
CBaseDevice * Device() const
{
return m_pDevice;
} // Device
// Method to for swapping the underlying identity of
// a surface. Caller must make sure that we're not locked
// or other such badness.
void SwapKernelHandles(HANDLE *phKernelHandle)
{
DXGASSERT(m_hKernelHandle != NULL);
DXGASSERT(*phKernelHandle != NULL);
HANDLE tmp = m_hKernelHandle;
m_hKernelHandle = *phKernelHandle;
*phKernelHandle = tmp;
}
protected:
// The following are methods that are only
// accessible by derived classes: they don't make
// sense for other classes to call explicitly.
// Constructor
//
CBaseObject(CBaseDevice *pDevice, REF_TYPE ref = REF_EXTERNAL) :
m_pDevice(pDevice),
m_refType(ref),
m_hKernelHandle(NULL)
{
DXGASSERT(m_pDevice);
DXGASSERT(m_refType == REF_EXTERNAL ||
m_refType == REF_INTRINSIC ||
m_refType == REF_INTERNAL);
m_pPrivateDataHead = NULL;
if (ref == REF_EXTERNAL)
{
// External objects add-ref the device
// as well as having their own reference
// count set to one
m_pDevice->AddRef();
m_cRef = 1;
m_cUseCount = 0;
}
else
{
// Internal and intrinsic objects have no
// initial ref-count; but they are
// marked as InUse; the device frees them
// by calling DecrUseCount
m_cUseCount = 1;
m_cRef = 0;
}
}; // CBaseObject
// The destructor is marked virtual so that delete calls to
// the base interface will be handled properly
virtual ~CBaseObject()
{
DXGASSERT(m_pDevice);
DXGASSERT(m_cRef == 0);
DXGASSERT(m_cUseCount == 0);
CPrivateDataNode *pNode = m_pPrivateDataHead;
while (pNode)
{
CPrivateDataNode *pNodeNext = pNode->m_pNodeNext;
delete pNode;
pNode = pNodeNext;
}
if (m_refType == REF_EXTERNAL)
{
// Release our reference on the
// device
m_pDevice->Release();
}
}; // ~CBaseObject
// OnDestroy function is called just
// before an object is about to get deleted; we
// use this to provide synching as well as notification
// to FE when a texture is going away.
virtual void OnDestroy()
{
// Not all classes have to worry about this;
// so the default is to do nothing.
}; // OnDestroy
void SetKernelHandle(HANDLE hKernelHandle)
{
// This better be null; or we either leaking something
// or we had some uninitialized stuff that will hurt later
DXGASSERT(m_hKernelHandle == NULL);
// Remember our handles
m_hKernelHandle = hKernelHandle;
DXGASSERT(m_hKernelHandle != NULL);
} // SetHandle
BOOL IsInUse()
{
return (m_cUseCount > 0);
} // IsInUse
private:
//
// RefCount must be DWORD aligned
//
DWORD m_cRef;
// Keep a "Use Count" which indicates whether
// the device (or other object tied to the lifetime of
// the device) is relying on this object from staying around.
// This is used to prevent ref-count cycles that would
// occur whenever something like SetTexture add-ref'ed the
// the texture that was passed in.
DWORD m_cUseCount;
// Keep Track of the device that owns this object
// This is an add-ref'ed value
CBaseDevice *m_pDevice;
// We need an internal handle so that
// we can communicate to the driver/kernel.
//
// Note that the base object does not release this
// and it is the responsibility of the derived
// class to do so.
HANDLE m_hKernelHandle;
// Keep track of list of private data objects
class CPrivateDataNode;
CPrivateDataNode *m_pPrivateDataHead;
// Need to keep track of an intrinsic flag
// to decide whether to release the device on
// free
// CONSIDER: Try to merge this flag into some
// other variable.
REF_TYPE m_refType;
// Helper function to find the right node
CPrivateDataNode* Find(REFGUID refguidTag, BYTE iLevel) const;
// Each private data is stored in this node
// object which is only used for this purpose
// by this class
class CPrivateDataNode
{
public:
CPrivateDataNode() {};
~CPrivateDataNode()
{
Cleanup();
} // ~CPrivateDateNode
GUID m_guid;
CPrivateDataNode *m_pNodeNext;
DWORD m_cbSize;
DWORD m_dwFlags;
BYTE m_iLevel;
union
{
void *m_pvData;
IUnknown *m_pUnknown;
};
void Cleanup();
}; // CPrivateDataNode
}; // class CBaseObject
// Helper class for all surface types
//
class CBaseSurface : public IDirect3DSurface8
{
public:
CBaseSurface() :
m_isLocked(FALSE) {}
// Methods to allow D3D methods to treat all surface
// variants the same way
virtual DWORD DrawPrimHandle() const PURE;
virtual HANDLE KernelHandle() const PURE;
// See CBaseObject::IncrementUseCount and
// CBaseObject::DecrementUseCount and the
// description of the REF_TYPEs above.
virtual DWORD IncrementUseCount() PURE;
virtual DWORD DecrementUseCount() PURE;
// Batching logic is necessary to make sure
// that the command buffer is flushed before
// any read or write access is made to a
// a surface. This should be called at
// SetRenderTarget time; and it should be
// called for the currently set rendertarget
// and zbuffer when the batch count is updated
virtual void Batch() PURE;
// Sync should be called when a surface is
// about to be modified; Normally this is taken
// care of automatically by Lock; but it also
// needs to be called prior to using the Blt
// DDI.
virtual void Sync() PURE;
// Internal lock functions bypass
// parameter checking and also whether the
// surface marked as Lockable or LockOnce
// etc.
virtual HRESULT InternalLockRect(D3DLOCKED_RECT *pLockedRectData,
CONST RECT *pRect,
DWORD dwFlags) PURE;
virtual HRESULT InternalUnlockRect() PURE;
BOOL IsLocked() const
{
return m_isLocked;
}; // IsLocked
// Determines if a LOAD_ONCE surface has already
// been loaded
virtual BOOL IsLoaded() const PURE;
// Provides a method to access basic structure of the
// pieces of the resource.
virtual D3DSURFACE_DESC InternalGetDesc() const PURE;
// Access the device of the surface
virtual CBaseDevice *InternalGetDevice() const PURE;
public:
BOOL m_isLocked;
}; // class CBaseSurface
#endif // __D3DOBJ_HPP__