windows-nt/Source/XPSP1/NT/multimedia/directx/dxg/d3d8/fw/mipmap.cpp

1304 lines
38 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*==========================================================================;
*
* 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<void*>(static_cast<IDirect3DTexture8 *>(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<CMipMap*>(this);
CMipMap *pTexDest = static_cast<CMipMap*>(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<CMipMap*>(this);
CMipMap *pTexDest = static_cast<CMipMap*>(pResourceTarget);
if (CanTexBlt(pTexDest))
{
if (m_cRectUsed == MIPMAP_ALLDIRTY)
{
POINT p = {0, 0};
RECTL r = {0, 0, Desc()->Width, Desc()->Height};
hr = static_cast<CD3DBase*>(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<CD3DBase*>(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<CD3DBase*>(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