#ifndef __RESOURCE_HPP__ #define __RESOURCE_HPP__ /*==========================================================================; * * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. * * File: resource.hpp * Content: Base class header for resources. A resource is an non-trivial * object that is directly used by the graphics pipeline. It * be composed of a set of buffers; for example a mip-map is * a resource that is composed of Surfaces (which are buffers). * * Since resources are non-trivial (i.e. more than few bytes), * they may need management. The resource cooperates with the * Resource Manager component to get management functionality. * ***************************************************************************/ #include "d3dobj.hpp" // Forward Decl struct CMgmtInfo; class CResourceManager; // Handle for Resource Manager; internally implemented as a pointer typedef CMgmtInfo *RMHANDLE; // A Resource is a Base Object that additionally // has a Priority field class CResource : public CBaseObject { public: static HRESULT RestoreDriverManagementState(CBaseDevice *pDevice); // These methods are for the // use of the Resource Manager RMHANDLE RMHandle() const { return m_RMHandle; }; // RMHandle // Determine if a resource is managed or driver-managed BOOL IsD3DManaged() const { // Zero is not a valid RM handle return (m_RMHandle != 0); }; // IsD3DManaged // Set the device batch number that // this resource was last used in. In this // context; the batch refers to whether // this resource was used in the current // command buffer (i.e. containing unflushed commands). void Batch(); // Same as Batch() except it batches the // backing (or sysmem) texture rather than the // promoted (or vidmem) one. void BatchBase(); // Notifies the device that this resource // is about to be modified in a way that // may require a flush. (i.e. Whenever the bits // could change or a surface is going away.) void Sync(); // Sets batch number void SetBatchNumber(ULONGLONG batch) { // Batch numbers should only be increasing since we // start at zero. DXGASSERT(batch >= m_qwBatchCount); m_qwBatchCount = batch; } // SetBatchNumber // returns the batch number that this resource // was last referred in ULONGLONG GetBatchNumber() const { return m_qwBatchCount; } // returns the DrawPrim handle associated with // the Driver-Accessible clone if it is Managed; // otherwise returns the handle of itself. DWORD DriverAccessibleDrawPrimHandle() const; // returns the Kernel handle associated with // the Driver-Accessible clone if it is Managed; // otherwise returns the handle of itself. HANDLE DriverAccessibleKernelHandle() const; // Specifies a creation of a resource that // looks just like the current one. The LOD parameter // may not be relevant for all Resource types. virtual HRESULT Clone(D3DPOOL Pool, CResource **ppResource) const PURE; // Provides a method to access basic structure of the // pieces of the resource. virtual const D3DBUFFER_DESC* GetBufferDesc() const PURE; // Tells the resource that it should copy itself // to the target. It is the caller's responsibility // to make sure that Target is compatible with the // Source. (The Target may have different number of mip-levels // and be in a different pool; however, it must have the same size, // faces, format, etc.) // // This function will clear the dirty state. virtual HRESULT UpdateDirtyPortion(CResource *pResourceTarget) PURE; // Allows the Resource Manager to mark the texture // as needing to be completely updated on next // call to UpdateDirtyPortion virtual void MarkAllDirty() PURE; // Indicates whether the Resource has been modified since // the last time that UpdateDirtyPortion has been called. // All managed resources start out in the Dirty state. BOOL IsDirty() const { return m_bIsDirty; } // IsDirty void PreLoadImpl(); // Returns the pool which the user passed in D3DPOOL GetUserPool() const { return m_poolUser; } // GetUserPool protected: // The following are methods that only make sense // to be called by derived classes // Helper to check if a type is managed static BOOL IsTypeD3DManaged(CBaseDevice *pDevice, D3DRESOURCETYPE Type, D3DPOOL Pool); static BOOL IsTypeDriverManaged(CBaseDevice *pDevice, D3DRESOURCETYPE Type, D3DPOOL Pool); // Helper to determine what the 'real' pool is // for a managed resource. static D3DPOOL DetermineCreationPool(CBaseDevice *pDevice, D3DRESOURCETYPE Type, DWORD dwUsage, D3DPOOL Pool); // Constructor for Resources; all resources start out dirty CResource(CBaseDevice *pDevice, D3DPOOL Pool, REF_TYPE refType = REF_EXTERNAL) : CBaseObject(pDevice, refType), m_RMHandle(0), m_qwBatchCount(0), m_Priority(0), m_bIsDirty(TRUE), m_poolUser(Pool), m_pPrev(0) { m_pNext = pDevice->GetResourceList(); pDevice->SetResourceList(this); if (m_pNext != 0) { m_pNext->m_pPrev = this; } }; // CResource virtual ~CResource(); // Priority Inlines DWORD SetPriorityImpl(DWORD newPri); DWORD GetPriorityImpl(); // Allows initialization of the RMHandle after // construction is basically complete HRESULT InitializeRMHandle(); // Allows RMHandle to be set to zero void DeleteRMHandle(); // Helper to notify the RM that // we are now dirty. void OnResourceDirty(); // Helper to notify resource when it // is all clean void OnResourceClean(); // Resources need to implement OnDestroy by // calling Sync; (Textures overload this // to call OnTextureDestroy on the device before // calling their base class.) virtual void OnDestroy(void) { Sync(); return; } // OnDestroy // Returns the current priority DWORD GetPriorityI() const { return m_Priority; } // Sets the current priority (but does not do any work) DWORD SetPriorityI(DWORD Priority) { DWORD oldPriority = m_Priority; m_Priority = Priority; return oldPriority; } private: RMHANDLE m_RMHandle; ULONGLONG m_qwBatchCount; DWORD m_Priority; BOOL m_bIsDirty; // Remember the pool that the user passed in D3DPOOL m_poolUser; // Linked list of resources CResource *m_pPrev; CResource *m_pNext; friend CResourceManager; }; // CResource struct CMgmtInfo { // This is static because we assume all resources // to be in heap zero. WHEN the resource manager // supports multiple heaps, m_rmHeap should be // made per object again. static DWORD m_rmHeap; DWORD m_priority; DWORD m_LOD; BOOL m_bInUse; DWORD m_rmHeapIndex; DWORD m_scene; DWORD m_ticks; CResource *m_pRes; CResource *m_pBackup; CMgmtInfo(CResource*); ~CMgmtInfo(); ULONGLONG Cost() const { #ifdef _X86_ ULONGLONG retval; _asm { mov ebx, this; mov edx, [ebx]CMgmtInfo.m_bInUse; shl edx, 31; mov eax, [ebx]CMgmtInfo.m_priority; mov ecx, eax; shr eax, 1; or edx, eax; mov DWORD PTR retval + 4, edx; shl ecx, 31; mov eax, [ebx]CMgmtInfo.m_ticks; shr eax, 1; or eax, ecx; mov DWORD PTR retval, eax; } return retval; #else return ((ULONGLONG)m_bInUse << 63) + ((ULONGLONG)m_priority << 31) + ((ULONGLONG)(m_ticks >> 1)); #endif } }; // CMgmtInfo inline CMgmtInfo::CMgmtInfo(CResource *pBackup) { m_priority = 0; m_LOD = 0; m_bInUse = FALSE; m_rmHeap = 0; m_rmHeapIndex = 0; m_scene = 0; m_ticks = 0; m_pRes = 0; m_pBackup = pBackup; } // CMgmtInfo::CMgmtInfo inline CMgmtInfo::~CMgmtInfo() { if (m_pRes != 0) { m_pRes->DecrementUseCount(); } } // CMgmtInfo::~CMgmtInfo class CRMHeap { private: enum { InitialSize = 1023 }; DWORD m_next, m_size; CMgmtInfo **m_data_p; DWORD parent(DWORD k) const { return k / 2; } DWORD lchild(DWORD k) const { return k * 2; } DWORD rchild(DWORD k) const { return k * 2 + 1; } void heapify(DWORD k); public: CRMHeap(DWORD size = InitialSize); ~CRMHeap(); BOOL Initialize(); DWORD length() const { return m_next - 1; } CMgmtInfo* minCost() const { return m_data_p[1]; } BOOL add(CMgmtInfo*); CMgmtInfo* extractMin(); CMgmtInfo* extractMax(); CMgmtInfo* extractNotInScene(DWORD dwScene); void del(CMgmtInfo*); void update(CMgmtInfo*, BOOL inuse, DWORD priority, DWORD ticks); void resetAllTimeStamps(DWORD ticks); }; // class CRMHeap inline CRMHeap::CRMHeap(DWORD size) { m_next = 1; m_size = size + 1; } // CRMHeap::CRMHeap inline CRMHeap::~CRMHeap() { delete[] m_data_p; } // CRMHeap::~CRMHeap class CResourceManager { public: CResourceManager(); ~CResourceManager(); // Need to call before using the manager HRESULT Init(CBaseDevice *pD3D8); // Check to see if a type is going to driver managed // or going to be D3D managed BOOL IsDriverManaged(D3DRESOURCETYPE Type) const; // Specify that a resource needs to be managed // // Error indicates that we don't support management for this // resource type. HRESULT Manage(CResource *pResource, RMHANDLE *pHandle); // Stop managing a resouce; called when a managed resource // is going away void UnManage(RMHANDLE hRMHandle); // The RM manages Priority and LOD for the resource DWORD SetPriority(RMHANDLE hRMHandle, DWORD newPriority); DWORD SetLOD(RMHANDLE hRMHandle, DWORD dwLodNew); // Preloads resource into video memory void PreLoad(RMHANDLE hRMHandle); // Checks if the resource is in video memory BOOL InVidmem(RMHANDLE hRMHandle) const; // This is called when DrawPrimitive needs to // make sure that all resources used in the // current call are in video memory and are // uptodate. HRESULT UpdateVideo(RMHANDLE hRMHandle, BOOL *bDirty); HRESULT UpdateVideoInternal(CMgmtInfo *pMgmtInfo); // This returns the appropriate handle for a // managed resource DWORD DrawPrimHandle(RMHANDLE hRMHandle) const; // This returns the appropriate kernel handle for a // managed resource HANDLE KernelHandle(RMHANDLE hRMHandle) const; // This call will batch the appropriate resource // for the purpose of syncing void Batch(RMHANDLE hRMHandle, ULONGLONG batch) const; // Called from outside when a managed resource becomes dirty void OnResourceDirty(RMHANDLE hRMHandle) const; void DiscardBytes(DWORD cbBytes); void SceneStamp() { ++m_dwScene; } private: CBaseDevice *m_pD3D8; unsigned tcm_ticks, m_dwScene, m_dwNumHeaps; BOOL m_PreLoading; CRMHeap *m_heap_p; BOOL FreeResources(DWORD dwHeap, DWORD dwBytes); void Lock(RMHANDLE hRMHandle); void Unlock(RMHANDLE hRMHandle); void TimeStamp(CMgmtInfo *pMgmtInfo); }; // class CResourceManager #undef DPF_MODNAME #define DPF_MODNAME "CResource::IsTypeD3DManaged" inline BOOL CResource::IsTypeD3DManaged(CBaseDevice *pDevice, D3DRESOURCETYPE Type, D3DPOOL Pool) { if (Pool == D3DPOOL_MANAGED) { return !IsTypeDriverManaged(pDevice, Type, Pool); } else { return FALSE; } }; // IsTypeD3DManaged #undef DPF_MODNAME #define DPF_MODNAME "CResource::IsTypeDriverManaged" inline BOOL CResource::IsTypeDriverManaged(CBaseDevice *pDevice, D3DRESOURCETYPE Type, D3DPOOL Pool) { if (Pool == D3DPOOL_MANAGED) { if (pDevice->ResourceManager()->IsDriverManaged(Type)) { return TRUE; } } return FALSE; }; // IsTypeDriverManaged #undef DPF_MODNAME #define DPF_MODNAME "CResource::DetermineCreationPool" inline D3DPOOL CResource::DetermineCreationPool(CBaseDevice *pDevice, D3DRESOURCETYPE Type, DWORD dwUsage, D3DPOOL Pool) { if (Pool == D3DPOOL_MANAGED) { if (IsTypeDriverManaged(pDevice, Type, Pool)) { // This pool is used by the thunk layer // to use the driver management flag during // create return D3DPOOL_MANAGED; } else { // If it is not driver managed; then it // becomes D3DMANAGED return D3DPOOL_SYSTEMMEM; } } else { // Not managed at all; so we just // use the same pool we started with return Pool; } } // DetermineCreationPool #undef DPF_MODNAME #define DPF_MODNAME "CResource::~CResource" inline CResource::~CResource() { // If managed, we need to notify // the ResourceManager that we are going away if (IsD3DManaged()) { Device()->ResourceManager()->UnManage(m_RMHandle); } // Unlink from the resource list if (m_pNext != 0) { m_pNext->m_pPrev = m_pPrev; } if (m_pPrev != 0) { m_pPrev->m_pNext = m_pNext; DXGASSERT(Device()->GetResourceList() != this); } else { DXGASSERT(Device()->GetResourceList() == this); Device()->SetResourceList(m_pNext); } }; // ~CResource #undef DPF_MODNAME #define DPF_MODNAME "CResource::InitializeRMHandle" // Allows initialization of the RMHandle after // construction is basically complete inline HRESULT CResource::InitializeRMHandle() { // We should not already have a handle DXGASSERT(m_RMHandle == 0); // Get a handle from the resource manager return Device()->ResourceManager()->Manage(this, &m_RMHandle); }; // InitializeRMHandle #undef DPF_MODNAME #define DPF_MODNAME "CResource::DeleteRMHandle" inline void CResource::DeleteRMHandle() { // We should already have a handle DXGASSERT(m_RMHandle != 0); Device()->ResourceManager()->UnManage(m_RMHandle); m_RMHandle = 0; } #undef DPF_MODNAME #define DPF_MODNAME "CResource::OnResourceDirty" // Add a helper to notify the RM that // we are now dirty. inline void CResource::OnResourceDirty() { // Update our state m_bIsDirty = TRUE; // Only need to notify RM for managed textures // that have been been set through SetTexture if (IsD3DManaged() && IsInUse()) { Device()->ResourceManager()->OnResourceDirty(m_RMHandle); } return; }; // OnResourceDirty #undef DPF_MODNAME #define DPF_MODNAME "CResource::OnResourceClean" // Add a helper to help maintain m_bIsDirty bit inline void CResource::OnResourceClean() { DXGASSERT(m_bIsDirty == TRUE); m_bIsDirty = FALSE; return; }; // OnResourceDirty #undef DPF_MODNAME #define DPF_MODNAME "CResource::DriverAccessibleDrawPrimHandle" inline DWORD CResource::DriverAccessibleDrawPrimHandle() const { if (IsD3DManaged()) { // Return the DrawPrim handle of my clone return Device()->ResourceManager()->DrawPrimHandle(RMHandle()); } else { return BaseDrawPrimHandle(); } } // CResource::DriverAccessibleDrawPrimHandle #undef DPF_MODNAME #define DPF_MODNAME "CResource::DriverAccessibleKernelHandle" inline HANDLE CResource::DriverAccessibleKernelHandle() const { if (IsD3DManaged()) { // Return the DrawPrim handle of my clone HANDLE h = Device()->ResourceManager()->KernelHandle(RMHandle()); // If this handle is NULL, then it means it was called // without calling UpdateVideo which isn't allowed/sane DXGASSERT(h != NULL); return h; } else { return BaseKernelHandle(); } } // CResource::DriverAccessibleKernelHandle #undef DPF_MODNAME #define DPF_MODNAME "CResourceManager::CResourceManager" inline CResourceManager::CResourceManager() { m_pD3D8 = 0; tcm_ticks = m_dwScene = m_dwNumHeaps = 0; m_heap_p = 0; m_PreLoading = FALSE; } // CResourceManager::CResourceManager #undef DPF_MODNAME #define DPF_MODNAME "CResourceManager::~CResourceManager" inline CResourceManager::~CResourceManager() { // We should not call DiscardBytes here // because this destructor can be called via // the device destructor chain. In this situation // DiscardBytes will access bad or already freed // data. delete[] m_heap_p; } // CResourceManager::~CResourceManager #undef DPF_MODNAME #define DPF_MODNAME "CResourceManager::DrawPrimHandle" inline DWORD CResourceManager::DrawPrimHandle(RMHANDLE hRMHandle) const { if (InVidmem(hRMHandle)) { CMgmtInfo* &pMgmtInfo = hRMHandle; return pMgmtInfo->m_pRes->BaseDrawPrimHandle(); } else { return 0; } } // CResourceManager::DrawPrimHandle #undef DPF_MODNAME #define DPF_MODNAME "CResourceManager::KernelHandle" inline HANDLE CResourceManager::KernelHandle(RMHANDLE hRMHandle) const { if (InVidmem(hRMHandle)) { CMgmtInfo* &pMgmtInfo = hRMHandle; return pMgmtInfo->m_pRes->BaseKernelHandle(); } else { return 0; } } // CResourceManager::Kernelhandle #undef DPF_MODNAME #define DPF_MODNAME "CResourceManager::InVidmem" inline BOOL CResourceManager::InVidmem(RMHANDLE hRMHandle) const { CMgmtInfo* &pMgmtInfo = hRMHandle; DXGASSERT(pMgmtInfo != 0); return pMgmtInfo->m_pRes != 0; } // CResourceManager::InVidmem #undef DPF_MODNAME #define DPF_MODNAME "CResourceManager::Batch" inline void CResourceManager::Batch(RMHANDLE hRMHandle, ULONGLONG batch) const { if (InVidmem(hRMHandle)) { CMgmtInfo* &pMgmtInfo = hRMHandle; pMgmtInfo->m_pRes->SetBatchNumber(batch); } } // CResourceManager::Batch #undef DPF_MODNAME #define DPF_MODNAME "CResourceManager::UpdateVideo" inline HRESULT CResourceManager::UpdateVideo(RMHANDLE hRMHandle, BOOL *bDirty) { HRESULT ddrval = S_OK; CMgmtInfo* &pMgmtInfo = hRMHandle; if (!InVidmem(hRMHandle)) { ddrval = UpdateVideoInternal(pMgmtInfo); *bDirty = TRUE; } else { if (pMgmtInfo->m_pBackup->IsDirty()) { ddrval = pMgmtInfo->m_pBackup->UpdateDirtyPortion(pMgmtInfo->m_pRes); } TimeStamp(pMgmtInfo); } return ddrval; } #endif // __RESOURCE_HPP__