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.

1408 lines
41 KiB

  1. /*==========================================================================;
  2. *
  3. * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: mipvol.cpp
  6. * Content: Implementation of the CMipVolume and CManagedMipVolume
  7. * classes.
  8. *
  9. *
  10. ***************************************************************************/
  11. #include "ddrawpr.h"
  12. #include "mipvol.hpp"
  13. #include "volume.hpp"
  14. #include "d3di.hpp"
  15. #include "resource.inl"
  16. #undef DPF_MODNAME
  17. #define DPF_MODNAME "CMipVolume::Create"
  18. // Static class function for creating a mip-map object.
  19. // (Because it is static; it doesn't have a this pointer.)
  20. //
  21. // We do all parameter checking here to reduce the overhead
  22. // in the constructor which is called by the internal Clone
  23. // method which is used by resource management as part of the
  24. // performance critical download operation.
  25. HRESULT CMipVolume::Create(CBaseDevice *pDevice,
  26. DWORD Width,
  27. DWORD Height,
  28. DWORD Depth,
  29. DWORD cLevels,
  30. DWORD Usage,
  31. D3DFORMAT Format,
  32. D3DPOOL Pool,
  33. IDirect3DVolumeTexture8 **ppMipVolume)
  34. {
  35. HRESULT hr;
  36. // Do parameter checking here
  37. if (!VALID_PTR_PTR(ppMipVolume))
  38. {
  39. DPF_ERR("Bad parameter passed for ppMipVolume for creating a MipVolume");
  40. return D3DERR_INVALIDCALL;
  41. }
  42. // Zero-out return parameter
  43. *ppMipVolume = NULL;
  44. // Check if format, pool is valid
  45. hr = Validate(pDevice,
  46. D3DRTYPE_VOLUMETEXTURE,
  47. Pool,
  48. Usage,
  49. Format);
  50. if (FAILED(hr))
  51. {
  52. // Validate does it's own DPFing
  53. return D3DERR_INVALIDCALL;
  54. }
  55. // Check usage flags
  56. if (Usage & ~D3DUSAGE_VOLUMETEXTURE_VALID)
  57. {
  58. DPF_ERR("Invalid flag specified for volume texture creation.");
  59. return D3DERR_INVALIDCALL;
  60. }
  61. // Infer internal usage flags
  62. Usage = InferUsageFlags(Pool, Usage, Format);
  63. // Expand cLevels if necessary
  64. if (cLevels == 0)
  65. {
  66. // See if HW can mip
  67. if ( (Pool != D3DPOOL_SCRATCH) && !(pDevice->GetD3DCaps()->TextureCaps &
  68. D3DPTEXTURECAPS_MIPVOLUMEMAP))
  69. {
  70. // Can't mip so use 1
  71. cLevels = 1;
  72. }
  73. else
  74. {
  75. // Determine number of levels
  76. cLevels = ComputeLevels(Width, Height, Depth);
  77. }
  78. }
  79. if (cLevels > 32)
  80. {
  81. DPF_ERR("No more than 32 levels are supported. CreateVolumeTexture failed");
  82. // This limitation is based on the number of
  83. // bits that we have allocated for iLevel in
  84. // some of the supporting classes.
  85. return D3DERR_INVALIDCALL;
  86. }
  87. if (cLevels > 1)
  88. {
  89. if ((Width >> (cLevels - 1)) == 0 &&
  90. (Height >> (cLevels - 1)) == 0 &&
  91. (Depth >> (cLevels - 1)) == 0)
  92. {
  93. DPF_ERR("Too many levels for volume texture of this size.");
  94. return D3DERR_INVALIDCALL;
  95. }
  96. }
  97. if (Pool != D3DPOOL_SCRATCH)
  98. {
  99. //Device specific constraints:
  100. // Check size constraints for volumes
  101. if (pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP_POW2)
  102. {
  103. if (!IsPowerOfTwo(Width))
  104. {
  105. DPF_ERR("Width must be power of two for mip-volumes");
  106. return D3DERR_INVALIDCALL;
  107. }
  108. if (!IsPowerOfTwo(Height))
  109. {
  110. DPF_ERR("Height must be power of two for mip-volumes");
  111. return D3DERR_INVALIDCALL;
  112. }
  113. if (!IsPowerOfTwo(Depth))
  114. {
  115. DPF_ERR("Depth must be power of two for mip-volumes");
  116. return D3DERR_INVALIDCALL;
  117. }
  118. }
  119. // Check texture size restrictions
  120. if (Width > pDevice->GetD3DCaps()->MaxVolumeExtent)
  121. {
  122. DPF_ERR("Texture width is larger than what the device supports. CreateVolumeTexture fails");
  123. return D3DERR_INVALIDCALL;
  124. }
  125. if (Height > pDevice->GetD3DCaps()->MaxVolumeExtent)
  126. {
  127. DPF_ERR("Texture height is larger than what the device supports. CreateVolumeTexture fails");
  128. return D3DERR_INVALIDCALL;
  129. }
  130. if (Depth > pDevice->GetD3DCaps()->MaxVolumeExtent)
  131. {
  132. DPF_ERR("Texture depth is larger than what the device supports. CreateVolumeTexture fails");
  133. return D3DERR_INVALIDCALL;
  134. }
  135. // Check that the device supports volume texture
  136. if (!(pDevice->GetD3DCaps()->TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP))
  137. {
  138. DPF_ERR("Device doesn't support volume textures; creation failed.");
  139. return D3DERR_INVALIDCALL;
  140. }
  141. // Check if the device supports mipped volumes
  142. if (cLevels > 1)
  143. {
  144. if (!(pDevice->GetD3DCaps()->TextureCaps &
  145. D3DPTEXTURECAPS_MIPVOLUMEMAP))
  146. {
  147. DPF_ERR("Device doesn't support mipped volume textures; creation failed.");
  148. return D3DERR_INVALIDCALL;
  149. }
  150. }
  151. }
  152. // Size is required to be 4x4
  153. if (CPixel::Requires4X4(Format))
  154. {
  155. if ((Width & 3) ||
  156. (Height & 3))
  157. {
  158. DPF_ERR("DXT Formats require width/height to multiples of 4. CreateVolumeTexture fails");
  159. return D3DERR_INVALIDCALL;
  160. }
  161. if (CPixel::IsVolumeDXT(Format))
  162. {
  163. if (Depth & 3)
  164. {
  165. DPF_ERR("DXT Formats require width/height to multiples of 4. CreateVolumeTexture fails");
  166. return D3DERR_INVALIDCALL;
  167. }
  168. }
  169. }
  170. // Validate against zero width/height/depth
  171. if (Width == 0 ||
  172. Height == 0 ||
  173. Depth == 0)
  174. {
  175. DPF_ERR("Width/Height/Depth must be non-zero. CreateVolumeTexture fails");
  176. return D3DERR_INVALIDCALL;
  177. }
  178. // DX9: May need to support mapping for volumes that
  179. // contain depth data someday.
  180. // Allocate a new MipVolume object and return it
  181. CMipVolume *pMipVolume = new CMipVolume(pDevice,
  182. Width,
  183. Height,
  184. Depth,
  185. cLevels,
  186. Usage,
  187. Format,
  188. Pool,
  189. REF_EXTERNAL,
  190. &hr);
  191. if (pMipVolume == NULL)
  192. {
  193. DPF_ERR("Out of Memory creating mip-volume");
  194. return E_OUTOFMEMORY;
  195. }
  196. if (FAILED(hr))
  197. {
  198. DPF_ERR("Error during initialization of mip-volume");
  199. pMipVolume->ReleaseImpl();
  200. return hr;
  201. }
  202. // We're done; just return the object
  203. *ppMipVolume = pMipVolume;
  204. return hr;
  205. } // static Create
  206. #undef DPF_MODNAME
  207. #define DPF_MODNAME "CMipVolume::CMipVolume"
  208. // Constructor for the mip map class
  209. CMipVolume::CMipVolume(CBaseDevice *pDevice,
  210. DWORD Width,
  211. DWORD Height,
  212. DWORD Depth,
  213. DWORD cLevels,
  214. DWORD Usage,
  215. D3DFORMAT UserFormat,
  216. D3DPOOL UserPool,
  217. REF_TYPE refType,
  218. HRESULT *phr
  219. ) :
  220. CBaseTexture(pDevice, cLevels, UserPool, UserFormat, refType),
  221. m_VolumeArray(NULL),
  222. m_rgbPixels(NULL),
  223. m_cBoxUsed(MIPVOLUME_ALLDIRTY)
  224. {
  225. // We assume that we start out dirty
  226. DXGASSERT(IsDirty());
  227. // Initialize basic structures
  228. m_desc.Format = UserFormat;
  229. m_desc.Pool = UserPool;
  230. m_desc.Usage = Usage;
  231. m_desc.Type = D3DRTYPE_VOLUMETEXTURE;
  232. m_desc.Width = Width;
  233. m_desc.Height = Height;
  234. m_desc.Depth = Depth;
  235. // Estimate size of memory allocation
  236. m_desc.Size = CPixel::ComputeMipVolumeSize(Width,
  237. Height,
  238. Depth,
  239. cLevels,
  240. UserFormat);
  241. // Allocate Pixel Data for SysMem or D3DManaged cases
  242. if (IS_D3D_ALLOCATED_POOL(UserPool) ||
  243. IsTypeD3DManaged(Device(), D3DRTYPE_VOLUMETEXTURE, UserPool))
  244. {
  245. m_rgbPixels = new BYTE[m_desc.Size];
  246. if (m_rgbPixels == NULL)
  247. {
  248. DPF_ERR("Out of memory allocating memory for mip-volume levels");
  249. *phr = E_OUTOFMEMORY;
  250. return;
  251. }
  252. }
  253. // Create the DDSURFACEINFO array and CreateSurfaceData object
  254. DXGASSERT(cLevels <= 32);
  255. DDSURFACEINFO SurfInfo[32];
  256. ZeroMemory(SurfInfo, sizeof(SurfInfo));
  257. D3D8_CREATESURFACEDATA CreateSurfaceData;
  258. ZeroMemory(&CreateSurfaceData, sizeof(CreateSurfaceData));
  259. // Set up the basic information
  260. CreateSurfaceData.hDD = pDevice->GetHandle();
  261. CreateSurfaceData.pSList = &SurfInfo[0];
  262. CreateSurfaceData.dwSCnt = cLevels;
  263. CreateSurfaceData.Type = D3DRTYPE_VOLUMETEXTURE;
  264. CreateSurfaceData.dwUsage = m_desc.Usage;
  265. CreateSurfaceData.Format = UserFormat;
  266. CreateSurfaceData.MultiSampleType = D3DMULTISAMPLE_NONE;
  267. CreateSurfaceData.Pool = DetermineCreationPool(Device(),
  268. D3DRTYPE_VOLUMETEXTURE,
  269. Usage,
  270. UserPool);
  271. // Iterate of each level to create the individual level
  272. // data
  273. for (DWORD iLevel = 0; iLevel < cLevels; iLevel++)
  274. {
  275. // Fill in the relevant information
  276. DXGASSERT(Width >= 1);
  277. DXGASSERT(Height >= 1);
  278. DXGASSERT(Depth >= 1);
  279. SurfInfo[iLevel].cpWidth = Width;
  280. SurfInfo[iLevel].cpHeight = Height;
  281. SurfInfo[iLevel].cpDepth = Depth;
  282. // If we allocated the memory, pass down
  283. // the sys-mem pointers
  284. if (m_rgbPixels)
  285. {
  286. D3DLOCKED_BOX lock;
  287. CPixel::ComputeMipVolumeOffset(&m_desc,
  288. iLevel,
  289. m_rgbPixels,
  290. NULL, // pBox
  291. &lock);
  292. SurfInfo[iLevel].pbPixels = (BYTE*)lock.pBits;
  293. SurfInfo[iLevel].iPitch = lock.RowPitch;
  294. SurfInfo[iLevel].iSlicePitch = lock.SlicePitch;
  295. }
  296. // Scale width and height down
  297. if (Width > 1)
  298. {
  299. Width >>= 1;
  300. }
  301. if (Height > 1)
  302. {
  303. Height >>= 1;
  304. }
  305. if (Depth > 1)
  306. {
  307. Depth >>= 1;
  308. }
  309. }
  310. // Allocate array of pointers to MipSurfaces
  311. m_VolumeArray = new CVolume*[cLevels];
  312. if (m_VolumeArray == NULL)
  313. {
  314. DPF_ERR("Out of memory creating VolumeTexture");
  315. *phr = E_OUTOFMEMORY;
  316. return;
  317. }
  318. // Zero the memory for safe cleanup
  319. ZeroMemory(m_VolumeArray, sizeof(*m_VolumeArray) * cLevels);
  320. // NOTE: any failures after this point needs to free up some
  321. // kernel handles, unless it's scratch
  322. if (UserPool != D3DPOOL_SCRATCH)
  323. {
  324. // Call the HAL to create this surface
  325. *phr = pDevice->GetHalCallbacks()->CreateSurface(&CreateSurfaceData);
  326. if (FAILED(*phr))
  327. return;
  328. // Remember what pool we really got
  329. m_desc.Pool = CreateSurfaceData.Pool;
  330. // We need to remember the handles from the top most
  331. // level of the mip-map
  332. SetKernelHandle(SurfInfo[0].hKernelHandle);
  333. }
  334. // Create and Initialize each MipLevel
  335. for (iLevel = 0; iLevel < cLevels; iLevel++)
  336. {
  337. // Is this a sys-mem surface; could be d3d managed
  338. if (IS_D3D_ALLOCATED_POOL(m_desc.Pool))
  339. {
  340. m_VolumeArray[iLevel] =
  341. new CVolume(this,
  342. (BYTE)iLevel,
  343. SurfInfo[iLevel].hKernelHandle);
  344. }
  345. else
  346. {
  347. // Must be a driver kind of surface; could be driver managed
  348. m_VolumeArray[iLevel] =
  349. new CDriverVolume(this,
  350. (BYTE)iLevel,
  351. SurfInfo[iLevel].hKernelHandle);
  352. }
  353. if (m_VolumeArray[iLevel] == NULL)
  354. {
  355. DPF_ERR("Out of memory creating volume level");
  356. *phr = E_OUTOFMEMORY;
  357. // Need to free handles that we got before we return; we
  358. // only free the ones that weren't successfully entrusted
  359. // to a CVolume because those will be cleaned up automatically
  360. // at their destructor
  361. if (UserPool != D3DPOOL_SCRATCH)
  362. {
  363. for (UINT i = iLevel; i < cLevels; i++)
  364. {
  365. DXGASSERT(SurfInfo[i].hKernelHandle);
  366. D3D8_DESTROYSURFACEDATA DestroySurfData;
  367. DestroySurfData.hDD = Device()->GetHandle();
  368. DestroySurfData.hSurface = SurfInfo[i].hKernelHandle;
  369. Device()->GetHalCallbacks()->DestroySurface(&DestroySurfData);
  370. }
  371. }
  372. return;
  373. }
  374. }
  375. // If this is a D3D managed volume then we need
  376. // to tell the Resource Manager to remember us. This has to happen
  377. // at the very end of the constructor so that the important data
  378. // members are built up correctly
  379. if (CResource::IsTypeD3DManaged(Device(), D3DRTYPE_VOLUMETEXTURE, UserPool))
  380. {
  381. *phr = InitializeRMHandle();
  382. }
  383. return;
  384. } // CMipVolume::CMipVolume
  385. #undef DPF_MODNAME
  386. #define DPF_MODNAME "CMipVolume::~CMipVolume"
  387. // Destructor
  388. CMipVolume::~CMipVolume()
  389. {
  390. // The destructor has to handle partially
  391. // created objects. Delete automatically
  392. // handles NULL; and members are nulled
  393. // as part of core constructors
  394. if (m_VolumeArray)
  395. {
  396. for (DWORD i = 0; i < m_cLevels; i++)
  397. {
  398. delete m_VolumeArray[i];
  399. }
  400. delete [] m_VolumeArray;
  401. }
  402. delete [] m_rgbPixels;
  403. } // CMipVolume::~CMipVolume
  404. // Methods for the Resource Manager
  405. #undef DPF_MODNAME
  406. #define DPF_MODNAME "CMipVolume::Clone"
  407. // Specifies a creation of a resource that
  408. // looks just like the current one; in a new POOL
  409. // with a new LOD.
  410. HRESULT CMipVolume::Clone(D3DPOOL Pool,
  411. CResource **ppResource) const
  412. {
  413. // NULL out parameter
  414. *ppResource = NULL;
  415. // Determine the number of levels/width/height/depth
  416. // of the clone
  417. DWORD cLevels = GetLevelCountImpl();
  418. DWORD Width = m_desc.Width;
  419. DWORD Height = m_desc.Height;
  420. DWORD Depth = m_desc.Depth;
  421. DWORD dwLOD = GetLODI();
  422. // If LOD is zero, then there are no changes
  423. if (dwLOD > 0)
  424. {
  425. // Clamp LOD to cLevels-1
  426. if (dwLOD >= cLevels)
  427. {
  428. dwLOD = cLevels - 1;
  429. }
  430. // scale down the destination texture
  431. // to correspond the appropiate max lod
  432. Width >>= dwLOD;
  433. if (Width == 0)
  434. Width = 1;
  435. Height >>= dwLOD;
  436. if (Height == 0)
  437. Height = 1;
  438. Depth >>= dwLOD;
  439. if (Depth == 0)
  440. Depth = 1;
  441. // Reduce the number based on the our max lod.
  442. cLevels -= dwLOD;
  443. }
  444. // Sanity checking
  445. DXGASSERT(cLevels >= 1);
  446. DXGASSERT(Width > 0);
  447. DXGASSERT(Height > 0);
  448. DXGASSERT(Depth > 0);
  449. // Create the new mip-map object now
  450. // Note: we treat clones as REF_INTERNAL; because
  451. // they are owned by the resource manager which
  452. // is owned by the device.
  453. // Also, we adjust the usage to disable lock-flags
  454. // since we don't need lockability
  455. DWORD Usage = m_desc.Usage;
  456. Usage &= ~(D3DUSAGE_LOCK | D3DUSAGE_LOADONCE);
  457. HRESULT hr;
  458. CResource *pResource = new CMipVolume(Device(),
  459. Width,
  460. Height,
  461. Depth,
  462. cLevels,
  463. Usage,
  464. m_desc.Format,
  465. Pool,
  466. REF_INTERNAL,
  467. &hr);
  468. if (pResource == NULL)
  469. {
  470. DPF_ERR("Failed to allocate mip-volume object when copying");
  471. return E_OUTOFMEMORY;
  472. }
  473. if (FAILED(hr))
  474. {
  475. DPF(5, "Failed to create mip-map when doing texture management");
  476. pResource->DecrementUseCount();
  477. return hr;
  478. }
  479. *ppResource = pResource;
  480. return hr;
  481. } // CMipVolume::Clone
  482. #undef DPF_MODNAME
  483. #define DPF_MODNAME "CMipVolume::GetBufferDesc"
  484. // Provides a method to access basic structure of the
  485. // pieces of the resource. A resource may be composed
  486. // of one or more buffers.
  487. const D3DBUFFER_DESC* CMipVolume::GetBufferDesc() const
  488. {
  489. return (const D3DBUFFER_DESC*)&m_desc;
  490. } // CMipVolume::GetBufferDesc
  491. // IUnknown methods
  492. #undef DPF_MODNAME
  493. #define DPF_MODNAME "CMipVolume::QueryInterface"
  494. STDMETHODIMP CMipVolume::QueryInterface(REFIID riid,
  495. VOID **ppvObj)
  496. {
  497. API_ENTER(Device());
  498. if (!VALID_PTR_PTR(ppvObj))
  499. {
  500. DPF_ERR("Invalid ppvObj parameter for QueryInterface of a VolumeTexture");
  501. return D3DERR_INVALIDCALL;
  502. }
  503. if (!VALID_PTR(&riid, sizeof(GUID)))
  504. {
  505. DPF_ERR("Invalid guid memory address to QueryInterface of a VolumeTexture");
  506. return D3DERR_INVALIDCALL;
  507. }
  508. if (riid == IID_IDirect3DVolumeTexture8 ||
  509. riid == IID_IDirect3DBaseTexture8 ||
  510. riid == IID_IDirect3DResource8 ||
  511. riid == IID_IUnknown)
  512. {
  513. *ppvObj = static_cast<void*>(static_cast<IDirect3DVolumeTexture8 *>(this));
  514. AddRef();
  515. return S_OK;
  516. }
  517. DPF_ERR("Unsupported Interface identifier passed to QueryInterface of a VolumeTexture");
  518. // Null out param
  519. *ppvObj = NULL;
  520. return E_NOINTERFACE;
  521. } // QueryInterface
  522. #undef DPF_MODNAME
  523. #define DPF_MODNAME "CMipVolume::AddRef"
  524. STDMETHODIMP_(ULONG) CMipVolume::AddRef()
  525. {
  526. API_ENTER_NO_LOCK(Device());
  527. return AddRefImpl();
  528. } // AddRef
  529. #undef DPF_MODNAME
  530. #define DPF_MODNAME "CMipVolume::Release"
  531. STDMETHODIMP_(ULONG) CMipVolume::Release()
  532. {
  533. API_ENTER_SUBOBJECT_RELEASE(Device());
  534. return ReleaseImpl();
  535. } // Release
  536. // IDirect3DResource methods
  537. #undef DPF_MODNAME
  538. #define DPF_MODNAME "CMipVolume::GetDevice"
  539. STDMETHODIMP CMipVolume::GetDevice(IDirect3DDevice8 **ppObj)
  540. {
  541. API_ENTER(Device());
  542. return GetDeviceImpl(ppObj);
  543. } // GetDevice
  544. #undef DPF_MODNAME
  545. #define DPF_MODNAME "CMipVolume::SetPrivateData"
  546. STDMETHODIMP CMipVolume::SetPrivateData(REFGUID riid,
  547. CONST VOID *pvData,
  548. DWORD cbData,
  549. DWORD dwFlags)
  550. {
  551. API_ENTER(Device());
  552. // For the private data that 'really' belongs to the
  553. // MipVolume, we use m_cLevels. (0 through m_cLevels-1 are for
  554. // each of the children levels.)
  555. return SetPrivateDataImpl(riid, pvData, cbData, dwFlags, m_cLevels);
  556. } // SetPrivateData
  557. #undef DPF_MODNAME
  558. #define DPF_MODNAME "CMipVolume::GetPrivateData"
  559. STDMETHODIMP CMipVolume::GetPrivateData(REFGUID riid,
  560. VOID *pvData,
  561. DWORD *pcbData)
  562. {
  563. API_ENTER(Device());
  564. // For the private data that 'really' belongs to the
  565. // MipVolume, we use m_cLevels. (0 through m_cLevels-1 are for
  566. // each of the children levels.)
  567. return GetPrivateDataImpl(riid, pvData, pcbData, m_cLevels);
  568. } // GetPrivateData
  569. #undef DPF_MODNAME
  570. #define DPF_MODNAME "CMipVolume::FreePrivateData"
  571. STDMETHODIMP CMipVolume::FreePrivateData(REFGUID riid)
  572. {
  573. API_ENTER(Device());
  574. // For the private data that 'really' belongs to the
  575. // MipVolume, we use m_cLevels. (0 through m_cLevels-1 are for
  576. // each of the children levels.)
  577. return FreePrivateDataImpl(riid, m_cLevels);
  578. } // FreePrivateData
  579. #undef DPF_MODNAME
  580. #define DPF_MODNAME "CMipVolume::GetPriority"
  581. STDMETHODIMP_(DWORD) CMipVolume::GetPriority()
  582. {
  583. API_ENTER_RET(Device(), DWORD);
  584. return GetPriorityImpl();
  585. } // GetPriority
  586. #undef DPF_MODNAME
  587. #define DPF_MODNAME "CMipVolume::SetPriority"
  588. STDMETHODIMP_(DWORD) CMipVolume::SetPriority(DWORD dwPriority)
  589. {
  590. API_ENTER_RET(Device(), DWORD);
  591. return SetPriorityImpl(dwPriority);
  592. } // SetPriority
  593. #undef DPF_MODNAME
  594. #define DPF_MODNAME "CMipVolume::PreLoad"
  595. STDMETHODIMP_(void) CMipVolume::PreLoad(void)
  596. {
  597. API_ENTER_VOID(Device());
  598. PreLoadImpl();
  599. return;
  600. } // PreLoad
  601. #undef DPF_MODNAME
  602. #define DPF_MODNAME "CMipVolume::GetType"
  603. STDMETHODIMP_(D3DRESOURCETYPE) CMipVolume::GetType(void)
  604. {
  605. API_ENTER_RET(Device(), D3DRESOURCETYPE);
  606. return m_desc.Type;
  607. } // GetType
  608. // IDirect3DMipTexture methods
  609. #undef DPF_MODNAME
  610. #define DPF_MODNAME "CMipVolume::GetLOD"
  611. STDMETHODIMP_(DWORD) CMipVolume::GetLOD()
  612. {
  613. API_ENTER_RET(Device(), DWORD);
  614. return GetLODImpl();
  615. } // GetLOD
  616. #undef DPF_MODNAME
  617. #define DPF_MODNAME "CMipVolume::SetLOD"
  618. STDMETHODIMP_(DWORD) CMipVolume::SetLOD(DWORD dwLOD)
  619. {
  620. API_ENTER_RET(Device(), DWORD);
  621. return SetLODImpl(dwLOD);
  622. } // SetLOD
  623. #undef DPF_MODNAME
  624. #define DPF_MODNAME "CMipVolume::GetLevelCount"
  625. STDMETHODIMP_(DWORD) CMipVolume::GetLevelCount()
  626. {
  627. API_ENTER_RET(Device(), DWORD);
  628. return GetLevelCountImpl();
  629. } // GetLevelCount
  630. // IDirect3DMipVolume methods
  631. #undef DPF_MODNAME
  632. #define DPF_MODNAME "CMipVolume::GetDesc"
  633. STDMETHODIMP CMipVolume::GetLevelDesc(UINT iLevel, D3DVOLUME_DESC *pDesc)
  634. {
  635. API_ENTER(Device());
  636. if (iLevel >= m_cLevels)
  637. {
  638. DPF_ERR("Invalid level number passed GetLevelDesc for a VolumeTexture");
  639. return D3DERR_INVALIDCALL;
  640. }
  641. return m_VolumeArray[iLevel]->GetDesc(pDesc);
  642. } // GetDesc
  643. #undef DPF_MODNAME
  644. #define DPF_MODNAME "CMipVolume::GetVolumeLevel"
  645. STDMETHODIMP CMipVolume::GetVolumeLevel(UINT iLevel,
  646. IDirect3DVolume8 **ppVolume)
  647. {
  648. API_ENTER(Device());
  649. if (!VALID_PTR_PTR(ppVolume))
  650. {
  651. DPF_ERR("Invalid parameter passed to GetVolumeLevel");
  652. return D3DERR_INVALIDCALL;
  653. }
  654. if (iLevel >= m_cLevels)
  655. {
  656. DPF_ERR("Invalid level number passed GetVolumeLevel");
  657. *ppVolume = NULL;
  658. return D3DERR_INVALIDCALL;
  659. }
  660. *ppVolume = m_VolumeArray[iLevel];
  661. (*ppVolume)->AddRef();
  662. return S_OK;
  663. } // GetSurfaceLevel
  664. #undef DPF_MODNAME
  665. #define DPF_MODNAME "CMipVolume::LockBox"
  666. STDMETHODIMP CMipVolume::LockBox(UINT iLevel,
  667. D3DLOCKED_BOX *pLockedBox,
  668. CONST D3DBOX *pBox,
  669. DWORD dwFlags)
  670. {
  671. API_ENTER(Device());
  672. if (iLevel >= m_cLevels)
  673. {
  674. DPF_ERR("Invalid level number passed LockBox");
  675. return D3DERR_INVALIDCALL;
  676. }
  677. return m_VolumeArray[iLevel]->LockBox(pLockedBox, pBox, dwFlags);
  678. } // LockRect
  679. #undef DPF_MODNAME
  680. #define DPF_MODNAME "CMipVolume::UnlockRect"
  681. STDMETHODIMP CMipVolume::UnlockBox(UINT iLevel)
  682. {
  683. API_ENTER(Device());
  684. if (iLevel >= m_cLevels)
  685. {
  686. DPF_ERR("Invalid level number passed UnlockBox");
  687. return D3DERR_INVALIDCALL;
  688. }
  689. return m_VolumeArray[iLevel]->UnlockBox();
  690. } // UnlockRect
  691. #undef DPF_MODNAME
  692. #define DPF_MODNAME "CMipMap::UpdateTexture"
  693. // This function does type-specific parameter checking
  694. // before calling UpdateDirtyPortion
  695. HRESULT CMipVolume::UpdateTexture(CBaseTexture *pResourceTarget)
  696. {
  697. CMipVolume *pTexSource = static_cast<CMipVolume*>(this);
  698. CMipVolume *pTexDest = static_cast<CMipVolume*>(pResourceTarget);
  699. // Figure out how many levels in the source to skip
  700. DXGASSERT(pTexSource->m_cLevels >= pTexDest->m_cLevels);
  701. DWORD StartLevel = pTexSource->m_cLevels - pTexDest->m_cLevels;
  702. DXGASSERT(StartLevel < 32);
  703. // Compute the size of the top level of the source that is
  704. // going to be copied.
  705. UINT SrcWidth = pTexSource->Desc()->Width;
  706. UINT SrcHeight = pTexSource->Desc()->Height;
  707. UINT SrcDepth = pTexSource->Desc()->Depth;
  708. if (StartLevel > 0)
  709. {
  710. SrcWidth >>= StartLevel;
  711. SrcHeight >>= StartLevel;
  712. SrcDepth >>= StartLevel;
  713. if (SrcWidth == 0)
  714. SrcWidth = 1;
  715. if (SrcHeight == 0)
  716. SrcHeight = 1;
  717. if (SrcDepth == 0)
  718. SrcDepth = 1;
  719. }
  720. // Source and Dest should be the same sizes at this point
  721. if (SrcWidth != pTexDest->Desc()->Width)
  722. {
  723. if (StartLevel)
  724. {
  725. DPF_ERR("Source and Destination for UpdateTexture are not"
  726. " compatible. Since both have the same number of"
  727. " levels; their widths must match.");
  728. }
  729. else
  730. {
  731. DPF_ERR("Source and Destination for UpdateTexture are not"
  732. " compatible. Since they have the different numbers of"
  733. " levels; the widths of the bottom-most levels of"
  734. " the source must match all the corresponding levels"
  735. " of the destination.");
  736. }
  737. return D3DERR_INVALIDCALL;
  738. }
  739. if (SrcHeight != pTexDest->Desc()->Height)
  740. {
  741. if (StartLevel)
  742. {
  743. DPF_ERR("Source and Destination for UpdateTexture are not"
  744. " compatible. Since both have the same number of"
  745. " levels; their heights must match.");
  746. }
  747. else
  748. {
  749. DPF_ERR("Source and Destination for UpdateTexture are not"
  750. " compatible. Since they have the different numbers of"
  751. " levels; the heights of the bottom-most levels of"
  752. " the source must match all the corresponding levels"
  753. " of the destination.");
  754. }
  755. return D3DERR_INVALIDCALL;
  756. }
  757. if (SrcDepth != pTexDest->Desc()->Depth)
  758. {
  759. if (StartLevel)
  760. {
  761. DPF_ERR("Source and Destination for UpdateTexture are not"
  762. " compatible. Since both have the same number of"
  763. " levels; their depths must match.");
  764. }
  765. else
  766. {
  767. DPF_ERR("Source and Destination for UpdateTexture are not"
  768. " compatible. Since they have the different numbers of"
  769. " levels; the depths of the bottom-most levels of"
  770. " the source must match all the corresponding levels"
  771. " of the destination.");
  772. }
  773. return D3DERR_INVALIDCALL;
  774. }
  775. return UpdateDirtyPortion(pResourceTarget);
  776. } // UpdateTexture
  777. #undef DPF_MODNAME
  778. #define DPF_MODNAME "CMipVolume::UpdateDirtyPortion"
  779. // Tells the resource that it should copy itself
  780. // to the target. It is the caller's responsibility
  781. // to make sure that Target is compatible with the
  782. // Source. (The Target may have different number of mip-levels
  783. // and be in a different pool; however, it must have the same size,
  784. // faces, format, etc.)
  785. //
  786. // This function will clear the dirty state.
  787. HRESULT CMipVolume::UpdateDirtyPortion(CResource *pResourceTarget)
  788. {
  789. HRESULT hr;
  790. // If we are clean, then do nothing
  791. if (m_cBoxUsed == 0)
  792. {
  793. if (IsDirty())
  794. {
  795. DPF_ERR("A volume texture has been locked with D3DLOCK_NO_DIRTY_UPDATE but "
  796. "no call to AddDirtyBox was made before the texture was used. "
  797. "Hardware texture was not updated.");
  798. }
  799. return S_OK;
  800. }
  801. // We are dirty; so we need to get some pointers
  802. CMipVolume *pTexSource = static_cast<CMipVolume*>(this);
  803. CMipVolume *pTexDest = static_cast<CMipVolume*>(pResourceTarget);
  804. if (CanTexBlt(pTexDest))
  805. {
  806. CD3DBase *pDevice = static_cast<CD3DBase*>(Device());
  807. if (m_cBoxUsed == MIPVOLUME_ALLDIRTY)
  808. {
  809. D3DBOX box;
  810. box.Left = 0;
  811. box.Right = Desc()->Width;
  812. box.Top = 0;
  813. box.Bottom = Desc()->Height;
  814. box.Front = 0;
  815. box.Back = Desc()->Depth;
  816. hr = pDevice->VolBlt(pTexDest,
  817. pTexSource,
  818. 0, 0, 0, // XYZ offset
  819. &box);
  820. if (FAILED(hr))
  821. {
  822. DPF_ERR("Failed to update volume texture; not clearing dirty state");
  823. return hr;
  824. }
  825. }
  826. else
  827. {
  828. DXGASSERT(m_cBoxUsed < MIPVOLUME_ALLDIRTY);
  829. for (DWORD i = 0; i < m_cBoxUsed; i++)
  830. {
  831. hr = pDevice->VolBlt(pTexDest,
  832. pTexSource,
  833. m_DirtyBoxArray[i].Left,
  834. m_DirtyBoxArray[i].Top,
  835. m_DirtyBoxArray[i].Front,
  836. &m_DirtyBoxArray[i]);
  837. if (FAILED(hr))
  838. {
  839. DPF_ERR("Failed to update volume texture; not clearing dirty state");
  840. return hr;
  841. }
  842. }
  843. }
  844. // Remember that we did the work
  845. m_cBoxUsed = 0;
  846. return S_OK;
  847. }
  848. // We can't use TexBlt, so we have to copy each level individually
  849. // with Lock and Copy
  850. // Determine number of source levels to skip
  851. DXGASSERT(pTexSource->m_cLevels >= pTexDest->m_cLevels);
  852. DWORD StartLevel = pTexSource->m_cLevels - pTexDest->m_cLevels;
  853. DWORD LevelsToCopy = pTexSource->m_cLevels - StartLevel;
  854. // Sanity check
  855. DXGASSERT(LevelsToCopy > 0);
  856. // Get the volume desc of the top level to copy
  857. D3DVOLUME_DESC desc;
  858. hr = pTexDest->GetLevelDesc(0, &desc);
  859. DXGASSERT(SUCCEEDED(hr));
  860. BOOL IsAllDirty = FALSE;
  861. if (m_cBoxUsed == MIPVOLUME_ALLDIRTY)
  862. {
  863. m_cBoxUsed = 1;
  864. m_DirtyBoxArray[0].Left = 0;
  865. m_DirtyBoxArray[0].Right = m_desc.Width >> StartLevel;
  866. m_DirtyBoxArray[0].Top = 0;
  867. m_DirtyBoxArray[0].Bottom = m_desc.Height >> StartLevel;
  868. m_DirtyBoxArray[0].Front = 0;
  869. m_DirtyBoxArray[0].Back = m_desc.Depth >> StartLevel;
  870. IsAllDirty = TRUE;
  871. }
  872. // Determine pixel/block size and make some
  873. // adjustments if necessary
  874. // cbPixel is size of pixel or (if negative)
  875. // a special value for use with AdjustForDXT
  876. UINT cbPixel = CPixel::ComputePixelStride(desc.Format);
  877. if (CPixel::IsDXT(cbPixel))
  878. {
  879. BOOL IsVolumeDXT = CPixel::IsVolumeDXT(desc.Format);
  880. // Adjust dirty rect coords from pixels into blocks
  881. for (DWORD iBox = 0; iBox < m_cBoxUsed; iBox++)
  882. {
  883. // Basically we just need to round the value
  884. // down by 2 powers-of-two. (left/top get rounded
  885. // down, right/bottom get rounded up)
  886. if (IsVolumeDXT)
  887. {
  888. ScaleBoxDown(&m_DirtyBoxArray[iBox], 2);
  889. }
  890. else
  891. {
  892. ScaleRectDown((RECT *)&m_DirtyBoxArray[iBox], 2);
  893. }
  894. }
  895. // Adjust width/height from pixels into blocks
  896. if (IsVolumeDXT)
  897. {
  898. CPixel::AdjustForVolumeDXT(&desc.Width,
  899. &desc.Height,
  900. &desc.Depth,
  901. &cbPixel);
  902. }
  903. else
  904. {
  905. CPixel::AdjustForDXT(&desc.Width, &desc.Height, &cbPixel);
  906. }
  907. }
  908. // cbPixel is now the size of a pixel (or of a block if we've
  909. // converted into DXT block space)
  910. // We need to copy each volume piece by piece
  911. for (DWORD Level = 0; Level < LevelsToCopy; Level++)
  912. {
  913. CVolume *pVolumeSrc;
  914. CVolume *pVolumeDst;
  915. DXGASSERT(Level + StartLevel < pTexSource->m_cLevels);
  916. pVolumeSrc = pTexSource->m_VolumeArray[Level + StartLevel];
  917. DXGASSERT(Level < pTexDest->m_cLevels);
  918. pVolumeDst = pTexDest->m_VolumeArray[Level];
  919. D3DLOCKED_BOX SrcBox;
  920. D3DLOCKED_BOX DstBox;
  921. // Lock the whole source
  922. hr = pVolumeSrc->InternalLockBox(&SrcBox,
  923. NULL,
  924. D3DLOCK_READONLY);
  925. if (FAILED(hr))
  926. {
  927. DPF_ERR("Failed to update volume texture; not clearing dirty state");
  928. return hr;
  929. }
  930. // Lock the whole dest
  931. hr = pVolumeDst->InternalLockBox(&DstBox,
  932. NULL,
  933. 0);
  934. if (FAILED(hr))
  935. {
  936. pVolumeSrc->InternalUnlockBox();
  937. DPF_ERR("Failed to update volume texture; not clearing dirty state");
  938. return hr;
  939. }
  940. // Can we do this with one big memcpy, or do we need
  941. // to break it up?
  942. if (IsAllDirty &&
  943. (SrcBox.RowPitch == DstBox.RowPitch) &&
  944. (SrcBox.SlicePitch == DstBox.SlicePitch) &&
  945. (SrcBox.RowPitch == (int)(desc.Width * cbPixel)) &&
  946. (SrcBox.SlicePitch == (int)(SrcBox.RowPitch * desc.Height)))
  947. {
  948. BYTE *pSrc = (BYTE*) SrcBox.pBits;
  949. BYTE *pDst = (BYTE*) DstBox.pBits;
  950. memcpy(pDst, pSrc, SrcBox.SlicePitch * desc.Depth);
  951. }
  952. else
  953. {
  954. // Copy each dirty box one by one
  955. for (DWORD iBox = 0; iBox < m_cBoxUsed; iBox++)
  956. {
  957. D3DBOX *pBox = &m_DirtyBoxArray[iBox];
  958. BYTE *pSrc = (BYTE*) SrcBox.pBits;
  959. pSrc += pBox->Front * SrcBox.SlicePitch;
  960. pSrc += pBox->Top * SrcBox.RowPitch;
  961. pSrc += pBox->Left * cbPixel;
  962. BYTE *pDst = (BYTE*) DstBox.pBits;
  963. pDst += pBox->Front * DstBox.SlicePitch;
  964. pDst += pBox->Top * DstBox.RowPitch;
  965. pDst += pBox->Left * cbPixel;
  966. for (DWORD i = pBox->Front; i < pBox->Back; i++)
  967. {
  968. BYTE *pDepthDst = pDst;
  969. BYTE *pDepthSrc = pSrc;
  970. DWORD cbSpan = cbPixel * (pBox->Right - pBox->Left);
  971. for (DWORD j = pBox->Top; j < pBox->Bottom; j++)
  972. {
  973. memcpy(pDst, pSrc, cbSpan);
  974. pDst += DstBox.RowPitch;
  975. pSrc += SrcBox.RowPitch;
  976. }
  977. pDst = pDepthDst + DstBox.SlicePitch;
  978. pSrc = pDepthSrc + SrcBox.SlicePitch;
  979. }
  980. }
  981. }
  982. // Release our locks
  983. hr = pVolumeDst->InternalUnlockBox();
  984. DXGASSERT(SUCCEEDED(hr));
  985. hr = pVolumeSrc->InternalUnlockBox();
  986. DXGASSERT(SUCCEEDED(hr));
  987. // Is the last one?
  988. if (Level+1 < LevelsToCopy)
  989. {
  990. // Shrink the desc
  991. desc.Width >>= 1;
  992. if (desc.Width == 0)
  993. desc.Width = 1;
  994. desc.Height >>= 1;
  995. if (desc.Height == 0)
  996. desc.Height = 1;
  997. desc.Depth >>= 1;
  998. if (desc.Depth == 0)
  999. desc.Depth = 1;
  1000. // Shrink the boxes
  1001. for (DWORD iBox = 0; iBox < m_cBoxUsed; iBox++)
  1002. {
  1003. ScaleBoxDown(&m_DirtyBoxArray[iBox]);
  1004. }
  1005. }
  1006. }
  1007. if (FAILED(hr))
  1008. {
  1009. DPF_ERR("Failed to update volume texture; not clearing dirty state");
  1010. return hr;
  1011. }
  1012. // Remember that we did the work
  1013. m_cBoxUsed = 0;
  1014. // Notify Resource base class that we are now clean
  1015. OnResourceClean();
  1016. DXGASSERT(!IsDirty());
  1017. return S_OK;
  1018. } // CMipVolume::UpdateDirtyPortion
  1019. #undef DPF_MODNAME
  1020. #define DPF_MODNAME "CMipVolume::MarkAllDirty"
  1021. // Allows the Resource Manager to mark the texture
  1022. // as needing to be completely updated on next
  1023. // call to UpdateDirtyPortion
  1024. void CMipVolume::MarkAllDirty()
  1025. {
  1026. // Set palette to __INVALIDPALETTE so that UpdateTextures
  1027. // calls the DDI SetPalette the next time.
  1028. SetPalette(__INVALIDPALETTE);
  1029. // Send dirty notification
  1030. m_cBoxUsed = MIPVOLUME_ALLDIRTY;
  1031. // Notify Resource base class that we are now dirty
  1032. OnResourceDirty();
  1033. return;
  1034. } // CMipVolume::MarkAllDirty
  1035. #undef DPF_MODNAME
  1036. #define DPF_MODNAME "CMipVolume::OnVolumeLock"
  1037. // Methods for the Volumes to call
  1038. // Notification when a mip-level is locked for writing
  1039. void CMipVolume::OnVolumeLock(DWORD iLevel, CONST D3DBOX *pBox, DWORD dwFlags)
  1040. {
  1041. // Need to Sync first
  1042. Sync();
  1043. // We only care about the top-most level of the mip-map
  1044. if (iLevel != 0)
  1045. {
  1046. return;
  1047. }
  1048. // Send dirty notification
  1049. OnResourceDirty();
  1050. // If we're not all dirty or if the lock specifies
  1051. // that we don't keep track of the lock then
  1052. // remember the box
  1053. if (m_cBoxUsed != MIPVOLUME_ALLDIRTY &&
  1054. !(dwFlags & D3DLOCK_NO_DIRTY_UPDATE))
  1055. {
  1056. InternalAddDirtyBox(pBox);
  1057. }
  1058. return;
  1059. } // CMipVolume::OnVolumeLock
  1060. #undef DPF_MODNAME
  1061. #define DPF_MODNAME "CMipVolume::AddDirtyBox"
  1062. STDMETHODIMP CMipVolume::AddDirtyBox(CONST D3DBOX *pBox)
  1063. {
  1064. API_ENTER(Device());
  1065. if (pBox != NULL && !VALID_PTR(pBox, sizeof(D3DBOX)))
  1066. {
  1067. DPF_ERR("Invalid parameter to AddDirtyBox");
  1068. return D3DERR_INVALIDCALL;
  1069. }
  1070. if (pBox)
  1071. {
  1072. if (!CPixel::IsValidBox(Desc()->Format,
  1073. Desc()->Width,
  1074. Desc()->Height,
  1075. Desc()->Depth,
  1076. pBox))
  1077. {
  1078. DPF_ERR("AddDirtyBox for a Volume Texture failed");
  1079. return D3DERR_INVALIDCALL;
  1080. }
  1081. }
  1082. InternalAddDirtyBox(pBox);
  1083. return S_OK;
  1084. } // AddDirtyBox
  1085. #undef DPF_MODNAME
  1086. #define DPF_MODNAME "CMipVolume::InternalAddDirtyBox"
  1087. void CMipVolume::InternalAddDirtyBox(CONST D3DBOX *pBox)
  1088. {
  1089. // If driver managed then batch token
  1090. if (Desc()->Pool == D3DPOOL_MANAGED && !IsD3DManaged())
  1091. {
  1092. D3DBOX Box;
  1093. DXGASSERT((Device()->GetD3DCaps()->Caps2 & DDCAPS2_CANMANAGERESOURCE) != 0);
  1094. if (pBox == NULL)
  1095. {
  1096. Box.Left = 0;
  1097. Box.Top = 0;
  1098. Box.Front = 0;
  1099. Box.Right = Desc()->Width;
  1100. Box.Bottom = Desc()->Height;
  1101. Box.Back = Desc()->Depth;
  1102. }
  1103. else
  1104. {
  1105. Box = *pBox;
  1106. }
  1107. static_cast<CD3DBase*>(Device())->AddDirtyBox(this, &Box); // This will fail only due to catastrophic
  1108. // error and we or the app can't do a
  1109. // a whole lot about it, so return nothing
  1110. return;
  1111. }
  1112. // Need to mark dirty bit in CResource so that the resource manager works correctly.
  1113. OnResourceDirty();
  1114. // If everything is being modified; then we're totally dirty
  1115. if (pBox == NULL)
  1116. {
  1117. m_cBoxUsed = MIPVOLUME_ALLDIRTY;
  1118. return;
  1119. }
  1120. // If we're all dirty, we can't get dirtier
  1121. if (m_cBoxUsed == MIPVOLUME_ALLDIRTY)
  1122. {
  1123. return;
  1124. }
  1125. // If the rect is the entire surface then we're all dirty
  1126. DXGASSERT(pBox != NULL);
  1127. if (pBox->Left == 0 &&
  1128. pBox->Top == 0 &&
  1129. pBox->Front == 0 &&
  1130. pBox->Right == Desc()->Width &&
  1131. pBox->Bottom == Desc()->Height &&
  1132. pBox->Back == Desc()->Depth)
  1133. {
  1134. m_cBoxUsed = MIPVOLUME_ALLDIRTY;
  1135. return;
  1136. }
  1137. // If we have filled up our boxes; then we're also all dirty now
  1138. if (m_cBoxUsed == MIPVOLUME_MAXDIRTYBOX)
  1139. {
  1140. m_cBoxUsed = MIPVOLUME_ALLDIRTY;
  1141. return;
  1142. }
  1143. // Remember this rect
  1144. DXGASSERT(m_cBoxUsed < MIPVOLUME_MAXDIRTYBOX);
  1145. DXGASSERT(pBox != NULL);
  1146. m_DirtyBoxArray[m_cBoxUsed] = *pBox;
  1147. m_cBoxUsed++;
  1148. // We're done now.
  1149. return;
  1150. } // InternalAddDirtyBox
  1151. #undef DPF_MODNAME
  1152. #define DPF_MODNAME "CMipVolume::IsTextureLocked"
  1153. // Debug only parameter checking do determine if a piece
  1154. // of a mip-chain is locked
  1155. #ifdef DEBUG
  1156. BOOL CMipVolume::IsTextureLocked()
  1157. {
  1158. for (UINT iLevel = 0; iLevel < m_cLevels; iLevel++)
  1159. {
  1160. if (m_VolumeArray[iLevel]->IsLocked())
  1161. return TRUE;
  1162. }
  1163. return FALSE;
  1164. } // IsTextureLocked
  1165. #endif // !DEBUG
  1166. // End of file : mipvol.cpp