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

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: volume.cpp
  6. * Content: Implementation of the CVolume and CDriverVolumne classes
  7. *
  8. *
  9. ***************************************************************************/
  10. #include "ddrawpr.h"
  11. #include "volume.hpp"
  12. // IUnknown methods
  13. #undef DPF_MODNAME
  14. #define DPF_MODNAME "CVolume::QueryInterface"
  15. STDMETHODIMP CVolume::QueryInterface (REFIID riid,
  16. VOID **ppvObj)
  17. {
  18. API_ENTER(Device());
  19. if (!VALID_PTR_PTR(ppvObj))
  20. {
  21. DPF_ERR("Invalid ppvObj parameter to QueryInterface for a level of a VolumeTexture");
  22. return D3DERR_INVALIDCALL;
  23. }
  24. if (!VALID_PTR(&riid, sizeof(GUID)))
  25. {
  26. DPF_ERR("Invalid guid memory address to QueryInterface for a level of a VolumeTexture");
  27. return D3DERR_INVALIDCALL;
  28. }
  29. if (riid == IID_IDirect3DVolume8 ||
  30. riid == IID_IUnknown)
  31. {
  32. *ppvObj = static_cast<void*>(static_cast<IDirect3DVolume8 *>(this));
  33. AddRef();
  34. return S_OK;
  35. }
  36. DPF_ERR("Unsupported Interface identifier passed to QueryInterface for a level of a VolumeTexture");
  37. // Null out param
  38. *ppvObj = NULL;
  39. return E_NOINTERFACE;
  40. } // QueryInterface
  41. #undef DPF_MODNAME
  42. #define DPF_MODNAME "CVolume::AddRef"
  43. STDMETHODIMP_(ULONG) CVolume::AddRef()
  44. {
  45. API_ENTER_NO_LOCK(Device());
  46. #ifdef DEBUG
  47. m_cRefDebug++;
  48. #endif // DEBUG
  49. return m_pParent->AddRefImpl();
  50. } // AddRef
  51. #undef DPF_MODNAME
  52. #define DPF_MODNAME "CVolume::Release"
  53. STDMETHODIMP_(ULONG) CVolume::Release()
  54. {
  55. API_ENTER_SUBOBJECT_RELEASE(Device());
  56. #ifdef DEBUG
  57. m_cRefDebug--;
  58. if (m_cRefDebug & 0x80000000)
  59. {
  60. DPF_ERR("A level of a mip-volume has been released more often than it has been add-ref'ed! Danger!!");
  61. }
  62. #endif // DEBUG
  63. return m_pParent->ReleaseImpl();
  64. } // Release
  65. // IBuffer methods
  66. #undef DPF_MODNAME
  67. #define DPF_MODNAME "CVolume::SetPrivateData"
  68. STDMETHODIMP CVolume::SetPrivateData(REFGUID riid,
  69. CONST VOID *pvData,
  70. DWORD cbData,
  71. DWORD dwFlags)
  72. {
  73. API_ENTER(Device());
  74. return m_pParent->SetPrivateDataImpl(riid,
  75. pvData,
  76. cbData,
  77. dwFlags,
  78. m_iLevel);
  79. } // SetPrivateData
  80. #undef DPF_MODNAME
  81. #define DPF_MODNAME "CVolume::GetPrivateData"
  82. STDMETHODIMP CVolume::GetPrivateData(REFGUID riid,
  83. VOID *pvData,
  84. DWORD *pcbData)
  85. {
  86. API_ENTER(Device());
  87. return m_pParent->GetPrivateDataImpl(riid,
  88. pvData,
  89. pcbData,
  90. m_iLevel);
  91. } // GetPrivateData
  92. #undef DPF_MODNAME
  93. #define DPF_MODNAME "CVolume::FreePrivateData"
  94. STDMETHODIMP CVolume::FreePrivateData(REFGUID riid)
  95. {
  96. API_ENTER(Device());
  97. return m_pParent->FreePrivateDataImpl(riid,
  98. m_iLevel);
  99. } // FreePrivateData
  100. #undef DPF_MODNAME
  101. #define DPF_MODNAME "CVolume::GetContainer"
  102. STDMETHODIMP CVolume::GetContainer(REFIID riid,
  103. void **ppContainer)
  104. {
  105. API_ENTER(Device());
  106. return m_pParent->QueryInterface(riid, ppContainer);
  107. } // OpenContainer
  108. #undef DPF_MODNAME
  109. #define DPF_MODNAME "CVolume::GetDevice"
  110. STDMETHODIMP CVolume::GetDevice(IDirect3DDevice8 **ppDevice)
  111. {
  112. API_ENTER(Device());
  113. return m_pParent->GetDevice(ppDevice);
  114. } // OpenDevice
  115. // IDirect3DVolume8 methods
  116. #undef DPF_MODNAME
  117. #define DPF_MODNAME "CVolume::GetDesc"
  118. STDMETHODIMP CVolume::GetDesc(D3DVOLUME_DESC *pDesc)
  119. {
  120. API_ENTER(Device());
  121. // If parameters are bad, then we should fail some stuff
  122. if (!VALID_WRITEPTR(pDesc, sizeof(D3DVOLUME_DESC)))
  123. {
  124. DPF_ERR("bad pointer for pDesc passed to GetDesc for a level of a VolumeTexture");
  125. return D3DERR_INVALIDCALL;
  126. }
  127. // We basically get our volume desc from our parent
  128. // and then modify the width, height, and depth fields.
  129. *pDesc = *m_pParent->Desc();
  130. pDesc->Width >>= m_iLevel;
  131. pDesc->Height >>= m_iLevel;
  132. pDesc->Depth >>= m_iLevel;
  133. if (pDesc->Width == 0)
  134. {
  135. pDesc->Width = 1;
  136. }
  137. if (pDesc->Height == 0)
  138. {
  139. pDesc->Height = 1;
  140. }
  141. if (pDesc->Depth == 0)
  142. {
  143. pDesc->Depth = 1;
  144. }
  145. // Also need to modify the type field
  146. pDesc->Type = D3DRTYPE_VOLUME;
  147. // Also modify the size field
  148. pDesc->Size = CPixel::ComputeVolumeSize(pDesc->Width,
  149. pDesc->Height,
  150. pDesc->Depth,
  151. pDesc->Format);
  152. // We also need to modify the pool and format
  153. // to reflect the data the user passed to us
  154. pDesc->Pool = m_pParent->GetUserPool();
  155. pDesc->Format = m_pParent->GetUserFormat();
  156. pDesc->Usage &= D3DUSAGE_EXTERNAL;
  157. // We're done
  158. return S_OK;
  159. } // GetDesc
  160. #undef DPF_MODNAME
  161. #define DPF_MODNAME "CVolume::LockBox"
  162. STDMETHODIMP CVolume::LockBox(D3DLOCKED_BOX *pLockedBoxData,
  163. CONST D3DBOX *pBox,
  164. DWORD dwFlags)
  165. {
  166. API_ENTER(Device());
  167. // If parameters are bad, then we should fail some stuff
  168. if (!VALID_WRITEPTR(pLockedBoxData, sizeof(D3DLOCKED_BOX)))
  169. {
  170. DPF_ERR("bad pointer for pLockedBoxData passed to LockBox for a level of a VolumeTexture");
  171. return D3DERR_INVALIDCALL;
  172. }
  173. // Zero out returned data
  174. ZeroMemory(pLockedBoxData, sizeof(D3DLOCKED_BOX));
  175. // Validate Box
  176. if (pBox != NULL)
  177. {
  178. DWORD Width = m_pParent->Desc()->Width >> m_iLevel;
  179. DWORD Height = m_pParent->Desc()->Height >> m_iLevel;
  180. DWORD Depth = m_pParent->Desc()->Depth >> m_iLevel;
  181. if (!CPixel::IsValidBox(m_pParent->Desc()->Format,
  182. Width,
  183. Height,
  184. Depth,
  185. pBox))
  186. {
  187. DPF_ERR("LockBox for a Volume fails");
  188. return D3DERR_INVALIDCALL;
  189. }
  190. }
  191. if (dwFlags & ~D3DLOCK_VOL_VALID)
  192. {
  193. if (dwFlags & D3DLOCK_DISCARD)
  194. {
  195. if (dwFlags & D3DLOCK_READONLY)
  196. {
  197. DPF_ERR("D3DLOCK_READONLY is not allowed with D3DLOCK_DISCARD");
  198. return D3DERR_INVALIDCALL;
  199. }
  200. if (!(m_pParent->Desc()->Usage & D3DUSAGE_DYNAMIC))
  201. {
  202. DPF_ERR("D3DLOCK_DISCARD is allowed only with dynamic textures");
  203. return D3DERR_INVALIDCALL;
  204. }
  205. if (m_iLevel > 0)
  206. {
  207. DPF_ERR("D3DLOCK_DISCARD is allowed only on level 0"
  208. " (the top mip level). DISCARD in this case will discard"
  209. " the entire volume.");
  210. return D3DERR_INVALIDCALL;
  211. }
  212. if (pBox != NULL)
  213. {
  214. DPF_ERR("Subboxes not allowed with D3DLOCK_DISCARD");
  215. return D3DERR_INVALIDCALL;
  216. }
  217. }
  218. else
  219. {
  220. DPF_ERR("Invalid dwFlags parameter passed to LockBox for a level of a VolumeTexture");
  221. DPF_EXPLAIN_BAD_LOCK_FLAGS(0, dwFlags & ~D3DLOCK_VOL_VALID);
  222. return D3DERR_INVALIDCALL;
  223. }
  224. }
  225. if (!m_isLockable)
  226. {
  227. m_pParent->ReportWhyLockFailed();
  228. return D3DERR_INVALIDCALL;
  229. }
  230. return InternalLockBox(pLockedBoxData, pBox, dwFlags);
  231. } // LockBox
  232. #undef DPF_MODNAME
  233. #define DPF_MODNAME "CVolume::InternalLockBox"
  234. HRESULT CVolume::InternalLockBox(D3DLOCKED_BOX *pLockedBoxData,
  235. CONST D3DBOX *pBox,
  236. DWORD dwFlags)
  237. {
  238. // Only one lock outstanding at a time is supported
  239. if (IsLocked())
  240. {
  241. DPF_ERR("LockBox failed on a mip level; volume was already locked.");
  242. return D3DERR_INVALIDCALL;
  243. }
  244. // Notify the parent/device if we are about to be modified
  245. if ( (m_pParent->GetUserPool() != D3DPOOL_SCRATCH) && (!(dwFlags & D3DLOCK_READONLY)) )
  246. {
  247. m_pParent->OnVolumeLock(m_iLevel, pBox, dwFlags);
  248. }
  249. // Figure out our stride/pointer to bits
  250. m_pParent->ComputeMipVolumeOffset(m_iLevel,
  251. pBox,
  252. pLockedBoxData);
  253. // Mark ourselves as locked
  254. m_isLocked = TRUE;
  255. // Done
  256. return S_OK;
  257. } // InternalLockBox
  258. #undef DPF_MODNAME
  259. #define DPF_MODNAME "CVolume::UnlockBox"
  260. STDMETHODIMP CVolume::UnlockBox()
  261. {
  262. API_ENTER(Device());
  263. // If we aren't locked; then something is wrong
  264. if (!IsLocked())
  265. {
  266. DPF_ERR("UnlockBox failed on a volume level; volume wasn't locked.");
  267. return D3DERR_INVALIDCALL;
  268. }
  269. DXGASSERT(m_isLockable);
  270. return InternalUnlockBox();
  271. } // UnlockBox
  272. #undef DPF_MODNAME
  273. #define DPF_MODNAME "CVolume::InternalUnlockBox"
  274. HRESULT CVolume::InternalUnlockBox()
  275. {
  276. // Clear our locked state
  277. m_isLocked = FALSE;
  278. // If we are lock-once; then we mark ourselves as not lockable
  279. if (m_pParent->Desc()->Usage & D3DUSAGE_LOADONCE)
  280. {
  281. m_isLockable = FALSE;
  282. }
  283. // Done
  284. return S_OK;
  285. } // InternalUnlockBox
  286. //
  287. // CDriverVolume class modifies the implementation
  288. // of the LockBox and UnlockBox methods of the CVolume class
  289. //
  290. #undef DPF_MODNAME
  291. #define DPF_MODNAME "CDriverVolume::LockBox"
  292. STDMETHODIMP CDriverVolume::LockBox(D3DLOCKED_BOX *pLockedBoxData,
  293. CONST D3DBOX *pBox,
  294. DWORD dwFlags)
  295. {
  296. API_ENTER(Device());
  297. // If parameters are bad, then we should fail some stuff
  298. if (!VALID_WRITEPTR(pLockedBoxData, sizeof(D3DLOCKED_BOX)))
  299. {
  300. DPF_ERR("bad pointer for pLockedBoxData passed to LockBox for a level of a VolumeTexture");
  301. return D3DERR_INVALIDCALL;
  302. }
  303. // Zero out returned data
  304. ZeroMemory(pLockedBoxData, sizeof(D3DLOCKED_BOX));
  305. // Validate Box
  306. if (pBox != NULL)
  307. {
  308. DWORD Width = m_pParent->Desc()->Width >> m_iLevel;
  309. DWORD Height = m_pParent->Desc()->Height >> m_iLevel;
  310. DWORD Depth = m_pParent->Desc()->Depth >> m_iLevel;
  311. if (!CPixel::IsValidBox(m_pParent->Desc()->Format,
  312. Width,
  313. Height,
  314. Depth,
  315. pBox))
  316. {
  317. DPF_ERR("LockBox for a Volume fails");
  318. return D3DERR_INVALIDCALL;
  319. }
  320. }
  321. if (dwFlags & ~D3DLOCK_VOL_VALID)
  322. {
  323. if (dwFlags & D3DLOCK_DISCARD)
  324. {
  325. if (dwFlags & D3DLOCK_READONLY)
  326. {
  327. DPF_ERR("D3DLOCK_READONLY is not allowed with D3DLOCK_DISCARD");
  328. return D3DERR_INVALIDCALL;
  329. }
  330. if (!(m_pParent->Desc()->Usage & D3DUSAGE_DYNAMIC))
  331. {
  332. DPF_ERR("D3DLOCK_DISCARD is allowed only with dynamic textures");
  333. return D3DERR_INVALIDCALL;
  334. }
  335. if (m_iLevel > 0)
  336. {
  337. DPF_ERR("D3DLOCK_DISCARD is allowed only on level 0"
  338. " (the top mip level). DISCARD in this case will discard"
  339. " the entire volume.");
  340. return D3DERR_INVALIDCALL;
  341. }
  342. if (pBox != NULL)
  343. {
  344. DPF_ERR("Subboxes not allowed with D3DLOCK_DISCARD");
  345. return D3DERR_INVALIDCALL;
  346. }
  347. }
  348. else
  349. {
  350. DPF_ERR("Invalid dwFlags parameter passed to LockBox for a level of a VolumeTexture");
  351. DPF_EXPLAIN_BAD_LOCK_FLAGS(0, dwFlags & ~D3DLOCK_VOL_VALID);
  352. return D3DERR_INVALIDCALL;
  353. }
  354. }
  355. if (!m_isLockable)
  356. {
  357. m_pParent->ReportWhyLockFailed();
  358. return D3DERR_INVALIDCALL;
  359. }
  360. return InternalLockBox(pLockedBoxData, pBox, dwFlags);
  361. } // CDriverVolume::LockBox
  362. #undef DPF_MODNAME
  363. #define DPF_MODNAME "CDriverVolume::InternalLockBox"
  364. HRESULT CDriverVolume::InternalLockBox(D3DLOCKED_BOX *pLockedBoxData,
  365. CONST D3DBOX *pBox,
  366. DWORD dwFlags)
  367. {
  368. // Only one lock outstanding at a time is supported
  369. if (IsLocked())
  370. {
  371. DPF_ERR("LockBox failed on a volume level; volume was already locked.");
  372. return D3DERR_INVALIDCALL;
  373. }
  374. // Notify the parent/device if we are about to be accessed.
  375. // Driver volume textures may be written to by HW through
  376. // UpdateTexture. So we may need to sync with the current
  377. // command batch.
  378. m_pParent->OnVolumeLock(m_iLevel, pBox, dwFlags);
  379. // Prepare a LockData structure for the HAL call
  380. D3D8_LOCKDATA lockData;
  381. ZeroMemory(&lockData, sizeof lockData);
  382. lockData.hDD = m_pParent->Device()->GetHandle();
  383. lockData.hSurface = m_hKernelHandle;
  384. lockData.dwFlags = dwFlags;
  385. if (pBox != NULL)
  386. {
  387. lockData.bHasBox = TRUE;
  388. lockData.box = *pBox;
  389. }
  390. HRESULT hr = m_pParent->Device()->GetHalCallbacks()->Lock(&lockData);
  391. if (FAILED(hr))
  392. {
  393. DPF_ERR("Failed to lock level of a driver volume");
  394. return hr;
  395. }
  396. // Fill in the Locked_Box fields
  397. D3DFORMAT Format = m_pParent->Desc()->Format;
  398. if (CPixel::IsDXT(Format))
  399. {
  400. // Start with our current width/height
  401. DWORD Width = m_pParent->Desc()->Width >> m_iLevel;
  402. DWORD Height = m_pParent->Desc()->Height >> m_iLevel;
  403. // Convert to blocks
  404. Width = Width / 4;
  405. Height = Height / 4;
  406. // At least one block
  407. if (Width == 0)
  408. Width = 1;
  409. if (Height == 0)
  410. Height = 1;
  411. switch (Format)
  412. {
  413. // For linear formats,
  414. // Row Pitch is a row of blocks; and SlicePitch is for
  415. // a plane of blocks.
  416. case D3DFMT_DXT1:
  417. // DXT1 is 8 bytes per block
  418. pLockedBoxData->RowPitch = Width * 8;
  419. pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
  420. break;
  421. case D3DFMT_DXT2:
  422. case D3DFMT_DXT3:
  423. case D3DFMT_DXT4:
  424. case D3DFMT_DXT5:
  425. // DXT2-5 are 16 bytes per block
  426. pLockedBoxData->RowPitch = Width * 16;
  427. pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
  428. break;
  429. #ifdef VOLUME_DXT
  430. case D3DFMT_DXV1:
  431. // DXV1 is 32-bytes per block
  432. pLockedBoxData->RowPitch = Width * 32;
  433. pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
  434. break;
  435. case D3DFMT_DXV2:
  436. case D3DFMT_DXV3:
  437. case D3DFMT_DXV4:
  438. case D3DFMT_DXV5:
  439. // DXV2-5 are 64-bytes per block
  440. pLockedBoxData->RowPitch = Width * 64;
  441. pLockedBoxData->SlicePitch = Height * pLockedBoxData->RowPitch;
  442. break;
  443. #endif //VOLUME_DXT
  444. default:
  445. DPF_ERR("Unknown DXT format?");
  446. DXGASSERT(FALSE);
  447. }
  448. }
  449. else
  450. {
  451. // For all other formats, just return what
  452. // the driver gave us
  453. pLockedBoxData->RowPitch = lockData.lPitch;
  454. pLockedBoxData->SlicePitch = lockData.lSlicePitch;
  455. }
  456. pLockedBoxData->pBits = lockData.lpSurfData;
  457. #ifdef DEBUG
  458. if ((dwFlags & D3DLOCK_DISCARD))
  459. {
  460. DXGASSERT(m_iLevel == 0);
  461. if (!CPixel::IsFourCC(Format) &&
  462. !CPixel::IsIHVFormat(Format))
  463. {
  464. DXGASSERT(pBox == NULL);
  465. memset(pLockedBoxData->pBits, 0xDD, pLockedBoxData->SlicePitch * m_pParent->Desc()->Depth);
  466. for (UINT i = 1; i < m_pParent->GetLevelCount(); ++i)
  467. {
  468. D3DLOCKED_BOX Box;
  469. HRESULT hr = m_pParent->LockBox(i, &Box, NULL, 0);
  470. if (FAILED(hr))
  471. {
  472. DPF(1, "Lock to mipsublevel failed. Not good.");
  473. break;
  474. }
  475. D3DVOLUME_DESC LevelDesc;
  476. m_pParent->GetLevelDesc(i, &LevelDesc);
  477. memset(Box.pBits, 0xDD, Box.SlicePitch * LevelDesc.Depth);
  478. m_pParent->UnlockBox(i);
  479. }
  480. }
  481. }
  482. #endif // DEBUG
  483. // Mark ourselves as locked
  484. m_isLocked = TRUE;
  485. // Done
  486. return S_OK;
  487. } // CDriverVolume::InternalLockBox
  488. #undef DPF_MODNAME
  489. #define DPF_MODNAME "CDriverVolume::UnlockBox"
  490. STDMETHODIMP CDriverVolume::UnlockBox()
  491. {
  492. API_ENTER(Device());
  493. // If we aren't locked; then something is wrong
  494. if (!IsLocked())
  495. {
  496. DPF_ERR("UnlockBox failed on a mip level; volume wasn't locked.");
  497. return D3DERR_INVALIDCALL;
  498. }
  499. DXGASSERT(m_isLockable);
  500. return InternalUnlockBox();
  501. } // CDriverVolume::UnlockBox
  502. #undef DPF_MODNAME
  503. #define DPF_MODNAME "CDriverVolume::InternalUnlockBox"
  504. HRESULT CDriverVolume::InternalUnlockBox()
  505. {
  506. // Call the driver to perform the unlock
  507. D3D8_UNLOCKDATA unlockData = {
  508. m_pParent->Device()->GetHandle(),
  509. m_hKernelHandle
  510. };
  511. HRESULT hr = m_pParent->Device()->GetHalCallbacks()->Unlock(&unlockData);
  512. if (FAILED(hr))
  513. {
  514. DPF_ERR("Driver volume failed to unlock");
  515. return hr;
  516. }
  517. // Clear our locked state
  518. m_isLocked = FALSE;
  519. // If we are lock-once; then we mark ourselves as not lockable
  520. if (m_pParent->Desc()->Usage & D3DUSAGE_LOADONCE)
  521. {
  522. m_isLockable = FALSE;
  523. }
  524. // Done
  525. return S_OK;
  526. } // CDriverVolume::InternalUnlockBox
  527. // End of file : volume.cpp