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.
 
 
 
 
 
 

635 lines
18 KiB

/*==========================================================================
*
* Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
*
* File: volume.cpp
* Content: Implementation of the CVolume and CDriverVolumne classes
*
*
***************************************************************************/
#include "ddrawpr.h"
#include "volume.hpp"
// IUnknown methods
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::QueryInterface"
STDMETHODIMP CVolume::QueryInterface (REFIID riid,
VOID **ppvObj)
{
API_ENTER(Device());
if (!VALID_PTR_PTR(ppvObj))
{
DPF_ERR("Invalid ppvObj parameter to QueryInterface for a level of a VolumeTexture");
return D3DERR_INVALIDCALL;
}
if (!VALID_PTR(&riid, sizeof(GUID)))
{
DPF_ERR("Invalid guid memory address to QueryInterface for a level of a VolumeTexture");
return D3DERR_INVALIDCALL;
}
if (riid == IID_IDirect3DVolume8 ||
riid == IID_IUnknown)
{
*ppvObj = static_cast<void*>(static_cast<IDirect3DVolume8 *>(this));
AddRef();
return S_OK;
}
DPF_ERR("Unsupported Interface identifier passed to QueryInterface for a level of a VolumeTexture");
// Null out param
*ppvObj = NULL;
return E_NOINTERFACE;
} // QueryInterface
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::AddRef"
STDMETHODIMP_(ULONG) CVolume::AddRef()
{
API_ENTER_NO_LOCK(Device());
#ifdef DEBUG
m_cRefDebug++;
#endif // DEBUG
return m_pParent->AddRefImpl();
} // AddRef
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::Release"
STDMETHODIMP_(ULONG) CVolume::Release()
{
API_ENTER_SUBOBJECT_RELEASE(Device());
#ifdef DEBUG
m_cRefDebug--;
if (m_cRefDebug & 0x80000000)
{
DPF_ERR("A level of a mip-volume has been released more often than it has been add-ref'ed! Danger!!");
}
#endif // DEBUG
return m_pParent->ReleaseImpl();
} // Release
// IBuffer methods
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::SetPrivateData"
STDMETHODIMP CVolume::SetPrivateData(REFGUID riid,
CONST VOID *pvData,
DWORD cbData,
DWORD dwFlags)
{
API_ENTER(Device());
return m_pParent->SetPrivateDataImpl(riid,
pvData,
cbData,
dwFlags,
m_iLevel);
} // SetPrivateData
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::GetPrivateData"
STDMETHODIMP CVolume::GetPrivateData(REFGUID riid,
VOID *pvData,
DWORD *pcbData)
{
API_ENTER(Device());
return m_pParent->GetPrivateDataImpl(riid,
pvData,
pcbData,
m_iLevel);
} // GetPrivateData
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::FreePrivateData"
STDMETHODIMP CVolume::FreePrivateData(REFGUID riid)
{
API_ENTER(Device());
return m_pParent->FreePrivateDataImpl(riid,
m_iLevel);
} // FreePrivateData
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::GetContainer"
STDMETHODIMP CVolume::GetContainer(REFIID riid,
void **ppContainer)
{
API_ENTER(Device());
return m_pParent->QueryInterface(riid, ppContainer);
} // OpenContainer
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::GetDevice"
STDMETHODIMP CVolume::GetDevice(IDirect3DDevice8 **ppDevice)
{
API_ENTER(Device());
return m_pParent->GetDevice(ppDevice);
} // OpenDevice
// IDirect3DVolume8 methods
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::GetDesc"
STDMETHODIMP CVolume::GetDesc(D3DVOLUME_DESC *pDesc)
{
API_ENTER(Device());
// If parameters are bad, then we should fail some stuff
if (!VALID_WRITEPTR(pDesc, sizeof(D3DVOLUME_DESC)))
{
DPF_ERR("bad pointer for pDesc passed to GetDesc for a level of a VolumeTexture");
return D3DERR_INVALIDCALL;
}
// We basically get our volume desc from our parent
// and then modify the width, height, and depth fields.
*pDesc = *m_pParent->Desc();
pDesc->Width >>= m_iLevel;
pDesc->Height >>= m_iLevel;
pDesc->Depth >>= m_iLevel;
if (pDesc->Width == 0)
{
pDesc->Width = 1;
}
if (pDesc->Height == 0)
{
pDesc->Height = 1;
}
if (pDesc->Depth == 0)
{
pDesc->Depth = 1;
}
// Also need to modify the type field
pDesc->Type = D3DRTYPE_VOLUME;
// Also modify the size field
pDesc->Size = CPixel::ComputeVolumeSize(pDesc->Width,
pDesc->Height,
pDesc->Depth,
pDesc->Format);
// We also need to modify the pool and format
// to reflect the data the user passed to us
pDesc->Pool = m_pParent->GetUserPool();
pDesc->Format = m_pParent->GetUserFormat();
pDesc->Usage &= D3DUSAGE_EXTERNAL;
// We're done
return S_OK;
} // GetDesc
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::LockBox"
STDMETHODIMP CVolume::LockBox(D3DLOCKED_BOX *pLockedBoxData,
CONST D3DBOX *pBox,
DWORD dwFlags)
{
API_ENTER(Device());
// If parameters are bad, then we should fail some stuff
if (!VALID_WRITEPTR(pLockedBoxData, sizeof(D3DLOCKED_BOX)))
{
DPF_ERR("bad pointer for pLockedBoxData passed to LockBox for a level of a VolumeTexture");
return D3DERR_INVALIDCALL;
}
// Zero out returned data
ZeroMemory(pLockedBoxData, sizeof(D3DLOCKED_BOX));
// Validate Box
if (pBox != NULL)
{
DWORD Width = m_pParent->Desc()->Width >> m_iLevel;
DWORD Height = m_pParent->Desc()->Height >> m_iLevel;
DWORD Depth = m_pParent->Desc()->Depth >> m_iLevel;
if (!CPixel::IsValidBox(m_pParent->Desc()->Format,
Width,
Height,
Depth,
pBox))
{
DPF_ERR("LockBox for a Volume fails");
return D3DERR_INVALIDCALL;
}
}
if (dwFlags & ~D3DLOCK_VOL_VALID)
{
if (dwFlags & D3DLOCK_DISCARD)
{
if (dwFlags & D3DLOCK_READONLY)
{
DPF_ERR("D3DLOCK_READONLY is not allowed with D3DLOCK_DISCARD");
return D3DERR_INVALIDCALL;
}
if (!(m_pParent->Desc()->Usage & D3DUSAGE_DYNAMIC))
{
DPF_ERR("D3DLOCK_DISCARD is allowed only with dynamic textures");
return D3DERR_INVALIDCALL;
}
if (m_iLevel > 0)
{
DPF_ERR("D3DLOCK_DISCARD is allowed only on level 0"
" (the top mip level). DISCARD in this case will discard"
" the entire volume.");
return D3DERR_INVALIDCALL;
}
if (pBox != NULL)
{
DPF_ERR("Subboxes not allowed with D3DLOCK_DISCARD");
return D3DERR_INVALIDCALL;
}
}
else
{
DPF_ERR("Invalid dwFlags parameter passed to LockBox for a level of a VolumeTexture");
DPF_EXPLAIN_BAD_LOCK_FLAGS(0, dwFlags & ~D3DLOCK_VOL_VALID);
return D3DERR_INVALIDCALL;
}
}
if (!m_isLockable)
{
m_pParent->ReportWhyLockFailed();
return D3DERR_INVALIDCALL;
}
return InternalLockBox(pLockedBoxData, pBox, dwFlags);
} // LockBox
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::InternalLockBox"
HRESULT CVolume::InternalLockBox(D3DLOCKED_BOX *pLockedBoxData,
CONST D3DBOX *pBox,
DWORD dwFlags)
{
// Only one lock outstanding at a time is supported
if (IsLocked())
{
DPF_ERR("LockBox failed on a mip level; volume was already locked.");
return D3DERR_INVALIDCALL;
}
// Notify the parent/device if we are about to be modified
if ( (m_pParent->GetUserPool() != D3DPOOL_SCRATCH) && (!(dwFlags & D3DLOCK_READONLY)) )
{
m_pParent->OnVolumeLock(m_iLevel, pBox, dwFlags);
}
// Figure out our stride/pointer to bits
m_pParent->ComputeMipVolumeOffset(m_iLevel,
pBox,
pLockedBoxData);
// Mark ourselves as locked
m_isLocked = TRUE;
// Done
return S_OK;
} // InternalLockBox
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::UnlockBox"
STDMETHODIMP CVolume::UnlockBox()
{
API_ENTER(Device());
// If we aren't locked; then something is wrong
if (!IsLocked())
{
DPF_ERR("UnlockBox failed on a volume level; volume wasn't locked.");
return D3DERR_INVALIDCALL;
}
DXGASSERT(m_isLockable);
return InternalUnlockBox();
} // UnlockBox
#undef DPF_MODNAME
#define DPF_MODNAME "CVolume::InternalUnlockBox"
HRESULT CVolume::InternalUnlockBox()
{
// Clear our locked state
m_isLocked = FALSE;
// If we are lock-once; then we mark ourselves as not lockable
if (m_pParent->Desc()->Usage & D3DUSAGE_LOADONCE)
{
m_isLockable = FALSE;
}
// Done
return S_OK;
} // InternalUnlockBox
//
// CDriverVolume class modifies the implementation
// of the LockBox and UnlockBox methods of the CVolume class
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDriverVolume::LockBox"
STDMETHODIMP CDriverVolume::LockBox(D3DLOCKED_BOX *pLockedBoxData,
CONST D3DBOX *pBox,
DWORD dwFlags)
{
API_ENTER(Device());
// If parameters are bad, then we should fail some stuff
if (!VALID_WRITEPTR(pLockedBoxData, sizeof(D3DLOCKED_BOX)))
{
DPF_ERR("bad pointer for pLockedBoxData passed to LockBox for a level of a VolumeTexture");
return D3DERR_INVALIDCALL;
}
// Zero out returned data
ZeroMemory(pLockedBoxData, sizeof(D3DLOCKED_BOX));
// Validate Box
if (pBox != NULL)
{
DWORD Width = m_pParent->Desc()->Width >> m_iLevel;
DWORD Height = m_pParent->Desc()->Height >> m_iLevel;
DWORD Depth = m_pParent->Desc()->Depth >> m_iLevel;
if (!CPixel::IsValidBox(m_pParent->Desc()->Format,
Width,
Height,
Depth,
pBox))
{
DPF_ERR("LockBox for a Volume fails");
return D3DERR_INVALIDCALL;
}
}
if (dwFlags & ~D3DLOCK_VOL_VALID)
{
if (dwFlags & D3DLOCK_DISCARD)
{
if (dwFlags & D3DLOCK_READONLY)
{
DPF_ERR("D3DLOCK_READONLY is not allowed with D3DLOCK_DISCARD");
return D3DERR_INVALIDCALL;
}
if (!(m_pParent->Desc()->Usage & D3DUSAGE_DYNAMIC))
{
DPF_ERR("D3DLOCK_DISCARD is allowed only with dynamic textures");
return D3DERR_INVALIDCALL;
}
if (m_iLevel > 0)
{
DPF_ERR("D3DLOCK_DISCARD is allowed only on level 0"
" (the top mip level). DISCARD in this case will discard"
" the entire volume.");
return D3DERR_INVALIDCALL;
}
if (pBox != NULL)
{
DPF_ERR("Subboxes not allowed with D3DLOCK_DISCARD");
return D3DERR_INVALIDCALL;
}
}
else
{
DPF_ERR("Invalid dwFlags parameter passed to LockBox for a level of a VolumeTexture");
DPF_EXPLAIN_BAD_LOCK_FLAGS(0, dwFlags & ~D3DLOCK_VOL_VALID);
return D3DERR_INVALIDCALL;
}
}
if (!m_isLockable)
{
m_pParent->ReportWhyLockFailed();
return D3DERR_INVALIDCALL;
}
return InternalLockBox(pLockedBoxData, pBox, dwFlags);
} // CDriverVolume::LockBox
#undef DPF_MODNAME
#define DPF_MODNAME "CDriverVolume::InternalLockBox"
HRESULT CDriverVolume::InternalLockBox(D3DLOCKED_BOX *pLockedBoxData,
CONST D3DBOX *pBox,
DWORD dwFlags)
{
// Only one lock outstanding at a time is supported
if (IsLocked())
{
DPF_ERR("LockBox failed on a volume level; volume was already locked.");
return D3DERR_INVALIDCALL;
}
// Notify the parent/device if we are about to be accessed.
// Driver volume textures may be written to by HW through
// UpdateTexture. So we may need to sync with the current
// command batch.
m_pParent->OnVolumeLock(m_iLevel, pBox, dwFlags);
// Prepare a LockData structure for the HAL call
D3D8_LOCKDATA lockData;
ZeroMemory(&lockData, sizeof lockData);
lockData.hDD = m_pParent->Device()->GetHandle();
lockData.hSurface = m_hKernelHandle;
lockData.dwFlags = dwFlags;
if (pBox != NULL)
{
lockData.bHasBox = TRUE;
lockData.box = *pBox;
}
HRESULT hr = m_pParent->Device()->GetHalCallbacks()->Lock(&lockData);
if (FAILED(hr))
{
DPF_ERR("Failed to lock level of a driver volume");
return hr;
}
// Fill in the Locked_Box fields
D3DFORMAT Format = m_pParent->Desc()->Format;
if (CPixel::IsDXT(Format))
{
// Start with our current width/height
DWORD Width = m_pParent->Desc()->Width >> m_iLevel;
DWORD Height = m_pParent->Desc()->Height >> m_iLevel;
// Convert to blocks
Width = Width / 4;
Height = Height / 4;
// At least one block
if (Width == 0)
Width = 1;
if (Height == 0)
Height = 1;
switch (Format)
{
// For linear formats,
// Row Pitch is a row of blocks; and SlicePitch is for
// a plane of blocks.
case D3DFMT_DXT1:
// DXT1 is 8 bytes per block
pLockedBoxData->RowPitch = Width * 8;
pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
break;
case D3DFMT_DXT2:
case D3DFMT_DXT3:
case D3DFMT_DXT4:
case D3DFMT_DXT5:
// DXT2-5 are 16 bytes per block
pLockedBoxData->RowPitch = Width * 16;
pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
break;
#ifdef VOLUME_DXT
case D3DFMT_DXV1:
// DXV1 is 32-bytes per block
pLockedBoxData->RowPitch = Width * 32;
pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
break;
case D3DFMT_DXV2:
case D3DFMT_DXV3:
case D3DFMT_DXV4:
case D3DFMT_DXV5:
// DXV2-5 are 64-bytes per block
pLockedBoxData->RowPitch = Width * 64;
pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
break;
#endif //VOLUME_DXT
default:
DPF_ERR("Unknown DXT format?");
DXGASSERT(FALSE);
}
}
else
{
// For all other formats, just return what
// the driver gave us
pLockedBoxData->RowPitch = lockData.lPitch;
pLockedBoxData->SlicePitch = lockData.lSlicePitch;
}
pLockedBoxData->pBits = lockData.lpSurfData;
#ifdef DEBUG
if ((dwFlags & D3DLOCK_DISCARD))
{
DXGASSERT(m_iLevel == 0);
if (!CPixel::IsFourCC(Format) &&
!CPixel::IsIHVFormat(Format))
{
DXGASSERT(pBox == NULL);
memset(pLockedBoxData->pBits, 0xDD, pLockedBoxData->SlicePitch * m_pParent->Desc()->Depth);
for (UINT i = 1; i < m_pParent->GetLevelCount(); ++i)
{
D3DLOCKED_BOX Box;
HRESULT hr = m_pParent->LockBox(i, &Box, NULL, 0);
if (FAILED(hr))
{
DPF(1, "Lock to mipsublevel failed. Not good.");
break;
}
D3DVOLUME_DESC LevelDesc;
m_pParent->GetLevelDesc(i, &LevelDesc);
memset(Box.pBits, 0xDD, Box.SlicePitch * LevelDesc.Depth);
m_pParent->UnlockBox(i);
}
}
}
#endif // DEBUG
// Mark ourselves as locked
m_isLocked = TRUE;
// Done
return S_OK;
} // CDriverVolume::InternalLockBox
#undef DPF_MODNAME
#define DPF_MODNAME "CDriverVolume::UnlockBox"
STDMETHODIMP CDriverVolume::UnlockBox()
{
API_ENTER(Device());
// If we aren't locked; then something is wrong
if (!IsLocked())
{
DPF_ERR("UnlockBox failed on a mip level; volume wasn't locked.");
return D3DERR_INVALIDCALL;
}
DXGASSERT(m_isLockable);
return InternalUnlockBox();
} // CDriverVolume::UnlockBox
#undef DPF_MODNAME
#define DPF_MODNAME "CDriverVolume::InternalUnlockBox"
HRESULT CDriverVolume::InternalUnlockBox()
{
// Call the driver to perform the unlock
D3D8_UNLOCKDATA unlockData = {
m_pParent->Device()->GetHandle(),
m_hKernelHandle
};
HRESULT hr = m_pParent->Device()->GetHalCallbacks()->Unlock(&unlockData);
if (FAILED(hr))
{
DPF_ERR("Driver volume failed to unlock");
return hr;
}
// Clear our locked state
m_isLocked = FALSE;
// If we are lock-once; then we mark ourselves as not lockable
if (m_pParent->Desc()->Usage & D3DUSAGE_LOADONCE)
{
m_isLockable = FALSE;
}
// Done
return S_OK;
} // CDriverVolume::InternalUnlockBox
// End of file : volume.cpp