/*==========================================================================; * * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. * * File: cubemap.cpp * Content: Implementation of the CCubeMap class. * * ***************************************************************************/ #include "ddrawpr.h" #include "cubemap.hpp" #include "cubesurf.hpp" #include "d3di.hpp" #include "resource.inl" #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::Create" // Static class function for creating a cube-map object. // (Because it is static; it doesn't have a this pointer.) // // 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 CCubeMap::Create(CBaseDevice *pDevice, DWORD cpEdge, DWORD cLevels, DWORD Usage, D3DFORMAT UserFormat, D3DPOOL Pool, IDirect3DCubeTexture8 **ppCubeMap) { HRESULT hr; // Do parameter checking here if (!VALID_PTR_PTR(ppCubeMap)) { DPF_ERR("Bad parameter passed for ppSurface for creating a cubemap"); return D3DERR_INVALIDCALL; } // Zero-out return parameter *ppCubeMap = NULL; // Check if format is valid hr = Validate(pDevice, D3DRTYPE_CUBETEXTURE, Pool, Usage, UserFormat); if (FAILED(hr)) { // VerifyFormat 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_MIPCUBEMAP))) { // Can't mip so use 1 cLevels = 1; } else { // Determine number of levels cLevels = ComputeLevels(cpEdge); } } // Start parameter checking if (cLevels > 32) { DPF_ERR("No more than 32 levels are supported for a cubemap texture"); // This limitation is based on the number of // bits that we have allocated for iLevel in // some of the supporting classes. return D3DERR_INVALIDCALL; } // Check if the device supports mipped cubemaps if (cLevels > 1) { if ((cpEdge >> (cLevels - 1)) == 0) { DPF_ERR("Too many levels for Cube Texture of this size."); return D3DERR_INVALIDCALL; } } D3DFORMAT RealFormat = UserFormat; if (Pool != D3DPOOL_SCRATCH) { // Check size constraints for cubemap if (pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_CUBEMAP_POW2) { if (!IsPowerOfTwo(cpEdge)) { DPF_ERR("Device requires that edge must be power of two for cube-maps"); return D3DERR_INVALIDCALL; } } // Check texture size restrictions if (cpEdge > pDevice->GetD3DCaps()->MaxTextureWidth) { DPF_ERR("Texture width is larger than what the device supports. Cube Texture creation fails."); return D3DERR_INVALIDCALL; } if (cpEdge > pDevice->GetD3DCaps()->MaxTextureHeight) { DPF_ERR("Texture height is larger than what the device supports. Cube Texture creation fails."); return D3DERR_INVALIDCALL; } // Check that the device supports cubemaps if (!(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_CUBEMAP)) { DPF_ERR("Device doesn't support Cube Texture; Cube Texture creation failed."); return D3DERR_INVALIDCALL; } // Check if the device supports mipped cubemaps if (cLevels > 1) { if (!(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP)) { DPF_ERR("Device doesn't support mipped cube textures; creation 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 (cpEdge & 3) { DPF_ERR("DXT Formats require edge to be a multiples of 4. Cube Texture creation fails."); return D3DERR_INVALIDCALL; } } // Validate against zero width/height if (cpEdge == 0) { DPF_ERR("Edge must be non-zero. Cube Texture creation fails."); return D3DERR_INVALIDCALL; } // Allocate a new CubeMap object and return it CCubeMap *pCubeMap = new CCubeMap(pDevice, cpEdge, cLevels, Usage, UserFormat, RealFormat, Pool, REF_EXTERNAL, &hr); if (pCubeMap == NULL) { DPF_ERR("Out of Memory creating cubemap"); return E_OUTOFMEMORY; } if (FAILED(hr)) { DPF_ERR("Error during initialization of cubemap"); pCubeMap->ReleaseImpl(); return hr; } // We're done; just return the object *ppCubeMap = pCubeMap; return hr; } // static Create #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::CCubeMap" // Constructor the cube map class CCubeMap::CCubeMap(CBaseDevice *pDevice, DWORD cpEdge, DWORD cLevels, DWORD Usage, D3DFORMAT UserFormat, D3DFORMAT RealFormat, D3DPOOL UserPool, REF_TYPE refType, HRESULT *phr ) : CBaseTexture(pDevice, cLevels, UserPool, UserFormat, refType), m_prgCubeSurfaces(NULL), m_rgbPixels(NULL), m_IsAnyFaceDirty(TRUE) { // Sanity check DXGASSERT(phr); DXGASSERT(cLevels <= 32); // Initialize basic structures m_prgCubeSurfaces = NULL; m_rgbPixels = NULL; m_desc.Format = RealFormat; m_desc.Pool = UserPool; m_desc.Usage = Usage; m_desc.Type = D3DRTYPE_CUBETEXTURE; m_desc.MultiSampleType = D3DMULTISAMPLE_NONE; m_desc.Width = cpEdge; m_desc.Height = cpEdge; // Initialize ourselves to all dirty for (DWORD iFace = 0; iFace < CUBEMAP_MAXFACES; iFace++) { m_IsFaceCleanArray [iFace] = FALSE; m_IsFaceAllDirtyArray[iFace] = TRUE; } // We assume that we start out dirty DXGASSERT(IsDirty()); // We always have 6 faces now DWORD cFaces = 6; // Allocate Pixel Data m_cbSingleFace = CPixel::ComputeMipMapSize(cpEdge, cpEdge, cLevels, RealFormat); // Round up to nearest 32 for alignment m_cbSingleFace += 31; m_cbSingleFace &= ~(31); m_desc.Size = m_cbSingleFace * cFaces; // Allocate Pixel Data for SysMem or D3DManaged cases if (IS_D3D_ALLOCATED_POOL(UserPool) || IsTypeD3DManaged(Device(), D3DRTYPE_CUBETEXTURE, UserPool)) { m_rgbPixels = new BYTE[m_desc.Size]; if (m_rgbPixels == NULL) { *phr = E_OUTOFMEMORY; return; } } // Create the DDSURFACEINFO array and CreateSurfaceData object DXGASSERT(cLevels <= 32); DDSURFACEINFO SurfInfo[6 * 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 * cFaces; CreateSurfaceData.Type = D3DRTYPE_CUBETEXTURE; CreateSurfaceData.dwUsage = m_desc.Usage; CreateSurfaceData.Format = RealFormat; CreateSurfaceData.MultiSampleType = D3DMULTISAMPLE_NONE; CreateSurfaceData.Pool = DetermineCreationPool(Device(), D3DRTYPE_CUBETEXTURE, Usage, UserPool); // Iterate of each face/level to create the individual level // data for (iFace = 0; iFace < cFaces; iFace++) { // Start width and height at the full size cpEdge = m_desc.Width; DXGASSERT(m_desc.Width == m_desc.Height); for (DWORD iLevel = 0; iLevel < cLevels; iLevel++) { int index = (iFace * cLevels) + iLevel; // Fill in the relevant information DXGASSERT(cpEdge >= 1); SurfInfo[index].cpWidth = cpEdge; SurfInfo[index].cpHeight = cpEdge; // If we allocated the memory, pass down // the sys-mem pointers if (m_rgbPixels) { D3DLOCKED_RECT lock; ComputeCubeMapOffset(iFace, iLevel, NULL, // pRect &lock); SurfInfo[index].pbPixels = (BYTE*)lock.pBits; SurfInfo[index].iPitch = lock.Pitch; } // Scale width and height down for each level cpEdge >>= 1; } } // Allocate array of pointers to CubeSurfaces m_prgCubeSurfaces = new CCubeSurface*[cLevels*cFaces]; if (m_prgCubeSurfaces == NULL) { *phr = E_OUTOFMEMORY; return; } // Zero the memory for safe cleanup ZeroMemory(m_prgCubeSurfaces, sizeof(*m_prgCubeSurfaces) * cLevels * cFaces); 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 CubeLevel for (iFace = 0; iFace < cFaces; iFace++) { for (DWORD iLevel = 0; iLevel < cLevels; iLevel++) { int index = (iFace * cLevels) + iLevel; DXGASSERT((BYTE)iFace == iFace); DXGASSERT((BYTE)iLevel == iLevel); // Create the appropriate cube-level depending on type // Is this a sys-mem surface; could be d3d managed if (IS_D3D_ALLOCATED_POOL(m_desc.Pool)) { m_prgCubeSurfaces[index] = new CCubeSurface(this, (BYTE)iFace, (BYTE)iLevel, SurfInfo[index].hKernelHandle); } else { // This is driver kind of cube-map; could be driver managed m_prgCubeSurfaces[index] = new CDriverCubeSurface(this, (BYTE)iFace, (BYTE)iLevel, SurfInfo[index].hKernelHandle); } if (m_prgCubeSurfaces[index] == NULL) { DPF_ERR("Out of memory creating cube map level"); *phr = E_OUTOFMEMORY; // Need to free handles that we got before we return; we // only free the ones that weren't successfully entrusted // to a CCubeSurf because those will be cleaned up automatically // at their destructor if (UserPool != D3DPOOL_SCRATCH) { for (UINT i = index; i < ((cFaces * cLevels) - 1); 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 surface 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_CUBETEXTURE, UserPool)) { *phr = InitializeRMHandle(); } } // CCubeMap::CCubeMap #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::~CCubeMap" // Destructor CCubeMap::~CCubeMap() { // The destructor has to handle partially // created objects. if (m_prgCubeSurfaces) { // How many faces do we have? DWORD cFaces = 6; // Delete each CubeSurface individually for (DWORD iSurf = 0; iSurf < (cFaces * m_cLevels); iSurf++) { delete m_prgCubeSurfaces[iSurf]; } delete [] m_prgCubeSurfaces; } delete [] m_rgbPixels; } // CCubeMap::~CCubeMap // Methods for the Resource Manager #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::Clone" // Specifies a creation of a resource that // looks just like the current one; in a new POOL // with a new LOD. HRESULT CCubeMap::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 Edge = m_desc.Width; DXGASSERT(m_desc.Width == 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 Edge >>= dwLOD; if (Edge == 0) Edge = 1; // Reduce the number based on the our max lod. cLevels -= dwLOD; } // Sanity checking DXGASSERT(cLevels >= 1); DXGASSERT(Edge > 0); // Create the new cube-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 CCubeMap(Device(), Edge, cLevels, Usage, m_desc.Format, // UserFormat m_desc.Format, // RealFormat Pool, REF_INTERNAL, &hr); if (pResource == NULL) { DPF_ERR("Failed to allocate cube-map object when copying"); return E_OUTOFMEMORY; } if (FAILED(hr)) { DPF(5, "Failed to create cube-map when doing texture management"); pResource->DecrementUseCount(); return hr; } *ppResource = pResource; return hr; } // CCubeMap::Clone #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::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* CCubeMap::GetBufferDesc() const { return (const D3DBUFFER_DESC*)&m_desc; } // CCubeMap::GetBufferDesc // IUnknown methods #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::QueryInterface" STDMETHODIMP CCubeMap::QueryInterface(REFIID riid, LPVOID FAR * ppvObj) { API_ENTER(Device()); if (!VALID_PTR_PTR(ppvObj)) { DPF_ERR("Invalid ppvObj parameter to QueryInterface for Cubemap"); return D3DERR_INVALIDCALL; } if (!VALID_PTR(&riid, sizeof(GUID))) { DPF_ERR("Invalid guid memory address to QueryInterface for Cubemap"); return D3DERR_INVALIDCALL; } if (riid == IID_IDirect3DCubeTexture8 || 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 QueryInterface for Cubemap"); // Null out param *ppvObj = NULL; return E_NOINTERFACE; } // QueryInterface #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::AddRef" STDMETHODIMP_(ULONG) CCubeMap::AddRef() { API_ENTER_NO_LOCK(Device()); return AddRefImpl(); } // AddRef #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::Release" STDMETHODIMP_(ULONG) CCubeMap::Release() { API_ENTER_SUBOBJECT_RELEASE(Device()); return ReleaseImpl(); } // Release // IDirect3DResource methods #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::GetDevice" STDMETHODIMP CCubeMap::GetDevice(IDirect3DDevice8 ** ppvObj) { API_ENTER(Device()); return GetDeviceImpl(ppvObj); } // GetDevice #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::SetPrivateData" STDMETHODIMP CCubeMap::SetPrivateData(REFGUID riid, CONST VOID* pvData, DWORD cbData, DWORD dwFlags) { API_ENTER(Device()); // For the private data that 'really' belongs to the // CubeMap, 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 "CCubeMap::GetPrivateData" STDMETHODIMP CCubeMap::GetPrivateData(REFGUID riid, LPVOID pvData, LPDWORD pcbData) { API_ENTER(Device()); // For the private data that 'really' belongs to the // CubeMap, 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 "CCubeMap::FreePrivateData" STDMETHODIMP CCubeMap::FreePrivateData(REFGUID riid) { API_ENTER(Device()); // For the private data that 'really' belongs to the // CubeMap, 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 "CCubeMap::GetPriority" STDMETHODIMP_(DWORD) CCubeMap::GetPriority() { API_ENTER_RET(Device(), DWORD); return GetPriorityImpl(); } // GetPriority #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::SetPriority" STDMETHODIMP_(DWORD) CCubeMap::SetPriority(DWORD dwPriority) { API_ENTER_RET(Device(), DWORD); return SetPriorityImpl(dwPriority); } // SetPriority #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::PreLoad" STDMETHODIMP_(void) CCubeMap::PreLoad(void) { API_ENTER_VOID(Device()); PreLoadImpl(); return; } // PreLoad #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::GetType" STDMETHODIMP_(D3DRESOURCETYPE) CCubeMap::GetType(void) { API_ENTER_RET(Device(), D3DRESOURCETYPE); return m_desc.Type; } // GetType // IDirect3DMipTexture methods #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::GetLOD" STDMETHODIMP_(DWORD) CCubeMap::GetLOD() { API_ENTER_RET(Device(), DWORD); return GetLODImpl(); } // GetLOD #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::SetLOD" STDMETHODIMP_(DWORD) CCubeMap::SetLOD(DWORD dwLOD) { API_ENTER_RET(Device(), DWORD); return SetLODImpl(dwLOD); } // SetLOD #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::GetLevelCount" STDMETHODIMP_(DWORD) CCubeMap::GetLevelCount() { API_ENTER_RET(Device(), DWORD); return GetLevelCountImpl(); } // GetLevelCount // IDirect3DCubeMap methods #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::GetLevelDesc" STDMETHODIMP CCubeMap::GetLevelDesc(UINT iLevel, D3DSURFACE_DESC *pDesc) { API_ENTER(Device()); if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed CCubeMap::GetLevelDesc"); return D3DERR_INVALIDCALL; } D3DCUBEMAP_FACES FaceType = D3DCUBEMAP_FACE_POSITIVE_X; return GetSurface(FaceType, iLevel)->GetDesc(pDesc); } // GetLevelDesc #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::GetCubeMapSurface" STDMETHODIMP CCubeMap::GetCubeMapSurface(D3DCUBEMAP_FACES FaceType, UINT iLevel, IDirect3DSurface8 **ppSurface) { API_ENTER(Device()); if (!VALID_PTR_PTR(ppSurface)) { DPF_ERR("Invalid ppSurface parameter passed to CCubeMap::GetCubeMapSurface"); return D3DERR_INVALIDCALL; } // Null out parameter *ppSurface = NULL; // Continue parameter checking if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed CCubeMap::OpenCubemapLevel"); return D3DERR_INVALIDCALL; } if (!VALID_CUBEMAP_FACETYPE(FaceType)) { DPF_ERR("Invalid face type passed CCubeMap::OpenCubemapLevel"); return D3DERR_INVALIDCALL; } // Count bits in dwAllFaces less than dwFaceType's bit *ppSurface = GetSurface(FaceType, iLevel); (*ppSurface)->AddRef(); return S_OK; } // GetCubeMapSurface #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::LockRect" STDMETHODIMP CCubeMap::LockRect(D3DCUBEMAP_FACES FaceType, UINT iLevel, D3DLOCKED_RECT *pLockedRectData, CONST RECT *pRect, DWORD dwFlags) { API_ENTER(Device()); if (pLockedRectData == NULL) { DPF_ERR("Invalid parameter passed to CCubeMap::LockRect"); return D3DERR_INVALIDCALL; } if (!VALID_CUBEMAP_FACETYPE(FaceType)) { DPF_ERR("Invalid face type passed CCubeMap::LockRect"); return D3DERR_INVALIDCALL; } if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed CCubeMap::LockRect"); return D3DERR_INVALIDCALL; } return GetSurface(FaceType, iLevel)->LockRect(pLockedRectData, pRect, dwFlags); } // LockRect #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::UnlockRect" STDMETHODIMP CCubeMap::UnlockRect(D3DCUBEMAP_FACES FaceType, UINT iLevel) { API_ENTER(Device()); if (!VALID_CUBEMAP_FACETYPE(FaceType)) { DPF_ERR("Invalid face type passed CCubeMap::UnlockRect"); return D3DERR_INVALIDCALL; } if (iLevel >= m_cLevels) { DPF_ERR("Invalid level number passed CCubeMap::UnlockRect"); return D3DERR_INVALIDCALL; } return GetSurface(FaceType, iLevel)->UnlockRect(); } // UnlockRect #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::UpdateTexture" // This function does type-specific parameter checking // before calling UpdateDirtyPortion HRESULT CCubeMap::UpdateTexture(CBaseTexture *pResourceTarget) { CCubeMap *pTexSource = static_cast(this); CCubeMap *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" " levels; their widths must match. UpdateTexture" " for CubeTexture fails"); } else { DPF_ERR("Source and Destination for UpdateTexture are not" " compatible. Since they have the different numbers of" " levels; the widths of the bottom-most levels of" " the source must match all the corresponding levels" " of the destination. UpdateTexture" " for CubeTexture fails"); } 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" " levels; their heights must match. UpdateTexture" " for CubeTexture fails"); } 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. UpdateTexture" " for CubeTexture fails"); } return D3DERR_INVALIDCALL; } return UpdateDirtyPortion(pResourceTarget); } // UpdateTexture #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::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 CCubeMap::UpdateDirtyPortion(CResource *pResourceTarget) { // If we are clean, then do nothing if (!m_IsAnyFaceDirty) { if (IsDirty()) { DPF_ERR("A Cube 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 CCubeMap *pTexSource = static_cast(this); CCubeMap *pTexDest = static_cast(pResourceTarget); // Call TexBlt for each face HRESULT hr = S_OK; if (CanTexBlt(pTexDest)) { CD3DBase *pDevice = static_cast(Device()); // Hack: go in reverse order for driver compat. for (INT iFace = CUBEMAP_MAXFACES-1; iFace >= 0; iFace--) { // Skip clean faces if (m_IsFaceCleanArray[iFace]) continue; // Figure out the right handles to use for this operation D3DCUBEMAP_FACES Face = (D3DCUBEMAP_FACES) iFace; DWORD dwDest = pTexDest->GetSurface(Face, 0 /* iLevel */)->DrawPrimHandle(); DWORD dwSource = pTexSource->GetSurface(Face, 0 /* iLevel */)->DrawPrimHandle(); // Is this face all dirty? if (m_IsFaceAllDirtyArray[iFace]) { POINT p = {0 , 0}; RECTL r = {0, 0, Desc()->Width, Desc()->Height}; hr = pDevice->CubeTexBlt(pTexDest, pTexSource, dwDest, dwSource, &p, &r); } else { // this face must be dirty DXGASSERT(!m_IsFaceCleanArray[iFace]); // Is this face partially dirty hr = pDevice->CubeTexBlt(pTexDest, pTexSource, dwDest, dwSource, (LPPOINT)&m_DirtyRectArray[iFace], (LPRECTL)&m_DirtyRectArray[iFace]); } if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state for Cubemap"); 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; DXGASSERT(StartLevel < this->m_cLevels); DXGASSERT(0 < pTexDest->m_cLevels); CBaseSurface *pSurfaceSrc; CBaseSurface *pSurfaceDest; // Iterate over each face for (DWORD iFace = 0; iFace < 6; iFace++) { if (m_IsFaceCleanArray[iFace]) continue; if (m_IsFaceAllDirtyArray[iFace]) { for (DWORD iLevel = 0; iLevel < LevelsToCopy; iLevel++) { DWORD IndexSrc = iFace * this->m_cLevels + iLevel + StartLevel; DXGASSERT(IndexSrc < (DWORD)(this->m_cLevels * 6)); pSurfaceSrc = this->m_prgCubeSurfaces[IndexSrc]; DWORD IndexDest = iFace * pTexDest->m_cLevels + iLevel; DXGASSERT(IndexDest < (DWORD)(pTexDest->m_cLevels * 6)); pSurfaceDest = pTexDest->m_prgCubeSurfaces[IndexDest]; // 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 for Cubemap"); return hr; } } } else { if (StartLevel) { ScaleRectDown(&m_DirtyRectArray[iFace], StartLevel); } // Use the rect for the top level; but just // copy the entirety of other levels DWORD iLevel = 0; DWORD IndexSrc = iFace * this->m_cLevels + iLevel + StartLevel; DXGASSERT(IndexSrc < (DWORD)(this->m_cLevels * 6)); pSurfaceSrc = this->m_prgCubeSurfaces[IndexSrc]; DWORD IndexDest = iFace * pTexDest->m_cLevels + iLevel; DXGASSERT(IndexDest < (DWORD)(pTexDest->m_cLevels * 6)); pSurfaceDest = pTexDest->m_prgCubeSurfaces[IndexDest]; DXGASSERT(pSurfaceSrc->InternalGetDesc().Width == pSurfaceDest->InternalGetDesc().Width); DXGASSERT(pSurfaceSrc->InternalGetDesc().Height == pSurfaceDest->InternalGetDesc().Height); // Passing pPoints as NULL means just do a non-translated // copy hr = Device()->InternalCopyRects(pSurfaceSrc, &m_DirtyRectArray[iFace], 1, pSurfaceDest, NULL); // pPoints if (FAILED(hr)) { DPF_ERR("Failed to update texture; not clearing dirty state for Cubemap"); return hr; } // Copy each of the levels for (iLevel = 1; iLevel < LevelsToCopy; iLevel++) { // Get the next surfaces DWORD IndexSrc = iFace * this->m_cLevels + iLevel + StartLevel; DXGASSERT(IndexSrc < (DWORD)(this->m_cLevels * 6)); pSurfaceSrc = this->m_prgCubeSurfaces[IndexSrc]; DWORD IndexDest = iFace * pTexDest->m_cLevels + iLevel; DXGASSERT(IndexDest < (DWORD)(pTexDest->m_cLevels * 6)); pSurfaceDest = pTexDest->m_prgCubeSurfaces[IndexDest]; // 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 for Cubemap"); return hr; } } } } } // Remember that we did the work m_IsAnyFaceDirty = FALSE; for (DWORD iFace = 0; iFace < CUBEMAP_MAXFACES; iFace++) { m_IsFaceCleanArray [iFace] = TRUE; m_IsFaceAllDirtyArray[iFace] = FALSE; } // Notify Resource base class that we are now clean OnResourceClean(); DXGASSERT(!IsDirty()); return S_OK; } // CCubeMap::UpdateDirtyPortion #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::MarkAllDirty" // Allows the Resource Manager to mark the texture // as needing to be completely updated on next // call to UpdateDirtyPortion void CCubeMap::MarkAllDirty() { // Set palette to __INVALIDPALETTE so that UpdateTextures // calls the DDI SetPalette the next time. SetPalette(__INVALIDPALETTE); // Mark everything dirty m_IsAnyFaceDirty = TRUE; for (int iFace = 0; iFace < CUBEMAP_MAXFACES; iFace++) { m_IsFaceCleanArray [iFace] = FALSE; m_IsFaceAllDirtyArray[iFace] = TRUE; } // Notify Resource base class that we are now dirty OnResourceDirty(); } // CCubeMap::MarkAllDirty #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::AddDirtyRect" STDMETHODIMP CCubeMap::AddDirtyRect(D3DCUBEMAP_FACES FaceType, CONST RECT *pRect) { API_ENTER(Device()); if (pRect != NULL && !VALID_PTR(pRect, sizeof(RECT))) { DPF_ERR("Invalid Rect parameter to AddDirtyRect for Cubemap"); return D3DERR_INVALIDCALL; } if (!VALID_CUBEMAP_FACETYPE(FaceType)) { DPF_ERR("Invalid FaceType parameter to AddDirtyRect for Cubemap"); return D3DERR_INVALIDCALL; } if (pRect) { if (!CPixel::IsValidRect(Desc()->Format, Desc()->Width, Desc()->Height, pRect)) { DPF_ERR("AddDirtyRect for a Cube Texture failed"); return D3DERR_INVALIDCALL; } } InternalAddDirtyRect((UINT)FaceType, pRect); return S_OK; } // AddDirtyRect #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::InternalAddDirtyRect" void CCubeMap::InternalAddDirtyRect(DWORD iFace, 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())->AddCubeDirtyRect(this, GetSurface((D3DCUBEMAP_FACES)iFace, 0)->DrawPrimHandle(), &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_IsFaceAllDirtyArray[iFace] = TRUE; m_IsFaceCleanArray [iFace] = FALSE; m_IsAnyFaceDirty = TRUE; return; } // If we're all dirty, we can't get dirtier if (m_IsFaceAllDirtyArray[iFace]) { 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_IsFaceAllDirtyArray[iFace] = TRUE; m_IsFaceCleanArray [iFace] = FALSE; m_IsAnyFaceDirty = TRUE; return; } // If the face is currently clean; then just remember the // new rect if (m_IsFaceCleanArray[iFace]) { m_DirtyRectArray [iFace] = *pRect; m_IsFaceCleanArray[iFace] = FALSE; m_IsAnyFaceDirty = TRUE; return; } // Union in this Rect // If we're unioning in rects, then we must // already be marked dirty but not all dirty DXGASSERT(!m_IsFaceAllDirtyArray[iFace]); DXGASSERT(m_IsAnyFaceDirty); if (m_DirtyRectArray[iFace].left > pRect->left) { m_DirtyRectArray[iFace].left = pRect->left; } if (m_DirtyRectArray[iFace].right < pRect->right) { m_DirtyRectArray[iFace].right = pRect->right; } if (m_DirtyRectArray[iFace].top > pRect->top) { m_DirtyRectArray[iFace].top = pRect->top; } if (m_DirtyRectArray[iFace].bottom < pRect->bottom) { m_DirtyRectArray[iFace].bottom = pRect->bottom; } return; } // InternalAddDirtyRect #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::OnSurfaceLock" // Methods for the CubeSurface to call // Notification when a cube-surface is locked for writing void CCubeMap::OnSurfaceLock(DWORD iFace, DWORD iLevel, CONST RECT *pRect, DWORD dwFlags) { // Need to Sync first Sync(); // We only care about the top-most levels of the cube-map 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 (dwFlags & D3DLOCK_READONLY) { return; } // Notify the resource that we are dirty OnResourceDirty(); // Don't do anything if we are already all dirty or // if the app has specified that we shouldn't keep // track of this rect if (!m_IsFaceAllDirtyArray[iFace] && !(dwFlags & D3DLOCK_NO_DIRTY_UPDATE)) { InternalAddDirtyRect(iFace, pRect); } // We're done now. return; } // OnSurfaceLock #undef DPF_MODNAME #define DPF_MODNAME "CCubeMap::IsTextureLocked" // Debug only parameter checking do determine if a piece // of a mip-chain is locked #ifdef DEBUG BOOL CCubeMap::IsTextureLocked() { for (DWORD iFace = 0; iFace < 6; iFace++) { for (UINT iLevel = 0; iLevel < m_cLevels; iLevel++) { D3DCUBEMAP_FACES Face = (D3DCUBEMAP_FACES) iFace; if (GetSurface(Face, iLevel)->IsLocked()) return TRUE; } } return FALSE; } // IsTextureLocked #endif // !DEBUG // End of file : cubemap.cpp