|
|
/*==========================================================================
* * 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
|