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.

1303 lines
38 KiB

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