1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
5 #include "IrrCompileConfig.h"
\r
7 #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
\r
9 #include "CD3D9Texture.h"
\r
10 #include "CD3D9Driver.h"
\r
18 CD3D9Texture::CD3D9Texture(const io::path& name, const core::array<IImage*>& image, E_TEXTURE_TYPE type, CD3D9Driver* driver)
\r
19 : ITexture(name, type), Driver(driver), InternalFormat(D3DFMT_UNKNOWN), LockReadOnly(false), LockData(0), LockLayer(0),
\r
20 MipLevelLocked(0), HardwareMipMaps(false), Device(0), Texture(0), CubeTexture(0), RTTSurface(0)
\r
23 setDebugName("CD3D9Texture");
\r
26 _IRR_DEBUG_BREAK_IF(image.size() == 0)
\r
28 Device=driver->getExposedVideoData().D3D9.D3DDev9;
\r
33 DriverType = Driver->getDriverType();
\r
34 HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
\r
35 HardwareMipMaps = Driver->getTextureCreationFlag(ETCF_AUTO_GENERATE_MIP_MAPS) && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE);
\r
37 getImageValues(image[0]);
\r
41 if (HasMipMaps && HardwareMipMaps)
\r
43 LPDIRECT3D9 intf = Driver->getExposedVideoData().D3D9.D3D9;
\r
44 D3DDISPLAYMODE d3ddm;
\r
45 intf->GetAdapterDisplayMode(Driver->Params.DisplayAdapter, &d3ddm);
\r
47 if (D3D_OK == intf->CheckDeviceFormat(Driver->Params.DisplayAdapter, D3DDEVTYPE_HAL, d3ddm.Format, D3DUSAGE_AUTOGENMIPMAP, D3DRTYPE_TEXTURE, InternalFormat))
\r
48 flags = D3DUSAGE_AUTOGENMIPMAP;
\r
50 HardwareMipMaps = false;
\r
58 hr = Device->CreateTexture(Size.Width, Size.Height, HasMipMaps ? 0 : 1, flags, InternalFormat, D3DPOOL_MANAGED, &Texture, NULL);
\r
61 hr = Device->CreateCubeTexture(Size.Width, HasMipMaps ? 0 : 1, flags, InternalFormat, D3DPOOL_MANAGED, &CubeTexture, NULL);
\r
64 _IRR_DEBUG_BREAK_IF(true)
\r
70 // Try again with 16-bit format
\r
71 if (InternalFormat == D3DFMT_A8R8G8B8)
\r
73 InternalFormat = D3DFMT_A1R5G5B5;
\r
74 ColorFormat = ECF_A1R5G5B5;
\r
76 else if (InternalFormat == D3DFMT_R8G8B8) // (24 bit is usually failing in d3d9, not sure if it's ever supported)
\r
78 InternalFormat = D3DFMT_R5G6B5;
\r
79 ColorFormat = ECF_R5G6B5;
\r
84 hr = Device->CreateTexture(Size.Width, Size.Height, HasMipMaps ? 0 : 1, flags, InternalFormat, D3DPOOL_MANAGED, &Texture, NULL);
\r
87 hr = Device->CreateCubeTexture(Size.Width, HasMipMaps ? 0 : 1, flags, InternalFormat, D3DPOOL_MANAGED, &CubeTexture, NULL);
\r
92 core::array<IImage*> tmpImage = image;
\r
93 bool releaseImageData = false;
\r
97 if (OriginalSize != Size || OriginalColorFormat != ColorFormat)
\r
99 releaseImageData = true;
\r
101 for (u32 i = 0; i < image.size(); ++i)
\r
103 tmpImage[i] = Driver->createImage(ColorFormat, Size);
\r
105 if (image[i]->getDimension() == Size)
\r
106 image[i]->copyTo(tmpImage[i]);
\r
108 image[i]->copyToScaling(tmpImage[i]);
\r
112 for (u32 i = 0; i < tmpImage.size(); ++i)
\r
113 uploadTexture(tmpImage[i]->getData(), 0, i);
\r
115 bool autoGenerateRequired = true;
\r
117 for (u32 i = 0; i < tmpImage.size(); ++i)
\r
119 void* mipmapsData = tmpImage[i]->getMipMapsData();
\r
121 if (autoGenerateRequired || mipmapsData)
\r
122 regenerateMipMapLevels(mipmapsData, i);
\r
125 autoGenerateRequired = false;
\r
132 case D3DERR_INVALIDCALL:
\r
133 os::Printer::log("Could not create DIRECT3D9 Texture. D3DERR_INVALIDCALL", ELL_WARNING);
\r
135 case D3DERR_OUTOFVIDEOMEMORY:
\r
136 os::Printer::log("Could not create DIRECT3D9 Texture. D3DERR_OUTOFVIDEOMEMORY", ELL_WARNING);
\r
138 case E_OUTOFMEMORY:
\r
139 os::Printer::log("Could not create DIRECT3D9 Texture. E_OUTOFMEMORY", ELL_WARNING);
\r
142 os::Printer::log("Could not create DIRECT3D9 Texture.", ELL_WARNING);
\r
146 if (releaseImageData)
\r
148 for (u32 i = 0; i < tmpImage.size(); ++i)
\r
149 tmpImage[i]->drop();
\r
153 CD3D9Texture::CD3D9Texture(CD3D9Driver* driver, const core::dimension2d<u32>& size, const io::path& name, E_TEXTURE_TYPE type, const ECOLOR_FORMAT format)
\r
154 : ITexture(name, type), Driver(driver), InternalFormat(D3DFMT_UNKNOWN), LockReadOnly(false), LockData(0), LockLayer(0),
\r
155 MipLevelLocked(0), HardwareMipMaps(false), Device(0), Texture(0), CubeTexture(0), RTTSurface(0)
\r
158 setDebugName("CD3D9Texture");
\r
161 Device = driver->getExposedVideoData().D3D9.D3DDev9;
\r
166 DriverType = Driver->getDriverType();
\r
167 HasMipMaps = false;
\r
168 IsRenderTarget = true;
\r
170 OriginalColorFormat = format;
\r
172 if (ECF_UNKNOWN == OriginalColorFormat)
\r
173 ColorFormat = getBestColorFormat(Driver->getColorFormat());
\r
175 ColorFormat = OriginalColorFormat;
\r
177 OriginalSize = size;
\r
178 Size = OriginalSize;
\r
180 if (!Driver->queryFeature(EVDF_TEXTURE_NPOT))
\r
182 Size = Size.getOptimalSize(true, !Driver->queryFeature(EVDF_TEXTURE_NSQUARE), true, Driver->Caps.MaxTextureWidth);
\r
184 if (Size != OriginalSize)
\r
185 os::Printer::log("RenderTarget size has to be a power of two", ELL_INFORMATION);
\r
188 Pitch = Size.Width * IImage::getBitsPerPixelFromFormat(ColorFormat) / 8;
\r
190 InternalFormat = Driver->getD3DFormatFromColorFormat(ColorFormat);
\r
192 generateRenderTarget();
\r
195 CD3D9Texture::~CD3D9Texture()
\r
203 void* CD3D9Texture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel, u32 layer, E_TEXTURE_LOCK_FLAGS lockFlags)
\r
208 if (IImage::isCompressedFormat(ColorFormat))
\r
211 MipLevelLocked = mipmapLevel;
\r
212 LockReadOnly = (mode == ETLM_READ_ONLY);
\r
216 D3DLOCKED_RECT rect;
\r
218 if (!IsRenderTarget)
\r
222 hr = Texture->LockRect(MipLevelLocked, &rect, 0, LockReadOnly ? D3DLOCK_READONLY : 0);
\r
224 else if (CubeTexture)
\r
226 _IRR_DEBUG_BREAK_IF(layer > 5)
\r
228 hr = CubeTexture->LockRect(static_cast<_D3DCUBEMAP_FACES>(layer), MipLevelLocked, &rect, 0, LockReadOnly ? D3DLOCK_READONLY : 0);
\r
232 os::Printer::log("Could not lock DIRECT3D9 Texture. Missing internal D3D texture.", ELL_ERROR);
\r
238 os::Printer::log("Could not lock DIRECT3D9 Texture.", ELL_ERROR);
\r
246 // Make RTT surface large enough for all miplevels (including 0)
\r
247 D3DSURFACE_DESC desc;
\r
249 Texture->GetLevelDesc(0, &desc);
\r
250 else if (CubeTexture)
\r
251 CubeTexture->GetLevelDesc(0, &desc);
\r
252 hr = Device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &RTTSurface, 0);
\r
255 os::Printer::log("Could not lock DIRECT3D9 Texture", "Offscreen surface creation failed.", ELL_ERROR);
\r
260 IDirect3DSurface9 *surface = 0;
\r
262 hr = Texture->GetSurfaceLevel(MipLevelLocked, &surface);
\r
263 else if (CubeTexture)
\r
264 hr = CubeTexture->GetCubeMapSurface(static_cast<_D3DCUBEMAP_FACES>(layer), MipLevelLocked, &surface);
\r
267 os::Printer::log("Could not lock DIRECT3D9 Texture", "Could not get surface.", ELL_ERROR);
\r
270 hr = Device->GetRenderTargetData(surface, RTTSurface);
\r
271 surface->Release();
\r
274 os::Printer::log("Could not lock DIRECT3D9 Texture", "Data copy failed.", ELL_ERROR);
\r
277 hr = RTTSurface->LockRect(&rect, 0, LockReadOnly ? D3DLOCK_READONLY : 0);
\r
280 os::Printer::log("Could not lock DIRECT3D9 Texture", "LockRect failed.", ELL_ERROR);
\r
285 LockData = rect.pBits;
\r
290 void CD3D9Texture::unlock()
\r
295 if (!IsRenderTarget)
\r
299 Texture->UnlockRect(MipLevelLocked);
\r
301 else if (CubeTexture)
\r
303 CubeTexture->UnlockRect(static_cast<_D3DCUBEMAP_FACES>(LockLayer), MipLevelLocked);
\r
306 else if (RTTSurface)
\r
308 RTTSurface->UnlockRect();
\r
311 LockReadOnly = false;
\r
316 void CD3D9Texture::regenerateMipMapLevels(void* data, u32 layer)
\r
318 if (!HasMipMaps || (Size.Width <= 1 && Size.Height <= 1))
\r
321 if ( HardwareMipMaps )
\r
323 // Can't update with custom data with those unfortunately
\r
324 // Also MSDN docs don't mention it, but GenerateMipSubLevels only works when AUTOGENMIPMAP is set.
\r
325 // So we can't call this to get hardware mipmaps when not setting AUTOGENMIPMAP.
\r
327 Texture->GenerateMipSubLevels();
\r
328 else if (CubeTexture)
\r
329 CubeTexture->GenerateMipSubLevels();
\r
333 u32 width = Size.Width;
\r
334 u32 height = Size.Height;
\r
335 u8* tmpData = static_cast<u8*>(data);
\r
347 dataSize = IImage::getDataSizeFromFormat(ColorFormat, width, height);
\r
350 uploadTexture(tmpData, level, layer);
\r
352 tmpData += dataSize;
\r
353 } while (width != 1 || height != 1);
\r
357 createManualMipMaps(1);
\r
361 void CD3D9Texture::copy16BitMipMap(char* src, char* tgt,
\r
362 s32 width, s32 height,
\r
363 s32 pitchsrc, s32 pitchtgt) const
\r
365 for (s32 y=0; y<height; ++y)
\r
367 for (s32 x=0; x<width; ++x)
\r
369 u32 a=0, r=0, g=0, b=0;
\r
371 for (s32 dy=0; dy<2; ++dy)
\r
373 const s32 tgy = (y*2)+dy;
\r
374 for (s32 dx=0; dx<2; ++dx)
\r
376 const s32 tgx = (x*2)+dx;
\r
379 if (ColorFormat == ECF_A1R5G5B5)
\r
380 c = A1R5G5B5toA8R8G8B8(*(u16*)(&src[(tgx*2)+(tgy*pitchsrc)]));
\r
382 c = R5G6B5toA8R8G8B8(*(u16*)(&src[(tgx*2)+(tgy*pitchsrc)]));
\r
397 if (ColorFormat == ECF_A1R5G5B5)
\r
398 c = RGBA16(r,g,b,a);
\r
400 c = A8R8G8B8toR5G6B5(SColor(a,r,g,b).color);
\r
401 *(u16*)(&tgt[(x*2)+(y*pitchtgt)]) = c;
\r
406 void CD3D9Texture::copy32BitMipMap(char* src, char* tgt,
\r
407 s32 width, s32 height,
\r
408 s32 pitchsrc, s32 pitchtgt) const
\r
410 for (s32 y=0; y<height; ++y)
\r
412 for (s32 x=0; x<width; ++x)
\r
414 u32 a=0, r=0, g=0, b=0;
\r
417 for (s32 dy=0; dy<2; ++dy)
\r
419 const s32 tgy = (y*2)+dy;
\r
420 for (s32 dx=0; dx<2; ++dx)
\r
422 const s32 tgx = (x*2)+dx;
\r
424 c = *(u32*)(&src[(tgx*4)+(tgy*pitchsrc)]);
\r
439 *(u32*)(&tgt[(x*4)+(y*pitchtgt)]) = c.color;
\r
444 bool CD3D9Texture::createManualMipMaps(u32 level)
\r
449 if (!Texture) //Manual mips for CubeTexture not supported yet
\r
454 // manual mipmap generation
\r
455 IDirect3DSurface9* upperSurface = 0;
\r
456 IDirect3DSurface9* lowerSurface = 0;
\r
459 HRESULT hr = Texture->GetSurfaceLevel(level-1, &upperSurface);
\r
460 if (FAILED(hr) || !upperSurface)
\r
462 os::Printer::log("Could not get upper surface level for mip map generation", ELL_WARNING);
\r
467 hr = Texture->GetSurfaceLevel(level, &lowerSurface);
\r
468 if (FAILED(hr) || !lowerSurface)
\r
470 os::Printer::log("Could not get lower surface level for mip map generation", ELL_WARNING);
\r
471 upperSurface->Release();
\r
475 D3DSURFACE_DESC upperDesc, lowerDesc;
\r
476 upperSurface->GetDesc(&upperDesc);
\r
477 lowerSurface->GetDesc(&lowerDesc);
\r
479 D3DLOCKED_RECT upperlr;
\r
480 D3DLOCKED_RECT lowerlr;
\r
482 // lock upper surface
\r
483 if (FAILED(upperSurface->LockRect(&upperlr, NULL, 0)))
\r
485 upperSurface->Release();
\r
486 lowerSurface->Release();
\r
487 os::Printer::log("Could not lock upper texture for mip map generation", ELL_WARNING);
\r
491 // lock lower surface
\r
492 if (FAILED(lowerSurface->LockRect(&lowerlr, NULL, 0)))
\r
494 upperSurface->UnlockRect();
\r
495 upperSurface->Release();
\r
496 lowerSurface->Release();
\r
497 os::Printer::log("Could not lock lower texture for mip map generation", ELL_WARNING);
\r
501 if (upperDesc.Format != lowerDesc.Format)
\r
503 os::Printer::log("Cannot copy mip maps with different formats.", ELL_WARNING);
\r
507 if ((upperDesc.Format == D3DFMT_A1R5G5B5) || (upperDesc.Format == D3DFMT_R5G6B5))
\r
508 copy16BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits,
\r
509 lowerDesc.Width, lowerDesc.Height,
\r
510 upperlr.Pitch, lowerlr.Pitch);
\r
512 if (upperDesc.Format == D3DFMT_A8R8G8B8)
\r
513 copy32BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits,
\r
514 lowerDesc.Width, lowerDesc.Height,
\r
515 upperlr.Pitch, lowerlr.Pitch);
\r
517 os::Printer::log("Unsupported mipmap format, cannot copy.", ELL_WARNING);
\r
522 if (FAILED(upperSurface->UnlockRect()))
\r
524 if (FAILED(lowerSurface->UnlockRect()))
\r
528 upperSurface->Release();
\r
529 lowerSurface->Release();
\r
531 if (!result || (upperDesc.Width <= 3 && upperDesc.Height <= 3))
\r
532 return result; // stop generating levels
\r
534 // generate next level
\r
535 return createManualMipMaps(level+1);
\r
539 IDirect3DBaseTexture9* CD3D9Texture::getDX9BaseTexture() const
\r
541 return (Texture) ? static_cast<IDirect3DBaseTexture9*>(Texture) : static_cast<IDirect3DBaseTexture9*>(CubeTexture);
\r
544 IDirect3DTexture9* CD3D9Texture::getDX9Texture() const
\r
549 IDirect3DCubeTexture9* CD3D9Texture::getDX9CubeTexture() const
\r
551 return CubeTexture;
\r
554 void CD3D9Texture::releaseTexture()
\r
558 if (RTTSurface->Release() == 0)
\r
564 if (Texture->Release() == 0)
\r
570 if (CubeTexture->Release() == 0)
\r
575 void CD3D9Texture::generateRenderTarget()
\r
577 DWORD flags = (IImage::isDepthFormat(ColorFormat)) ? D3DUSAGE_DEPTHSTENCIL : D3DUSAGE_RENDERTARGET;
\r
585 hr = Device->CreateTexture(Size.Width, Size.Height, 1, flags, InternalFormat, D3DPOOL_DEFAULT, &Texture, NULL);
\r
589 hr = Device->CreateCubeTexture(Size.Width, 1, flags, InternalFormat, D3DPOOL_DEFAULT, &CubeTexture, NULL);
\r
592 _IRR_DEBUG_BREAK_IF(true)
\r
598 if (D3DERR_INVALIDCALL == hr)
\r
599 os::Printer::log("Could not create render target texture", "Invalid Call", irr::ELL_ERROR);
\r
600 else if (D3DERR_OUTOFVIDEOMEMORY == hr)
\r
601 os::Printer::log("Could not create render target texture", "Out of Video Memory", irr::ELL_ERROR);
\r
602 else if (E_OUTOFMEMORY == hr)
\r
603 os::Printer::log("Could not create render target texture", "Out of Memory", irr::ELL_ERROR);
\r
605 os::Printer::log("Could not create render target texture", irr::ELL_ERROR);
\r
606 core::stringc params("Width:");
\r
607 params += (unsigned int)Size.Width;
\r
608 params += " Height: ";
\r
609 params += (unsigned int)Size.Height;
\r
610 params += " flag: ";
\r
611 params += (unsigned int)flags;
\r
612 params += " format";
\r
613 params += (unsigned int)InternalFormat;
\r
614 params += " Type: ";
\r
615 params += (unsigned int)Type;
\r
616 os::Printer::log(params.c_str(), irr::ELL_ERROR);
\r
620 ECOLOR_FORMAT CD3D9Texture::getBestColorFormat(ECOLOR_FORMAT format)
\r
622 // We only try for to adapt "simple" formats
\r
623 ECOLOR_FORMAT destFormat = (format <= ECF_A8R8G8B8) ? ECF_A8R8G8B8 : format;
\r
628 if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT))
\r
629 destFormat = ECF_A1R5G5B5;
\r
632 if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT))
\r
633 destFormat = ECF_R5G6B5;
\r
636 if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) ||
\r
637 Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
\r
638 destFormat = ECF_A1R5G5B5;
\r
641 // Note: Using ECF_A8R8G8B8 even when ETCF_ALWAYS_32_BIT is not set as 24 bit textures fail with too many cards
\r
642 if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) || Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
\r
643 destFormat = ECF_A1R5G5B5;
\r
648 if (Driver->getTextureCreationFlag(ETCF_NO_ALPHA_CHANNEL))
\r
650 switch (destFormat)
\r
653 destFormat = ECF_R5G6B5;
\r
656 destFormat = ECF_R8G8B8;
\r
666 void CD3D9Texture::getImageValues(const IImage* image)
\r
668 OriginalColorFormat = image->getColorFormat();
\r
669 ColorFormat = getBestColorFormat(OriginalColorFormat);
\r
671 InternalFormat = Driver->getD3DFormatFromColorFormat(ColorFormat);
\r
673 if (IImage::isCompressedFormat(image->getColorFormat()))
\r
675 HardwareMipMaps = false;
\r
678 OriginalSize = image->getDimension();
\r
679 Size = OriginalSize;
\r
681 if (Size.Width == 0 || Size.Height == 0)
\r
683 os::Printer::log("Invalid size of image for texture.", ELL_ERROR);
\r
687 const f32 ratio = (f32)Size.Width / (f32)Size.Height;
\r
689 if ((Size.Width > Driver->Caps.MaxTextureWidth) && (ratio >= 1.f))
\r
691 Size.Width = Driver->Caps.MaxTextureWidth;
\r
692 Size.Height = (u32)(Driver->Caps.MaxTextureWidth / ratio);
\r
694 else if (Size.Height > Driver->Caps.MaxTextureHeight)
\r
696 Size.Height = Driver->Caps.MaxTextureHeight;
\r
697 Size.Width = (u32)(Driver->Caps.MaxTextureHeight * ratio);
\r
700 bool needSquare = (!Driver->queryFeature(EVDF_TEXTURE_NSQUARE) || Type == ETT_CUBEMAP);
\r
702 Size = Size.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT), needSquare, true, Driver->Caps.MaxTextureWidth);
\r
704 Pitch = Size.Width * IImage::getBitsPerPixelFromFormat(ColorFormat) / 8;
\r
707 void CD3D9Texture::uploadTexture(void* data, u32 mipmapLevel, u32 layer)
\r
712 u32 width = Size.Width >> mipmapLevel;
\r
713 u32 height = Size.Height >> mipmapLevel;
\r
715 u32 dataSize = IImage::getDataSizeFromFormat(ColorFormat, width, height);
\r
719 D3DLOCKED_RECT lockRectangle;
\r
723 hr = Texture->LockRect(mipmapLevel, &lockRectangle, 0, 0);
\r
725 else if (CubeTexture)
\r
727 _IRR_DEBUG_BREAK_IF(layer > 5)
\r
729 hr = CubeTexture->LockRect(static_cast<_D3DCUBEMAP_FACES>(layer), mipmapLevel, &lockRectangle, 0, 0);
\r
734 os::Printer::log("Texture data not copied", "Could not LockRect D3D9 Texture.", ELL_ERROR);
\r
738 memcpy(lockRectangle.pBits, data, dataSize);
\r
742 hr = Texture->UnlockRect(mipmapLevel);
\r
744 else if (CubeTexture)
\r
746 hr = CubeTexture->UnlockRect(static_cast<_D3DCUBEMAP_FACES>(layer), mipmapLevel);
\r
751 os::Printer::log("Texture data not copied", "Could not UnlockRect D3D9 Texture.", ELL_ERROR);
\r