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.

668 lines
18 KiB

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