/*==========================================================================; * * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. * * File: mipmap.cpp * Content: Implementation of the CMipMap class. * * ***************************************************************************/ #include "ddrawpr.h" #include "mipmap.hpp" #include "mipsurf.hpp" #include "d3di.hpp" #include "resource.inl" #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::Create" // Static class function for creating a mip-map object. // // We do all parameter checking here to reduce the overhead // in the constructor which is called by the internal Clone // method which is used by resource management as part of the // performance critical download operation. HRESULT CMipMap::Create(CBaseDevice *pDevice, DWORD Width, DWORD Height, DWORD cLevels, DWORD Usage, D3DFORMAT UserFormat, D3DPOOL Pool, IDirect3DTexture8 **ppMipMap) { HRESULT hr; // Do parameter checking here if (!VALID_PTR_PTR(ppMipMap)) { DPF_ERR("Bad parameter passed pTexture. CreateTexture failed"); return D3DERR_INVALIDCALL; } // Zero-out return parameter *ppMipMap = NULL; // Check if format is valid hr = Validate(pDevice, D3DRTYPE_TEXTURE, Pool, Usage, UserFormat); if (FAILED(hr)) { // Validate does it's own DPFing return D3DERR_INVALIDCALL; } // Infer internal usage flags Usage = InferUsageFlags(Pool, Usage, UserFormat); // Expand cLevels if necessary if (cLevels == 0) { // See if HW can mip if ( (Pool != D3DPOOL_SCRATCH) && !(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_MIPMAP)) { // Can't mip so use 1 cLevels = 1; } else { // Determine number of levels cLevels = ComputeLevels(Width, Height); } } // Extra checks for multi-level case if (cLevels > 1) { if ((Width >> (cLevels - 1)) == 0 && (Height >> (cLevels - 1)) == 0) { DPF_ERR("Too many levels for mip-map of this size. CreateTexture failed."); return D3DERR_INVALIDCALL; } } if (cLevels > 32) { DPF_ERR("No more than 32 levels are supported. CreateTexture failed"); // This limitation is based on the number of // bits that we have allocated for iLevel in // some of the supporting classes. return D3DERR_INVALIDCALL; } D3DFORMAT RealFormat = UserFormat; // Start parameter checking if(Pool != D3DPOOL_SCRATCH) { //device-specific checking: // Check if device can do mipmaps if (cLevels > 1) { if (!(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_MIPMAP)) { DPF_ERR("Device doesn't support mip-map textures; CreateTexture failed."); return D3DERR_INVALIDCALL; } } // Check power-of-two constraints if (!IsPowerOfTwo(Width)) { if (pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_POW2) { if (!(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) { DPF_ERR("Device does not support non-pow2 width for texture"); return D3DERR_INVALIDCALL; } else if (cLevels > 1) { DPF_ERR("Device doesn't support non-pow2 width for multi-level texture"); return D3DERR_INVALIDCALL; } } } if (!IsPowerOfTwo(Height)) { if (pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_POW2) { if (!(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) { DPF_ERR("Device does not support non-pow2 height for texture. CreateTexture failed."); return D3DERR_INVALIDCALL; } else if (cLevels > 1) { DPF_ERR("Device doesn't support non-pow2 height for multi-level texture. CreateTexture failed."); return D3DERR_INVALIDCALL; } } } // See if the device requires square textures if (Width != Height) { if (pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) { DPF_ERR("Device requires square textures only. CreateTexture failed."); return D3DERR_INVALIDCALL; } } // Check texture size restrictions if (Width > pDevice->GetD3DCaps()->MaxTextureWidth) { DPF_ERR("Texture width is larger than what the device supports. CreateTexture failed."); return D3DERR_INVALIDCALL; } if (Height > pDevice->GetD3DCaps()->MaxTextureHeight) { DPF_ERR("Texture height is larger than what the device supports. CreateTexture failed."); return D3DERR_INVALIDCALL; } // Extra checks for multi-level case if (cLevels > 1) { // Check if the device can do multi-level mipmaps. if (!(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_MIPMAP)) { DPF_ERR("Device doesn't support multi-level mipmaps. CreateTexture failed."); return D3DERR_INVALIDCALL; } } // Map Depth/Stencil formats; returns no change if no // mapping is needed RealFormat = pDevice->MapDepthStencilFormat(UserFormat); } // Size may need to be 4x4 if (CPixel::Requires4X4(UserFormat)) { if ((Width & 3) || (Height & 3)) { DPF_ERR("DXT Formats require width/height to be a multiple of 4. CreateTexture failed."); return D3DERR_INVALIDCALL; } } // Validate against zero width/height if (Width == 0 || Height == 0) { DPF_ERR("Width and Height must be non-zero. CreateTexture failed."); return D3DERR_INVALIDCALL; } // We don't need to check if the HW can do textures since we // fail create if we find no texturing support // Allocate a new MipMap object and return it CMipMap *pMipMap = new CMipMap(pDevice, Width, Height, cLevels, Usage, UserFormat, RealFormat, Pool, REF_EXTERNAL, &hr); if (pMipMap == NULL) { DPF_ERR("Out of Memory creating texture. CreateTexture failed."); return E_OUTOFMEMORY; } if (FAILED(hr)) { DPF_ERR("Error during initialization of texture. CreateTexture failed."); pMipMap->ReleaseImpl(); return hr; } // We're done; just return the object *ppMipMap = pMipMap; return hr; } // static Create #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::CMipMap" // Constructor for the mip map class CMipMap::CMipMap(CBaseDevice *pDevice, DWORD Width, DWORD Height, DWORD cLevels, DWORD Usage, D3DFORMAT UserFormat, D3DFORMAT RealFormat, D3DPOOL UserPool, REF_TYPE refType, HRESULT *phr ) : CBaseTexture(pDevice, cLevels, UserPool, UserFormat, refType), m_prgMipSurfaces(NULL), m_rgbPixels(NULL), m_cRectUsed(MIPMAP_ALLDIRTY) { // Initialize basic structures m_desc.Format = RealFormat; m_desc.Pool = UserPool; m_desc.Usage = Usage; m_desc.Type = D3DRTYPE_TEXTURE; m_desc.MultiSampleType = D3DMULTISAMPLE_NONE; m_desc.Width = Width; m_desc.Height = Height; // Estimate size of memory allocation m_desc.Size = CPixel::ComputeMipMapSize(Width, Height, cLevels, RealFormat); // Allocate Pixel Data for SysMem or D3DManaged cases if (IS_D3D_ALLOCATED_POOL(UserPool) || IsTypeD3DManaged(Device(), D3DRTYPE_TEXTURE, UserPool)) { m_rgbPixels = new BYTE[m_desc.Size]; if (m_rgbPixels == NULL) { DPF_ERR("Out of memory allocating memory for mip-map levels."); *phr = E_OUTOFMEMORY; return; } // Mark our real pool as sys-mem m_desc.Pool = D3DPOOL_SYSTEMMEM; } // Create the DDSURFACEINFO array and CreateSurfaceData object DXGASSERT(cLevels <= 32); DDSURFACEINFO SurfInfo[32]; ZeroMemory(SurfInfo, sizeof(SurfInfo)); D3D8_CREATESURFACEDATA CreateSurfaceData; ZeroMemory(&CreateSurfaceData, sizeof(CreateSurfaceData)); // Set up the basic information CreateSurfaceData.hDD = pDevice->GetHandle(); CreateSurfaceData.pSList = &SurfInfo[0]; CreateSurfaceData.dwSCnt = cLevels; CreateSurfaceData.Type = D3DRTYPE_TEXTURE; CreateSurfaceData.dwUsage = m_desc.Usage; CreateSurfaceData.Format = RealFormat; CreateSurfaceData.MultiSampleType = D3DMULTISAMPLE_NONE; CreateSurfaceData.Pool = DetermineCreationPool(Device(), D3DRTYPE_TEXTURE, Usage, UserPool); // Iterate of each level to create the individual level // data for (DWORD iLevel = 0; iLevel < cLevels; iLevel++) { // Fill in the relevant information DXGASSERT(Width >= 1); DXGASSERT(Height >= 1); SurfInfo[iLevel].cpWidth = Width; SurfInfo[iLevel].cpHeight = Height; // If we allocated the memory, pass down // the sys-mem pointers if (m_rgbPixels) { D3DLOCKED_RECT lock; ComputeMipMapOffset(iLevel, NULL, // pRect &lock); SurfInfo[iLevel].pbPixels = (BYTE*)lock.pBits; SurfInfo[iLevel].iPitch = lock.Pitch; } // Scale width and height down if (Width > 1) { Width >>= 1; } if (Height > 1) { Height >>= 1; } } // Allocate array of pointers to MipSurfaces m_prgMipSurfaces = new CMipSurface*[cLevels]; if (m_prgMipSurfaces == NULL) { DPF_ERR("Out of memory creating mipmap"); *phr = E_OUTOFMEMORY; return; } // Zero the memory for safe cleanup ZeroMemory(m_prgMipSurfaces, sizeof(*m_prgMipSurfaces) * cLevels); if (UserPool != D3DPOOL_SCRATCH) { // Call the HAL to create this surface *phr = pDevice->GetHalCallbacks()->CreateSurface(&CreateSurfaceData); if (FAILED(*phr)) return; // NOTE: any failures after this point needs to free up some // kernel handles // Remember what pool we really got m_desc.Pool = CreateSurfaceData.Pool; // We need to remember the handles from the top most // level of the mip-map SetKernelHandle(SurfInfo[0].hKernelHandle); } // Create and Initialize each MipLevel for (iLevel = 0; iLevel < cLevels; iLevel++) { // Is this a sys-mem or scratch surface; could be d3d managed if (IS_D3D_ALLOCATED_POOL(m_desc.Pool)) { m_prgMipSurfaces[iLevel] = new CMipSurface(this, (BYTE)iLevel, SurfInfo[iLevel].hKernelHandle); } else { // Must be a driver kind of surface; could be driver managed m_prgMipSurfaces[iLevel] = new CDriverMipSurface(this, (BYTE)iLevel, SurfInfo[iLevel].hKernelHandle); } if (m_prgMipSurfaces[iLevel] == NULL) { DPF_ERR("Out of memory creating miplevel"); *phr = E_OUTOFMEMORY; if (UserPool != D3DPOOL_SCRATCH) { // Need to free handles that we got before we return; we // only free the ones that weren't successfully entrusted // to a CMipSurf because those will be cleaned up automatically // at their destructor for (UINT i = iLevel; i < cLevels; i++) { DXGASSERT(SurfInfo[i].hKernelHandle); D3D8_DESTROYSURFACEDATA DestroySurfData; DestroySurfData.hDD = Device()->GetHandle(); DestroySurfData.hSurface = SurfInfo[i].hKernelHandle; Device()->GetHalCallbacks()->DestroySurface(&DestroySurfData); } } return; } } // If this is a D3D managed mipmap then we need // to tell the Resource Manager to remember us. This has to happen // at the very end of the constructor so that the important data // members are built up correctly if (CResource::IsTypeD3DManaged(Device(), D3DRTYPE_TEXTURE, UserPool)) { *phr = InitializeRMHandle(); } return; } // CMipMap::CMipMap #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::~CMipMap" // Destructor CMipMap::~CMipMap() { // The destructor has to handle partially // created objects. Delete automatically // handles NULL; and members are nulled // as part of core constructors if (m_prgMipSurfaces) { for (DWORD i = 0; i < m_cLevels; i++) { delete m_prgMipSurfaces[i]; } delete [] m_prgMipSurfaces; } delete [] m_rgbPixels; } // CMipMap::~CMipMap // Methods for the Resource Manager #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::Clone" // Specifies a creation of a resource that // looks just like the current one; in a new POOL // with a new LOD. HRESULT CMipMap::Clone(D3DPOOL Pool, CResource **ppResource) const { // NULL out parameter *ppResource = NULL; // Determine the number of levels/width/height // of the clone DWORD cLevels = GetLevelCountImpl(); DWORD Width = m_desc.Width; DWORD Height = m_desc.Height; DWORD dwLOD = GetLODI(); // If LOD is zero, then there are no changes if (dwLOD > 0) { // Clamp LOD to cLevels-1 if (dwLOD >= cLevels) { dwLOD = cLevels - 1; } // scale down the destination texture // to correspond the appropiate max lod Width >>= dwLOD; if (Width == 0) Width = 1; Height >>= dwLOD; if (Height == 0) Height = 1; // Reduce the number based on the our max lod. cLevels -= dwLOD; } // Sanity checking DXGASSERT(cLevels >= 1); DXGASSERT(Width > 0); DXGASSERT(Height > 0); // Create the new mip-map object now // Note: we treat clones as REF_INTERNAL; because // they are owned by the resource manager which // is owned by the device. // Also, we adjust the usage to disable lock-flags // since we don't need lockability DWORD Usage = m_desc.Usage; Usage &= ~(D3DUSAGE_LOCK | D3DUSAGE_LOADONCE); HRESULT hr; CResource *pResource = new CMipMap(Device(), Width, Height, cLevels, Usage, m_desc.Format, // UserFormat m_desc.Format, // RealFormat Pool, REF_INTERNAL, &hr); if (pResource == NULL) { DPF_ERR("Failed to allocate mip-map object when copying"); return E_OUTOFMEMORY; } if (FAILED(hr)) { DPF(5, "Failed to create mip-map when doing texture management"); pResource->DecrementUseCount(); return hr; } *ppResource = pResource; return hr; } // CMipMap::Clone #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetBufferDesc" // Provides a method to access basic structure of the // pieces of the resource. A resource may be composed // of one or more buffers. const D3DBUFFER_DESC* CMipMap::GetBufferDesc() const { return (const D3DBUFFER_DESC*)&m_desc; } // CMipMap::GetBufferDesc // IUnknown methods #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::QueryInterface" STDMETHODIMP CMipMap::QueryInterface(REFIID riid, LPVOID FAR * ppvObj) { API_ENTER(Device()); if (!VALID_PTR_PTR(ppvObj)) { DPF_ERR("Invalid ppvObj parameter for a IDirect3DTexture8::QueryInterface"); return D3DERR_INVALIDCALL; } if (!VALID_PTR(&riid, sizeof(GUID))) { DPF_ERR("Invalid guid memory address to IDirect3DTexture8::QueryInterface"); return D3DERR_INVALIDCALL; } if (riid == IID_IDirect3DTexture8 || riid == IID_IDirect3DBaseTexture8 || riid == IID_IDirect3DResource8 || riid == IID_IUnknown) { *ppvObj = static_cast(static_cast(this)); AddRef(); return S_OK; } DPF_ERR("Unsupported Interface identifier passed to IDirect3DTexture8::QueryInterface"); // Null out param *ppvObj = NULL; return E_NOINTERFACE; } // QueryInterface #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::AddRef" STDMETHODIMP_(ULONG) CMipMap::AddRef() { API_ENTER_NO_LOCK(Device()); return AddRefImpl(); } // AddRef #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::Release" STDMETHODIMP_(ULONG) CMipMap::Release() { API_ENTER_SUBOBJECT_RELEASE(Device()); return ReleaseImpl(); } // Release // IDirect3DResource methods #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetDevice" STDMETHODIMP CMipMap::GetDevice(IDirect3DDevice8 **ppObj) { API_ENTER(Device()); return GetDeviceImpl(ppObj); } // GetDevice #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::SetPrivateData" STDMETHODIMP CMipMap::SetPrivateData(REFGUID riid, CONST VOID *pvData, DWORD cbData, DWORD dwFlags) { API_ENTER(Device()); // For the private data that 'really' belongs to the // MipMap, we use m_cLevels. (0 through m_cLevels-1 are for // each of the children levels.) return SetPrivateDataImpl(riid, pvData, cbData, dwFlags, m_cLevels); } // SetPrivateData #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetPrivateData" STDMETHODIMP CMipMap::GetPrivateData(REFGUID riid, VOID *pvData, DWORD *pcbData) { API_ENTER(Device()); // For the private data that 'really' belongs to the // MipMap, we use m_cLevels. (0 through m_cLevels-1 are for // each of the children levels.) return GetPrivateDataImpl(riid, pvData, pcbData, m_cLevels); } // GetPrivateData #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::FreePrivateData" STDMETHODIMP CMipMap::FreePrivateData(REFGUID riid) { API_ENTER(Device()); // For the private data that 'really' belongs to the // MipMap, we use m_cLevels. (0 through m_cLevels-1 are for // each of the children levels.) return FreePrivateDataImpl(riid, m_cLevels); } // FreePrivateData #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetPriority" STDMETHODIMP_(DWORD) CMipMap::GetPriority() { API_ENTER_RET(Device(), DWORD); return GetPriorityImpl(); } // GetPriority #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::SetPriority" STDMETHODIMP_(DWORD) CMipMap::SetPriority(DWORD dwPriority) { API_ENTER_RET(Device(), DWORD); return SetPriorityImpl(dwPriority); } // SetPriority #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::PreLoad" STDMETHODIMP_(void) CMipMap::PreLoad(void) { API_ENTER_VOID(Device()); PreLoadImpl(); return; } // PreLoad #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetType" STDMETHODIMP_(D3DRESOURCETYPE) CMipMap::GetType(void) { API_ENTER_RET(Device(), D3DRESOURCETYPE); return m_desc.Type; } // GetType // IDirect3DMipTexture methods #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetLOD" STDMETHODIMP_(DWORD) CMipMap::GetLOD() { API_ENTER_RET(Device(), DWORD); return GetLODImpl(); } // GetLOD #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::SetLOD" STDMETHODIMP_(DWORD) CMipMap::SetLOD(DWORD dwLOD) { API_ENTER_RET(Device(), DWORD); return SetLODImpl(dwLOD); } // SetLOD #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetLevelCount" STDMETHODIMP_(DWORD) CMipMap::GetLevelCount() { API_ENTER_RET(Device(), DWORD); return GetLevelCountImpl(); } // GetLevelCount // IDirect3DMipMap methods #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetLevelDesc" STDMETHODIMP CMipMap::GetLevelDesc(UINT iLevel, D3DSURFACE_DESC *pDesc) { API_ENTER(Device()); if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed GetLevelDesc of IDirect3DTexture8"); return D3DERR_INVALIDCALL; } return m_prgMipSurfaces[iLevel]->GetDesc(pDesc); } // GetLevelDesc; #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::GetSurfaceLevel" STDMETHODIMP CMipMap::GetSurfaceLevel(UINT iLevel, IDirect3DSurface8 **ppSurface) { API_ENTER(Device()); if (!VALID_PTR_PTR(ppSurface)) { DPF_ERR("Invalid parameter passed to GetSurfaceLevel of IDirect3DTexture8"); return D3DERR_INVALIDCALL; } if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed GetSurfaceLevel of IDirect3DTexture8"); *ppSurface = NULL; return D3DERR_INVALIDCALL; } *ppSurface = m_prgMipSurfaces[iLevel]; (*ppSurface)->AddRef(); return S_OK; } // GetSurfaceLevel #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::LockRect" STDMETHODIMP CMipMap::LockRect(UINT iLevel, D3DLOCKED_RECT *pLockedRectData, CONST RECT *pRect, DWORD dwFlags) { API_ENTER(Device()); // This is a high-frequency API, so we put parameter // checking into debug only #ifdef DEBUG if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed LockRect of IDirect3DTexture8"); return D3DERR_INVALIDCALL; } #endif // DEBUG return m_prgMipSurfaces[iLevel]->LockRect(pLockedRectData, pRect, dwFlags); } // LockRect #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::UnlockRect" STDMETHODIMP CMipMap::UnlockRect(UINT iLevel) { API_ENTER(Device()); // This is a high-frequency API; so we only do // parameter checking in debug #ifdef DEBUG if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed UnlockRect of IDirect3DTexture8"); return D3DERR_INVALIDCALL; } return m_prgMipSurfaces[iLevel]->UnlockRect(); #else // !DEBUG // We can go to the internal function to avoid // the unnecessary call and also to avoid the // crit-sec taken twice return m_prgMipSurfaces[iLevel]->InternalUnlockRect(); #endif // !DEBUG } // UnlockRect #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::UpdateTexture" // This function does type-specific parameter checking // before calling UpdateDirtyPortion HRESULT CMipMap::UpdateTexture(CBaseTexture *pResourceTarget) { CMipMap *pTexSource = static_cast(this); CMipMap *pTexDest = static_cast(pResourceTarget); // Figure out how many levels in the source to skip DXGASSERT(pTexSource->m_cLevels >= pTexDest->m_cLevels); DWORD StartLevel = pTexSource->m_cLevels - pTexDest->m_cLevels; DXGASSERT(StartLevel < 32); // Compute the size of the top level of the source that is // going to be copied. UINT SrcWidth = pTexSource->Desc()->Width; UINT SrcHeight = pTexSource->Desc()->Height; if (StartLevel > 0) { SrcWidth >>= StartLevel; SrcHeight >>= StartLevel; if (SrcWidth == 0) SrcWidth = 1; if (SrcHeight == 0) SrcHeight = 1; } // Source and Dest should be the same sizes at this point if (SrcWidth != pTexDest->Desc()->Width) { if (StartLevel) { DPF_ERR("Source and Destination for UpdateTexture are not" " compatible. Since both have the same number of" " mip-levels; their widths must match."); } else { DPF_ERR("Source and Destination for UpdateTexture are not" " compatible. Since they have the different numbers of" " mip-levels; the widths of the bottom-most levels of" " the source must match all the corresponding levels" " of the destination."); } return D3DERR_INVALIDCALL; } if (SrcHeight != pTexDest->Desc()->Height) { if (StartLevel) { DPF_ERR("Source and Destination for UpdateTexture are not" " compatible. Since both have the same number of" " mip-levels; their heights must match."); } else { DPF_ERR("Source and Destination for UpdateTexture are not" " compatible. Since they have the different numbers of" " mip-levels; the heights of the bottom-most levels of" " the source must match all the corresponding levels" " of the destination."); } return D3DERR_INVALIDCALL; } return UpdateDirtyPortion(pResourceTarget); } // UpdateTexture #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::UpdateDirtyPortion" // 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. HRESULT CMipMap::UpdateDirtyPortion(CResource *pResourceTarget) { HRESULT hr; // If we are clean, then do nothing if (m_cRectUsed == 0) { if (IsDirty()) { DPF_ERR("A Texture has been locked with D3DLOCK_NO_DIRTY_UPDATE but " "no call to AddDirtyRect was made before the texture was used. " "Hardware texture was not updated."); } return S_OK; } // We are dirty; so we need to get some pointers CMipMap *pTexSource = static_cast(this); CMipMap *pTexDest = static_cast(pResourceTarget); if (CanTexBlt(pTexDest)) { if (m_cRectUsed == MIPMAP_ALLDIRTY) { POINT p = {0, 0}; RECTL r = {0, 0, Desc()->Width, Desc()->Height}; hr = static_cast(Device())->TexBlt(pTexDest, pTexSource, &p, &r); if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state"); return hr; } } else { DXGASSERT(m_cRectUsed < MIPMAP_ALLDIRTY); for (DWORD i = 0; i < m_cRectUsed; i++) { hr = static_cast(Device())->TexBlt(pTexDest, pTexSource, (LPPOINT)&m_DirtyRectArray[i], (LPRECTL)&m_DirtyRectArray[i]); if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state"); return hr; } } } } else { // We can't use TexBlt, so we have to copy each level individually // through InternalCopyRects // Determine number of source levels to skip DXGASSERT(pTexSource->m_cLevels >= pTexDest->m_cLevels); DWORD StartLevel = pTexSource->m_cLevels - pTexDest->m_cLevels; DWORD LevelsToCopy = pTexSource->m_cLevels - StartLevel; CBaseSurface *pSurfaceSrc; CBaseSurface *pSurfaceDest; if (m_cRectUsed == MIPMAP_ALLDIRTY) { for (DWORD iLevel = 0; iLevel < LevelsToCopy; iLevel++) { DXGASSERT(iLevel + StartLevel < this->m_cLevels); DXGASSERT(iLevel < pTexDest->m_cLevels); pSurfaceSrc = this->m_prgMipSurfaces[iLevel + StartLevel]; pSurfaceDest = pTexDest->m_prgMipSurfaces[iLevel]; // Source and Dest should be the same // or our caller made a mistake DXGASSERT(pSurfaceSrc->InternalGetDesc().Width == pSurfaceDest->InternalGetDesc().Width); DXGASSERT(pSurfaceSrc->InternalGetDesc().Height == pSurfaceDest->InternalGetDesc().Height); // Copy the entire level hr = Device()->InternalCopyRects(pSurfaceSrc, NULL, 0, pSurfaceDest, NULL); if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state"); return hr; } } } else { DXGASSERT(m_cRectUsed > 0); DXGASSERT(m_cRectUsed <= MIPMAP_MAXDIRTYRECT); if (StartLevel) { // Figure out the right set of target rects for (DWORD i = 0; i < m_cRectUsed; i++) { ScaleRectDown(&m_DirtyRectArray[i], StartLevel); } } // Use the rects for the top level; but just // copy the entirety of other levels DXGASSERT(StartLevel < this->m_cLevels); DXGASSERT(0 < pTexDest->m_cLevels); pSurfaceSrc = this->m_prgMipSurfaces[StartLevel]; pSurfaceDest = pTexDest->m_prgMipSurfaces[0]; DXGASSERT(pSurfaceSrc->InternalGetDesc().Width == pSurfaceDest->InternalGetDesc().Width); DXGASSERT(pSurfaceSrc->InternalGetDesc().Height == pSurfaceDest->InternalGetDesc().Height); // Passing points as NULL means just do a non-translated // copy // CONSIDER: Maybe we should use the rects for copying the top // two levels.. hr = Device()->InternalCopyRects(pSurfaceSrc, m_DirtyRectArray, m_cRectUsed, pSurfaceDest, NULL); // pPoints if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state"); return hr; } // Copy each of the levels for (DWORD iLevel = 1; iLevel < LevelsToCopy; iLevel++) { DXGASSERT(iLevel + StartLevel < this->m_cLevels); DXGASSERT(iLevel < pTexDest->m_cLevels); // Get the next surfaces pSurfaceSrc = this->m_prgMipSurfaces[iLevel + StartLevel]; pSurfaceDest = pTexDest->m_prgMipSurfaces[iLevel]; // Check that sizes match DXGASSERT(pSurfaceSrc->InternalGetDesc().Width == pSurfaceDest->InternalGetDesc().Width); DXGASSERT(pSurfaceSrc->InternalGetDesc().Height == pSurfaceDest->InternalGetDesc().Height); // Copy the entirety of non-top levels hr = Device()->InternalCopyRects(pSurfaceSrc, NULL, 0, pSurfaceDest, NULL); if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state"); return hr; } } } } if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state"); return hr; } // Remember that we did the work m_cRectUsed = 0; // Notify Resource base class that we are now clean OnResourceClean(); DXGASSERT(!IsDirty()); return S_OK; } // CMipMap::UpdateDirtyPortion #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::MarkAllDirty" // Allows the Resource Manager to mark the texture // as needing to be completely updated on next // call to UpdateDirtyPortion void CMipMap::MarkAllDirty() { // Set palette to __INVALIDPALETTE so that UpdateTextures // calls the DDI SetPalette the next time. SetPalette(__INVALIDPALETTE); m_cRectUsed = MIPMAP_ALLDIRTY; // Notify Resource base class that we are now dirty OnResourceDirty(); return; } // CMipMap::MarkAllDirty #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::OnSurfaceLock" // Methods for the MipSurface to call // Notification when a mip-level is locked for writing void CMipMap::OnSurfaceLock(DWORD iLevel, CONST RECT *pRect, DWORD Flags) { // Sync first Sync(); // We only care about the top-most level of the mip-map // for dirty rect information if (iLevel != 0) { return; } // We don't need to mark the surface dirty if this was a // read-only lock; (this can happen for RT+Tex where we // need to sync even for read-only locks). if (Flags & D3DLOCK_READONLY) { return; } // Send dirty notification OnResourceDirty(); // Remember this dirty rect if (m_cRectUsed != MIPMAP_ALLDIRTY && !(Flags & D3DLOCK_NO_DIRTY_UPDATE)) { InternalAddDirtyRect(pRect); } // We're done now. return; } // CMipMap::OnSurfaceLock // AddDirtyRect Method #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::AddDirtyRect" STDMETHODIMP CMipMap::AddDirtyRect(CONST RECT *pRect) { API_ENTER(Device()); if (pRect != NULL && !VALID_PTR(pRect, sizeof(RECT))) { DPF_ERR("Invalid parameter to of IDirect3DTexture8::AddDirtyRect"); return D3DERR_INVALIDCALL; } if (pRect) { if (!CPixel::IsValidRect(Desc()->Format, Desc()->Width, Desc()->Height, pRect)) { DPF_ERR("AddDirtyRect for a Texture failed"); return D3DERR_INVALIDCALL; } } InternalAddDirtyRect(pRect); return S_OK; } // AddDirtyRect #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::InternalAddDirtyRect" // Internal version of AddDirtyRect: no crit-sec // or parameter checking void CMipMap::InternalAddDirtyRect(CONST RECT *pRect) { // If driver managed then batch token if (Desc()->Pool == D3DPOOL_MANAGED && !IsD3DManaged()) { RECTL Rect; DXGASSERT((Device()->GetD3DCaps()->Caps2 & DDCAPS2_CANMANAGERESOURCE) != 0); if (pRect == NULL) { Rect.left = 0; Rect.top = 0; Rect.right = (LONG)Desc()->Width; Rect.bottom = (LONG)Desc()->Height; } else { Rect = *((CONST RECTL*)pRect); } static_cast(Device())->AddDirtyRect(this, &Rect); // This will fail only due to catastrophic // error and we or the app can't do a // a whole lot about it, so return nothing return; } // Need to mark dirty bit in CResource so that the resource manager works correctly. OnResourceDirty(); // If everything is being modified; then we're totally dirty if (pRect == NULL) { m_cRectUsed = MIPMAP_ALLDIRTY; return; } // If we're all dirty, we can't get dirtier if (m_cRectUsed == MIPMAP_ALLDIRTY) { return; } // If the rect is the entire surface then we're all dirty DXGASSERT(pRect != NULL); if (pRect->left == 0 && pRect->top == 0 && pRect->right == (LONG)Desc()->Width && pRect->bottom == (LONG)Desc()->Height) { m_cRectUsed = MIPMAP_ALLDIRTY; return; } // If we have filled up our rects; then we're also all dirty now if (m_cRectUsed == MIPMAP_MAXDIRTYRECT) { m_cRectUsed = MIPMAP_ALLDIRTY; return; } // Remember this rect DXGASSERT(m_cRectUsed < MIPMAP_MAXDIRTYRECT); DXGASSERT(pRect != NULL); m_DirtyRectArray[m_cRectUsed] = *pRect; m_cRectUsed++; return; } // InternalAddDirtyRect #undef DPF_MODNAME #define DPF_MODNAME "CMipMap::IsTextureLocked" // Debug only parameter checking do determine if a piece // of a mip-chain is locked #ifdef DEBUG BOOL CMipMap::IsTextureLocked() { for (UINT iLevel = 0; iLevel < m_cLevels; iLevel++) { if (m_prgMipSurfaces[iLevel]->IsLocked()) return TRUE; } return FALSE; } // IsTextureLocked #endif // !DEBUG // End of file : mipmap.cpp