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.

1377 lines
42 KiB

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