Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1377 lines
42 KiB

/*==========================================================================;
*
* 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<void*>(static_cast<IDirect3DCubeTexture8*>(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<CCubeMap*>(this);
CCubeMap *pTexDest = static_cast<CCubeMap*>(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<CCubeMap*>(this);
CCubeMap *pTexDest = static_cast<CCubeMap*>(pResourceTarget);
// Call TexBlt for each face
HRESULT hr = S_OK;
if (CanTexBlt(pTexDest))
{
CD3DBase *pDevice = static_cast<CD3DBase*>(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<CD3DBase*>(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