590 lines
20 KiB
C++
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__
|