1 //
2 // Copyright (c) 2012-2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // TextureStorage9.cpp: Implements the abstract rx::TextureStorage9 class and its concrete derived
8 // classes TextureStorage9_2D and TextureStorage9_Cube, which act as the interface to the
9 // D3D9 texture.
10 
11 #include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h"
12 
13 #include "libANGLE/formatutils.h"
14 #include "libANGLE/Texture.h"
15 #include "libANGLE/renderer/d3d/EGLImageD3D.h"
16 #include "libANGLE/renderer/d3d/TextureD3D.h"
17 #include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
18 #include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h"
19 #include "libANGLE/renderer/d3d/d3d9/Renderer9.h"
20 #include "libANGLE/renderer/d3d/d3d9/RenderTarget9.h"
21 #include "libANGLE/renderer/d3d/d3d9/SwapChain9.h"
22 
23 namespace rx
24 {
TextureStorage9(Renderer9 * renderer,DWORD usage)25 TextureStorage9::TextureStorage9(Renderer9 *renderer, DWORD usage)
26     : mTopLevel(0),
27       mMipLevels(0),
28       mTextureWidth(0),
29       mTextureHeight(0),
30       mInternalFormat(GL_NONE),
31       mTextureFormat(D3DFMT_UNKNOWN),
32       mRenderer(renderer),
33       mD3DUsage(usage),
34       mD3DPool(mRenderer->getTexturePool(usage))
35 {
36 }
37 
~TextureStorage9()38 TextureStorage9::~TextureStorage9()
39 {
40 }
41 
GetTextureUsage(GLenum internalformat,bool renderTarget)42 DWORD TextureStorage9::GetTextureUsage(GLenum internalformat, bool renderTarget)
43 {
44     DWORD d3dusage = 0;
45 
46     const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
47     const d3d9::TextureFormat &d3dFormatInfo = d3d9::GetTextureFormatInfo(internalformat);
48     if (formatInfo.depthBits > 0 || formatInfo.stencilBits > 0)
49     {
50         d3dusage |= D3DUSAGE_DEPTHSTENCIL;
51     }
52     else if (renderTarget && (d3dFormatInfo.renderFormat != D3DFMT_UNKNOWN))
53     {
54         d3dusage |= D3DUSAGE_RENDERTARGET;
55     }
56 
57     return d3dusage;
58 }
59 
60 
isRenderTarget() const61 bool TextureStorage9::isRenderTarget() const
62 {
63     return (mD3DUsage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0;
64 }
65 
isManaged() const66 bool TextureStorage9::isManaged() const
67 {
68     return (mD3DPool == D3DPOOL_MANAGED);
69 }
70 
supportsNativeMipmapFunction() const71 bool TextureStorage9::supportsNativeMipmapFunction() const
72 {
73     return false;
74 }
75 
getPool() const76 D3DPOOL TextureStorage9::getPool() const
77 {
78     return mD3DPool;
79 }
80 
getUsage() const81 DWORD TextureStorage9::getUsage() const
82 {
83     return mD3DUsage;
84 }
85 
getTopLevel() const86 int TextureStorage9::getTopLevel() const
87 {
88     return mTopLevel;
89 }
90 
getLevelCount() const91 int TextureStorage9::getLevelCount() const
92 {
93     return static_cast<int>(mMipLevels) - mTopLevel;
94 }
95 
setData(const gl::ImageIndex & index,ImageD3D * image,const gl::Box * destBox,GLenum type,const gl::PixelUnpackState & unpack,const uint8_t * pixelData)96 gl::Error TextureStorage9::setData(const gl::ImageIndex &index, ImageD3D *image, const gl::Box *destBox, GLenum type,
97                                    const gl::PixelUnpackState &unpack, const uint8_t *pixelData)
98 {
99     UNREACHABLE();
100     return gl::Error(GL_INVALID_OPERATION);
101 }
102 
TextureStorage9_2D(Renderer9 * renderer,SwapChain9 * swapchain)103 TextureStorage9_2D::TextureStorage9_2D(Renderer9 *renderer, SwapChain9 *swapchain)
104     : TextureStorage9(renderer, D3DUSAGE_RENDERTARGET)
105 {
106     IDirect3DTexture9 *surfaceTexture = swapchain->getOffscreenTexture();
107     mTexture = surfaceTexture;
108     mMipLevels = surfaceTexture->GetLevelCount();
109 
110     mInternalFormat = swapchain->getRenderTargetInternalFormat();
111 
112     D3DSURFACE_DESC surfaceDesc;
113     surfaceTexture->GetLevelDesc(0, &surfaceDesc);
114     mTextureWidth = surfaceDesc.Width;
115     mTextureHeight = surfaceDesc.Height;
116     mTextureFormat = surfaceDesc.Format;
117 
118     mRenderTargets.resize(mMipLevels, nullptr);
119 }
120 
TextureStorage9_2D(Renderer9 * renderer,GLenum internalformat,bool renderTarget,GLsizei width,GLsizei height,int levels)121 TextureStorage9_2D::TextureStorage9_2D(Renderer9 *renderer, GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels)
122     : TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget))
123 {
124     mTexture = NULL;
125 
126     mInternalFormat = internalformat;
127 
128     const d3d9::TextureFormat &d3dFormatInfo = d3d9::GetTextureFormatInfo(internalformat);
129     mTextureFormat = d3dFormatInfo.texFormat;
130 
131     d3d9::MakeValidSize(false, d3dFormatInfo.texFormat, &width, &height, &mTopLevel);
132     mTextureWidth = width;
133     mTextureHeight = height;
134     mMipLevels = mTopLevel + levels;
135 
136     mRenderTargets.resize(levels, nullptr);
137 }
138 
~TextureStorage9_2D()139 TextureStorage9_2D::~TextureStorage9_2D()
140 {
141     SafeRelease(mTexture);
142     for (auto &renderTarget : mRenderTargets)
143     {
144         SafeDelete(renderTarget);
145     }
146 }
147 
148 // Increments refcount on surface.
149 // caller must Release() the returned surface
getSurfaceLevel(GLenum target,int level,bool dirty,IDirect3DSurface9 ** outSurface)150 gl::Error TextureStorage9_2D::getSurfaceLevel(GLenum target,
151                                               int level,
152                                               bool dirty,
153                                               IDirect3DSurface9 **outSurface)
154 {
155     ASSERT(target == GL_TEXTURE_2D);
156 
157     IDirect3DBaseTexture9 *baseTexture = NULL;
158     gl::Error error = getBaseTexture(&baseTexture);
159     if (error.isError())
160     {
161         return error;
162     }
163 
164     IDirect3DTexture9 *texture = static_cast<IDirect3DTexture9*>(baseTexture);
165 
166     HRESULT result = texture->GetSurfaceLevel(level + mTopLevel, outSurface);
167 
168     ASSERT(SUCCEEDED(result));
169     if (FAILED(result))
170     {
171         return gl::Error(GL_OUT_OF_MEMORY, "Failed to get the surface from a texture, result: 0x%X.", result);
172     }
173 
174     // With managed textures the driver needs to be informed of updates to the lower mipmap levels
175     if (level + mTopLevel != 0 && isManaged() && dirty)
176     {
177         texture->AddDirtyRect(NULL);
178     }
179 
180     return gl::Error(GL_NO_ERROR);
181 }
182 
getRenderTarget(const gl::ImageIndex & index,RenderTargetD3D ** outRT)183 gl::Error TextureStorage9_2D::getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT)
184 {
185     ASSERT(index.mipIndex < getLevelCount());
186 
187     if (!mRenderTargets[index.mipIndex] && isRenderTarget())
188     {
189         IDirect3DBaseTexture9 *baseTexture = NULL;
190         gl::Error error = getBaseTexture(&baseTexture);
191         if (error.isError())
192         {
193             return error;
194         }
195 
196         IDirect3DSurface9 *surface = NULL;
197         error = getSurfaceLevel(GL_TEXTURE_2D, index.mipIndex, false, &surface);
198         if (error.isError())
199         {
200             return error;
201         }
202 
203         size_t textureMipLevel = mTopLevel + index.mipIndex;
204         size_t mipWidth        = std::max<size_t>(mTextureWidth >> textureMipLevel, 1u);
205         size_t mipHeight       = std::max<size_t>(mTextureHeight >> textureMipLevel, 1u);
206 
207         baseTexture->AddRef();
208         mRenderTargets[index.mipIndex] = new TextureRenderTarget9(
209             baseTexture, textureMipLevel, surface, mInternalFormat, static_cast<GLsizei>(mipWidth),
210             static_cast<GLsizei>(mipHeight), 1, 0);
211     }
212 
213     ASSERT(outRT);
214     *outRT = mRenderTargets[index.mipIndex];
215     return gl::Error(GL_NO_ERROR);
216 }
217 
generateMipmap(const gl::ImageIndex & sourceIndex,const gl::ImageIndex & destIndex)218 gl::Error TextureStorage9_2D::generateMipmap(const gl::ImageIndex &sourceIndex, const gl::ImageIndex &destIndex)
219 {
220     IDirect3DSurface9 *upper = NULL;
221     gl::Error error = getSurfaceLevel(GL_TEXTURE_2D, sourceIndex.mipIndex, false, &upper);
222     if (error.isError())
223     {
224         return error;
225     }
226 
227     IDirect3DSurface9 *lower = NULL;
228     error = getSurfaceLevel(GL_TEXTURE_2D, destIndex.mipIndex, true, &lower);
229     if (error.isError())
230     {
231         SafeRelease(upper);
232         return error;
233     }
234 
235     ASSERT(upper && lower);
236     error = mRenderer->boxFilter(upper, lower);
237 
238     SafeRelease(upper);
239     SafeRelease(lower);
240 
241     return error;
242 }
243 
getBaseTexture(IDirect3DBaseTexture9 ** outTexture)244 gl::Error TextureStorage9_2D::getBaseTexture(IDirect3DBaseTexture9 **outTexture)
245 {
246     // if the width or height is not positive this should be treated as an incomplete texture
247     // we handle that here by skipping the d3d texture creation
248     if (mTexture == NULL && mTextureWidth > 0 && mTextureHeight > 0)
249     {
250         ASSERT(mMipLevels > 0);
251 
252         IDirect3DDevice9 *device = mRenderer->getDevice();
253         HRESULT result = device->CreateTexture(static_cast<unsigned int>(mTextureWidth),
254                                                static_cast<unsigned int>(mTextureHeight),
255                                                static_cast<unsigned int>(mMipLevels), getUsage(),
256                                                mTextureFormat, getPool(), &mTexture, NULL);
257 
258         if (FAILED(result))
259         {
260             ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
261             return gl::Error(GL_OUT_OF_MEMORY, "Failed to create 2D storage texture, result: 0x%X.", result);
262         }
263     }
264 
265     *outTexture = mTexture;
266     return gl::Error(GL_NO_ERROR);
267 }
268 
copyToStorage(TextureStorage * destStorage)269 gl::Error TextureStorage9_2D::copyToStorage(TextureStorage *destStorage)
270 {
271     ASSERT(destStorage);
272 
273     TextureStorage9_2D *dest9 = GetAs<TextureStorage9_2D>(destStorage);
274 
275     int levels = getLevelCount();
276     for (int i = 0; i < levels; ++i)
277     {
278         IDirect3DSurface9 *srcSurf = NULL;
279         gl::Error error = getSurfaceLevel(GL_TEXTURE_2D, i, false, &srcSurf);
280         if (error.isError())
281         {
282             return error;
283         }
284 
285         IDirect3DSurface9 *dstSurf = NULL;
286         error = dest9->getSurfaceLevel(GL_TEXTURE_2D, i, true, &dstSurf);
287         if (error.isError())
288         {
289             SafeRelease(srcSurf);
290             return error;
291         }
292 
293         error = mRenderer->copyToRenderTarget(dstSurf, srcSurf, isManaged());
294 
295         SafeRelease(srcSurf);
296         SafeRelease(dstSurf);
297 
298         if (error.isError())
299         {
300             return error;
301         }
302     }
303 
304     return gl::Error(GL_NO_ERROR);
305 }
306 
TextureStorage9_EGLImage(Renderer9 * renderer,EGLImageD3D * image,RenderTarget9 * renderTarget9)307 TextureStorage9_EGLImage::TextureStorage9_EGLImage(Renderer9 *renderer,
308                                                    EGLImageD3D *image,
309                                                    RenderTarget9 *renderTarget9)
310     : TextureStorage9(renderer, D3DUSAGE_RENDERTARGET), mImage(image)
311 {
312     mInternalFormat = renderTarget9->getInternalFormat();
313     mTextureFormat  = renderTarget9->getD3DFormat();
314     mTextureWidth   = renderTarget9->getWidth();
315     mTextureHeight  = renderTarget9->getHeight();
316     mTopLevel       = static_cast<int>(renderTarget9->getTextureLevel());
317     mMipLevels      = mTopLevel + 1;
318 }
319 
~TextureStorage9_EGLImage()320 TextureStorage9_EGLImage::~TextureStorage9_EGLImage()
321 {
322 }
323 
getSurfaceLevel(GLenum target,int level,bool,IDirect3DSurface9 ** outSurface)324 gl::Error TextureStorage9_EGLImage::getSurfaceLevel(GLenum target,
325                                                     int level,
326                                                     bool,
327                                                     IDirect3DSurface9 **outSurface)
328 {
329     ASSERT(target == GL_TEXTURE_2D);
330     ASSERT(level == 0);
331 
332     RenderTargetD3D *renderTargetD3D = nullptr;
333     gl::Error error = mImage->getRenderTarget(&renderTargetD3D);
334     if (error.isError())
335     {
336         return error;
337     }
338 
339     RenderTarget9 *renderTarget9 = GetAs<RenderTarget9>(renderTargetD3D);
340 
341     *outSurface = renderTarget9->getSurface();
342     return gl::Error(GL_NO_ERROR);
343 }
344 
getRenderTarget(const gl::ImageIndex & index,RenderTargetD3D ** outRT)345 gl::Error TextureStorage9_EGLImage::getRenderTarget(const gl::ImageIndex &index,
346                                                     RenderTargetD3D **outRT)
347 {
348     ASSERT(!index.hasLayer());
349     ASSERT(index.mipIndex == 0);
350 
351     return mImage->getRenderTarget(outRT);
352 }
353 
getBaseTexture(IDirect3DBaseTexture9 ** outTexture)354 gl::Error TextureStorage9_EGLImage::getBaseTexture(IDirect3DBaseTexture9 **outTexture)
355 {
356     RenderTargetD3D *renderTargetD3D = nullptr;
357     gl::Error error = mImage->getRenderTarget(&renderTargetD3D);
358     if (error.isError())
359     {
360         return error;
361     }
362 
363     RenderTarget9 *renderTarget9 = GetAs<RenderTarget9>(renderTargetD3D);
364     *outTexture = renderTarget9->getTexture();
365     ASSERT(*outTexture != nullptr);
366 
367     return gl::Error(GL_NO_ERROR);
368 }
369 
generateMipmap(const gl::ImageIndex &,const gl::ImageIndex &)370 gl::Error TextureStorage9_EGLImage::generateMipmap(const gl::ImageIndex &, const gl::ImageIndex &)
371 {
372     UNREACHABLE();
373     return gl::Error(GL_INVALID_OPERATION);
374 }
375 
copyToStorage(TextureStorage * destStorage)376 gl::Error TextureStorage9_EGLImage::copyToStorage(TextureStorage *destStorage)
377 {
378     ASSERT(destStorage);
379     ASSERT(getLevelCount() == 1);
380 
381     TextureStorage9 *dest9 = GetAs<TextureStorage9>(destStorage);
382 
383     IDirect3DBaseTexture9 *destBaseTexture9 = nullptr;
384     gl::Error error = dest9->getBaseTexture(&destBaseTexture9);
385     if (error.isError())
386     {
387         return error;
388     }
389 
390     IDirect3DTexture9 *destTexture9 = static_cast<IDirect3DTexture9 *>(destBaseTexture9);
391 
392     IDirect3DSurface9 *destSurface = nullptr;
393     HRESULT result = destTexture9->GetSurfaceLevel(destStorage->getTopLevel(), &destSurface);
394     if (FAILED(result))
395     {
396         return gl::Error(GL_OUT_OF_MEMORY,
397                          "Failed to get the surface from a texture, result: 0x%X.", result);
398     }
399 
400     RenderTargetD3D *sourceRenderTarget = nullptr;
401     error = mImage->getRenderTarget(&sourceRenderTarget);
402     if (error.isError())
403     {
404         SafeRelease(destSurface);
405         return error;
406     }
407 
408     RenderTarget9 *sourceRenderTarget9 = GetAs<RenderTarget9>(sourceRenderTarget);
409     error =
410         mRenderer->copyToRenderTarget(destSurface, sourceRenderTarget9->getSurface(), isManaged());
411     if (error.isError())
412     {
413         SafeRelease(destSurface);
414         return error;
415     }
416 
417     if (destStorage->getTopLevel() != 0)
418     {
419         destTexture9->AddDirtyRect(nullptr);
420     }
421 
422     SafeRelease(destSurface);
423     return gl::Error(GL_NO_ERROR);
424 }
425 
TextureStorage9_Cube(Renderer9 * renderer,GLenum internalformat,bool renderTarget,int size,int levels,bool hintLevelZeroOnly)426 TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
427     : TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget))
428 {
429     mTexture = NULL;
430     for (size_t i = 0; i < CUBE_FACE_COUNT; ++i)
431     {
432         mRenderTarget[i] = NULL;
433     }
434 
435     mInternalFormat = internalformat;
436 
437     const d3d9::TextureFormat &d3dFormatInfo = d3d9::GetTextureFormatInfo(internalformat);
438     mTextureFormat = d3dFormatInfo.texFormat;
439 
440     int height = size;
441     d3d9::MakeValidSize(false, d3dFormatInfo.texFormat, &size, &height, &mTopLevel);
442     mTextureWidth = size;
443     mTextureHeight = size;
444     mMipLevels = mTopLevel + levels;
445 }
446 
~TextureStorage9_Cube()447 TextureStorage9_Cube::~TextureStorage9_Cube()
448 {
449     SafeRelease(mTexture);
450 
451     for (size_t i = 0; i < CUBE_FACE_COUNT; ++i)
452     {
453         SafeDelete(mRenderTarget[i]);
454     }
455 }
456 
457 // Increments refcount on surface.
458 // caller must Release() the returned surface
getSurfaceLevel(GLenum target,int level,bool dirty,IDirect3DSurface9 ** outSurface)459 gl::Error TextureStorage9_Cube::getSurfaceLevel(GLenum target,
460                                                 int level,
461                                                 bool dirty,
462                                                 IDirect3DSurface9 **outSurface)
463 {
464     IDirect3DBaseTexture9 *baseTexture = NULL;
465     gl::Error error = getBaseTexture(&baseTexture);
466     if (error.isError())
467     {
468         return error;
469     }
470 
471     IDirect3DCubeTexture9 *texture = static_cast<IDirect3DCubeTexture9*>(baseTexture);
472 
473     D3DCUBEMAP_FACES face = gl_d3d9::ConvertCubeFace(target);
474     HRESULT result        = texture->GetCubeMapSurface(face, level, outSurface);
475 
476     ASSERT(SUCCEEDED(result));
477     if (FAILED(result))
478     {
479         return gl::Error(GL_OUT_OF_MEMORY, "Failed to get the surface from a texture, result: 0x%X.", result);
480     }
481 
482     // With managed textures the driver needs to be informed of updates to the lower mipmap levels
483     if (level != 0 && isManaged() && dirty)
484     {
485         texture->AddDirtyRect(face, NULL);
486     }
487 
488     return gl::Error(GL_NO_ERROR);
489 }
490 
getRenderTarget(const gl::ImageIndex & index,RenderTargetD3D ** outRT)491 gl::Error TextureStorage9_Cube::getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT)
492 {
493     ASSERT(outRT);
494     ASSERT(index.mipIndex == 0);
495     ASSERT(index.layerIndex >= 0 && static_cast<size_t>(index.layerIndex) < CUBE_FACE_COUNT);
496 
497     if (mRenderTarget[index.layerIndex] == NULL && isRenderTarget())
498     {
499         IDirect3DBaseTexture9 *baseTexture = NULL;
500         gl::Error error = getBaseTexture(&baseTexture);
501         if (error.isError())
502         {
503             return error;
504         }
505 
506         IDirect3DSurface9 *surface = NULL;
507         error = getSurfaceLevel(GL_TEXTURE_CUBE_MAP_POSITIVE_X + index.layerIndex,
508                                 mTopLevel + index.mipIndex, false, &surface);
509         if (error.isError())
510         {
511             return error;
512         }
513 
514         baseTexture->AddRef();
515         mRenderTarget[index.layerIndex] = new TextureRenderTarget9(
516             baseTexture, mTopLevel + index.mipIndex, surface, mInternalFormat,
517             static_cast<GLsizei>(mTextureWidth), static_cast<GLsizei>(mTextureHeight), 1, 0);
518     }
519 
520     *outRT = mRenderTarget[index.layerIndex];
521     return gl::Error(GL_NO_ERROR);
522 }
523 
generateMipmap(const gl::ImageIndex & sourceIndex,const gl::ImageIndex & destIndex)524 gl::Error TextureStorage9_Cube::generateMipmap(const gl::ImageIndex &sourceIndex, const gl::ImageIndex &destIndex)
525 {
526     IDirect3DSurface9 *upper = NULL;
527     gl::Error error = getSurfaceLevel(sourceIndex.type, sourceIndex.mipIndex, false, &upper);
528     if (error.isError())
529     {
530         return error;
531     }
532 
533     IDirect3DSurface9 *lower = NULL;
534     error = getSurfaceLevel(destIndex.type, destIndex.mipIndex, true, &lower);
535     if (error.isError())
536     {
537         SafeRelease(upper);
538         return error;
539     }
540 
541     ASSERT(upper && lower);
542     error = mRenderer->boxFilter(upper, lower);
543 
544     SafeRelease(upper);
545     SafeRelease(lower);
546 
547     return error;
548 }
549 
getBaseTexture(IDirect3DBaseTexture9 ** outTexture)550 gl::Error TextureStorage9_Cube::getBaseTexture(IDirect3DBaseTexture9 **outTexture)
551 {
552     // if the size is not positive this should be treated as an incomplete texture
553     // we handle that here by skipping the d3d texture creation
554     if (mTexture == NULL && mTextureWidth > 0 && mTextureHeight > 0)
555     {
556         ASSERT(mMipLevels > 0);
557         ASSERT(mTextureWidth == mTextureHeight);
558 
559         IDirect3DDevice9 *device = mRenderer->getDevice();
560         HRESULT result = device->CreateCubeTexture(
561             static_cast<unsigned int>(mTextureWidth), static_cast<unsigned int>(mMipLevels),
562             getUsage(), mTextureFormat, getPool(), &mTexture, NULL);
563 
564         if (FAILED(result))
565         {
566             ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
567             return gl::Error(GL_OUT_OF_MEMORY, "Failed to create cube storage texture, result: 0x%X.", result);
568         }
569     }
570 
571     *outTexture = mTexture;
572     return gl::Error(GL_NO_ERROR);
573 }
574 
copyToStorage(TextureStorage * destStorage)575 gl::Error TextureStorage9_Cube::copyToStorage(TextureStorage *destStorage)
576 {
577     ASSERT(destStorage);
578 
579     TextureStorage9_Cube *dest9 = GetAs<TextureStorage9_Cube>(destStorage);
580 
581     int levels = getLevelCount();
582     for (int f = 0; f < static_cast<int>(CUBE_FACE_COUNT); f++)
583     {
584         for (int i = 0; i < levels; i++)
585         {
586             IDirect3DSurface9 *srcSurf = NULL;
587             gl::Error error =
588                 getSurfaceLevel(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, false, &srcSurf);
589             if (error.isError())
590             {
591                 return error;
592             }
593 
594             IDirect3DSurface9 *dstSurf = NULL;
595             error = dest9->getSurfaceLevel(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true, &dstSurf);
596             if (error.isError())
597             {
598                 SafeRelease(srcSurf);
599                 return error;
600             }
601 
602             error = mRenderer->copyToRenderTarget(dstSurf, srcSurf, isManaged());
603 
604             SafeRelease(srcSurf);
605             SafeRelease(dstSurf);
606 
607             if (error.isError())
608             {
609                 return error;
610             }
611         }
612     }
613 
614     return gl::Error(GL_NO_ERROR);
615 }
616 
617 }
618