#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__