/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. ----------------------------------------------------------------------------- */ #include "OgreD3D9Texture.h" #include "OgreD3D9HardwarePixelBuffer.h" #include "OgreException.h" #include "OgreLogManager.h" #include "OgreStringConverter.h" #include "OgreBitwise.h" #include "OgreNoMemoryMacros.h" #include #include #include "OgreMemoryMacros.h" namespace Ogre { /****************************************************************************************/ D3D9Texture::D3D9Texture(ResourceManager* creator, const String& name, ResourceHandle handle, const String& group, bool isManual, ManualResourceLoader* loader, IDirect3DDevice9 *pD3DDevice) :Texture(creator, name, handle, group, isManual, loader), mpDev(pD3DDevice), mpD3D(NULL), mpNormTex(NULL), mpCubeTex(NULL), mpVolumeTex(NULL), mpZBuff(NULL), mpTex(NULL), mDynamicTextures(false) { _initDevice(); } /****************************************************************************************/ D3D9Texture::~D3D9Texture() { // have to call this here reather than in Resource destructor // since calling virtual methods in base destructors causes crash if (mIsLoaded) { unload(); } else { freeInternalResources(); } } /****************************************************************************************/ void D3D9Texture::copyToTexture(TexturePtr& target) { // check if this & target are the same format and type // blitting from or to cube textures is not supported yet if (target->getUsage() != this->getUsage() || target->getTextureType() != this->getTextureType()) { OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, "Src. and dest. textures must be of same type and must have the same usage !!!", "D3D9Texture::copyToTexture" ); } HRESULT hr; D3D9Texture *other; // get the target other = reinterpret_cast< D3D9Texture * >( target.get() ); // target rectangle (whole surface) RECT dstRC = {0, 0, other->getWidth(), other->getHeight()}; // do it plain for normal texture if (this->getTextureType() == TEX_TYPE_2D) { // get our source surface IDirect3DSurface9 *pSrcSurface = 0; if( FAILED( hr = mpNormTex->GetSurfaceLevel(0, &pSrcSurface) ) ) { String msg = DXGetErrorDescription9(hr); OGRE_EXCEPT( hr, "Couldn't blit : " + msg, "D3D9Texture::copyToTexture" ); } // get our target surface IDirect3DSurface9 *pDstSurface = 0; IDirect3DTexture9 *pOthTex = other->getNormTexture(); if( FAILED( hr = pOthTex->GetSurfaceLevel(0, &pDstSurface) ) ) { String msg = DXGetErrorDescription9(hr); SAFE_RELEASE(pSrcSurface); OGRE_EXCEPT( hr, "Couldn't blit : " + msg, "D3D9Texture::copyToTexture" ); } // do the blit, it's called StretchRect in D3D9 :) if( FAILED( hr = mpDev->StretchRect( pSrcSurface, NULL, pDstSurface, &dstRC, D3DTEXF_NONE) ) ) { String msg = DXGetErrorDescription9(hr); SAFE_RELEASE(pSrcSurface); SAFE_RELEASE(pDstSurface); OGRE_EXCEPT( hr, "Couldn't blit : " + msg, "D3D9Texture::copyToTexture" ); } // release temp. surfaces SAFE_RELEASE(pSrcSurface); SAFE_RELEASE(pDstSurface); } else if (this->getTextureType() == TEX_TYPE_CUBE_MAP) { // get the target cube texture IDirect3DCubeTexture9 *pOthTex = other->getCubeTexture(); // blit to 6 cube faces for (size_t face = 0; face < 6; face++) { // get our source surface IDirect3DSurface9 *pSrcSurface = 0; if( FAILED( hr = mpCubeTex->GetCubeMapSurface((D3DCUBEMAP_FACES)face, 0, &pSrcSurface) ) ) { String msg = DXGetErrorDescription9(hr); OGRE_EXCEPT( hr, "Couldn't blit : " + msg, "D3D9Texture::copyToTexture" ); } // get our target surface IDirect3DSurface9 *pDstSurface = 0; if( FAILED( hr = pOthTex->GetCubeMapSurface((D3DCUBEMAP_FACES)face, 0, &pDstSurface) ) ) { String msg = DXGetErrorDescription9(hr); SAFE_RELEASE(pSrcSurface); OGRE_EXCEPT( hr, "Couldn't blit : " + msg, "D3D9Texture::copyToTexture" ); } // do the blit, it's called StretchRect in D3D9 :) if( FAILED( hr = mpDev->StretchRect( pSrcSurface, NULL, pDstSurface, &dstRC, D3DTEXF_NONE) ) ) { String msg = DXGetErrorDescription9(hr); SAFE_RELEASE(pSrcSurface); SAFE_RELEASE(pDstSurface); OGRE_EXCEPT( hr, "Couldn't blit : " + msg, "D3D9Texture::copyToTexture" ); } // release temp. surfaces SAFE_RELEASE(pSrcSurface); SAFE_RELEASE(pDstSurface); } } else { OGRE_EXCEPT( Exception::UNIMPLEMENTED_FEATURE, "Copy to texture is implemented only for 2D and cube textures !!!", "D3D9Texture::copyToTexture" ); } } /****************************************************************************************/ void D3D9Texture::loadImage( const Image &img ) { // Use OGRE its own codecs std::vector imagePtrs; imagePtrs.push_back(&img); _loadImages( imagePtrs ); } /****************************************************************************************/ void D3D9Texture::loadImpl() { if (mUsage & TU_RENDERTARGET) { createInternalResources(); mIsLoaded = true; return; } // load based on tex.type switch (this->getTextureType()) { case TEX_TYPE_1D: case TEX_TYPE_2D: this->_loadNormTex(); break; case TEX_TYPE_3D: this->_loadVolumeTex(); break; case TEX_TYPE_CUBE_MAP: this->_loadCubeTex(); break; default: OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Unknown texture type", "D3D9Texture::loadImpl" ); } } /****************************************************************************************/ void D3D9Texture::freeInternalResourcesImpl() { SAFE_RELEASE(mpTex); SAFE_RELEASE(mpNormTex); SAFE_RELEASE(mpCubeTex); SAFE_RELEASE(mpVolumeTex); SAFE_RELEASE(mpZBuff); } /****************************************************************************************/ void D3D9Texture::_loadCubeTex() { assert(this->getTextureType() == TEX_TYPE_CUBE_MAP); // DDS load? if (StringUtil::endsWith(getName(), ".dds")) { // find & load resource data DataStreamPtr dstream = ResourceGroupManager::getSingleton().openResource(mName, mGroup); MemoryDataStream stream( dstream ); HRESULT hr = D3DXCreateCubeTextureFromFileInMemory( mpDev, stream.getPtr(), stream.size(), &mpCubeTex); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't create cube texture", "D3D9Texture::_loadCubeTex" ); } hr = mpCubeTex->QueryInterface(IID_IDirect3DBaseTexture9, (void **)&mpTex); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get base texture", "D3D9Texture::_loadCubeTex" ); } D3DSURFACE_DESC texDesc; mpCubeTex->GetLevelDesc(0, &texDesc); // set src and dest attributes to the same, we can't know _setSrcAttributes(texDesc.Width, texDesc.Height, 1, _getPF(texDesc.Format)); _setFinalAttributes(texDesc.Width, texDesc.Height, 1, _getPF(texDesc.Format)); mIsLoaded = true; mInternalResourcesCreated = true; } else { // Load from 6 separate files // Use OGRE its own codecs String baseName, ext; std::vector images(6); std::vector imagePtrs; static const String suffixes[6] = {"_rt", "_lf", "_up", "_dn", "_fr", "_bk"}; for(size_t i = 0; i < 6; i++) { size_t pos = mName.find_last_of("."); baseName = mName.substr(0, pos); ext = mName.substr(pos); String fullName = baseName + suffixes[i] + ext; images[i].load(fullName, mGroup); imagePtrs.push_back(&images[i]); } _loadImages( imagePtrs ); } } /****************************************************************************************/ void D3D9Texture::_loadVolumeTex() { assert(this->getTextureType() == TEX_TYPE_3D); // DDS load? if (StringUtil::endsWith(getName(), ".dds")) { // find & load resource data DataStreamPtr dstream = ResourceGroupManager::getSingleton().openResource(mName, mGroup); MemoryDataStream stream(dstream); HRESULT hr = D3DXCreateVolumeTextureFromFileInMemory( mpDev, stream.getPtr(), stream.size(), &mpVolumeTex); if (FAILED(hr)) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Unable to load volume texture from " + this->getName(), "D3D9Texture::_loadVolumeTex"); } hr = mpVolumeTex->QueryInterface(IID_IDirect3DBaseTexture9, (void **)&mpTex); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get base texture", "D3D9Texture::_loadVolumeTex" ); } D3DVOLUME_DESC texDesc; hr = mpVolumeTex->GetLevelDesc(0, &texDesc); // set src and dest attributes to the same, we can't know _setSrcAttributes(texDesc.Width, texDesc.Height, texDesc.Depth, _getPF(texDesc.Format)); _setFinalAttributes(texDesc.Width, texDesc.Height, texDesc.Depth, _getPF(texDesc.Format)); mIsLoaded = true; mInternalResourcesCreated = true; } else { Image img; img.load(mName, mGroup); loadImage(img); } } /****************************************************************************************/ void D3D9Texture::_loadNormTex() { assert(this->getTextureType() == TEX_TYPE_1D || this->getTextureType() == TEX_TYPE_2D); // DDS load? if (StringUtil::endsWith(getName(), ".dds")) { // Use D3DX // find & load resource data DataStreamPtr dstream = ResourceGroupManager::getSingleton().openResource(mName, mGroup); MemoryDataStream stream(dstream); HRESULT hr = D3DXCreateTextureFromFileInMemory( mpDev, stream.getPtr(), stream.size(), &mpNormTex); if (FAILED(hr)) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Unable to load texture from " + this->getName(), "D3D9Texture::_loadNormTex"); } hr = mpNormTex->QueryInterface(IID_IDirect3DBaseTexture9, (void **)&mpTex); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get base texture", "D3D9Texture::_loadNormTex" ); } D3DSURFACE_DESC texDesc; mpNormTex->GetLevelDesc(0, &texDesc); // set src and dest attributes to the same, we can't know _setSrcAttributes(texDesc.Width, texDesc.Height, 1, _getPF(texDesc.Format)); _setFinalAttributes(texDesc.Width, texDesc.Height, 1, _getPF(texDesc.Format)); mIsLoaded = true; mInternalResourcesCreated = true; } else { Image img; img.load(mName, mGroup); loadImage(img); } } /****************************************************************************************/ void D3D9Texture::createInternalResourcesImpl(void) { // If mSrcWidth and mSrcHeight are zero, the requested extents have probably been set // through setWidth and setHeight, which set mWidth and mHeight. Take those values. if(mSrcWidth == 0 || mSrcHeight == 0) { mSrcWidth = mWidth; mSrcHeight = mHeight; } // Determine D3D pool to use // Use managed unless we're a render target or user has asked for // a dynamic texture if ((mUsage & TU_RENDERTARGET) || (mUsage & TU_DYNAMIC)) { mD3DPool = D3DPOOL_DEFAULT; } else { mD3DPool = D3DPOOL_MANAGED; } // load based on tex.type switch (this->getTextureType()) { case TEX_TYPE_1D: case TEX_TYPE_2D: this->_createNormTex(); break; case TEX_TYPE_CUBE_MAP: this->_createCubeTex(); break; case TEX_TYPE_3D: this->_createVolumeTex(); break; default: this->freeInternalResources(); OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Unknown texture type", "D3D9Texture::createInternalResources" ); } } /****************************************************************************************/ void D3D9Texture::_createNormTex() { // we must have those defined here assert(mSrcWidth > 0 || mSrcHeight > 0); // determine wich D3D9 pixel format we'll use HRESULT hr; D3DFORMAT d3dPF = this->_chooseD3DFormat(); // let's D3DX check the corrected pixel format hr = D3DXCheckTextureRequirements(mpDev, NULL, NULL, NULL, 0, &d3dPF, mD3DPool); // Use D3DX to help us create the texture, this way it can adjust any relevant sizes DWORD usage = (mUsage & TU_RENDERTARGET) ? D3DUSAGE_RENDERTARGET : 0; UINT numMips = mNumRequestedMipmaps + 1; // Check dynamic textures if (mUsage & TU_DYNAMIC) { if (_canUseDynamicTextures(usage, D3DRTYPE_TEXTURE, d3dPF)) { usage |= D3DUSAGE_DYNAMIC; mDynamicTextures = true; } else { mDynamicTextures = false; } } // check if mip maps are supported on hardware mMipmapsHardwareGenerated = false; if (mDevCaps.TextureCaps & D3DPTEXTURECAPS_MIPMAP) { if (mUsage & TU_AUTOMIPMAP && mNumRequestedMipmaps != 0) { // use auto.gen. if available, and if desired mMipmapsHardwareGenerated = this->_canAutoGenMipmaps(usage, D3DRTYPE_TEXTURE, d3dPF); if (mMipmapsHardwareGenerated) { usage |= D3DUSAGE_AUTOGENMIPMAP; numMips = 0; } } } else { // no mip map support for this kind of textures :( mNumMipmaps = 0; numMips = 1; } // create the texture hr = D3DXCreateTexture( mpDev, // device mSrcWidth, // width mSrcHeight, // height numMips, // number of mip map levels usage, // usage d3dPF, // pixel format mD3DPool, &mpNormTex); // data pointer // check result and except if failed if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Error creating texture", "D3D9Texture::_createNormTex" ); } // set the base texture we'll use in the render system hr = mpNormTex->QueryInterface(IID_IDirect3DBaseTexture9, (void **)&mpTex); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get base texture", "D3D9Texture::_createNormTex" ); } // set final tex. attributes from tex. description // they may differ from the source image !!! D3DSURFACE_DESC desc; hr = mpNormTex->GetLevelDesc(0, &desc); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get texture description", "D3D9Texture::_createNormTex" ); } this->_setFinalAttributes(desc.Width, desc.Height, 1, this->_getPF(desc.Format)); // create a depth stencil if this is a render target if (mUsage & TU_RENDERTARGET) this->_createDepthStencil(); // Set best filter type if(mMipmapsHardwareGenerated) { hr = mpTex->SetAutoGenFilterType(_getBestFilterMethod()); if(FAILED(hr)) { OGRE_EXCEPT( hr, "Could not set best autogen filter type", "D3D9Texture::_createNormTex" ); } } } /****************************************************************************************/ void D3D9Texture::_createCubeTex() { // we must have those defined here assert(mSrcWidth > 0 || mSrcHeight > 0); // determine wich D3D9 pixel format we'll use HRESULT hr; D3DFORMAT d3dPF = this->_chooseD3DFormat(); // let's D3DX check the corrected pixel format hr = D3DXCheckCubeTextureRequirements(mpDev, NULL, NULL, 0, &d3dPF, mD3DPool); // Use D3DX to help us create the texture, this way it can adjust any relevant sizes DWORD usage = (mUsage & TU_RENDERTARGET) ? D3DUSAGE_RENDERTARGET : 0; UINT numMips = mNumRequestedMipmaps + 1; // Check dynamic textures if (mUsage & TU_DYNAMIC) { if (_canUseDynamicTextures(usage, D3DRTYPE_CUBETEXTURE, d3dPF)) { usage |= D3DUSAGE_DYNAMIC; mDynamicTextures = true; } else { mDynamicTextures = false; } } // check if mip map cube textures are supported mMipmapsHardwareGenerated = false; if (mDevCaps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP) { if (mUsage & TU_AUTOMIPMAP && mNumRequestedMipmaps != 0) { // use auto.gen. if available mMipmapsHardwareGenerated = this->_canAutoGenMipmaps(usage, D3DRTYPE_CUBETEXTURE, d3dPF); if (mMipmapsHardwareGenerated) { usage |= D3DUSAGE_AUTOGENMIPMAP; numMips = 0; } } } else { // no mip map support for this kind of textures :( mNumMipmaps = 0; numMips = 1; } // create the texture hr = D3DXCreateCubeTexture( mpDev, // device mSrcWidth, // dimension numMips, // number of mip map levels usage, // usage d3dPF, // pixel format mD3DPool, &mpCubeTex); // data pointer // check result and except if failed if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Error creating texture", "D3D9Texture::_createCubeTex" ); } // set the base texture we'll use in the render system hr = mpCubeTex->QueryInterface(IID_IDirect3DBaseTexture9, (void **)&mpTex); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get base texture", "D3D9Texture::_createCubeTex" ); } // set final tex. attributes from tex. description // they may differ from the source image !!! D3DSURFACE_DESC desc; hr = mpCubeTex->GetLevelDesc(0, &desc); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get texture description", "D3D9Texture::_createCubeTex" ); } this->_setFinalAttributes(desc.Width, desc.Height, 1, this->_getPF(desc.Format)); // create a depth stencil if this is a render target if (mUsage & TU_RENDERTARGET) this->_createDepthStencil(); // Set best filter type if(mMipmapsHardwareGenerated) { hr = mpTex->SetAutoGenFilterType(_getBestFilterMethod()); if(FAILED(hr)) { OGRE_EXCEPT( hr, "Could not set best autogen filter type", "D3D9Texture::_createCubeTex" ); } } } /****************************************************************************************/ void D3D9Texture::_createVolumeTex() { // we must have those defined here assert(mWidth > 0 && mHeight > 0 && mDepth>0); // determine wich D3D9 pixel format we'll use HRESULT hr; D3DFORMAT d3dPF = this->_chooseD3DFormat(); // let's D3DX check the corrected pixel format hr = D3DXCheckVolumeTextureRequirements(mpDev, NULL, NULL, NULL, NULL, 0, &d3dPF, mD3DPool); // Use D3DX to help us create the texture, this way it can adjust any relevant sizes DWORD usage = (mUsage & TU_RENDERTARGET) ? D3DUSAGE_RENDERTARGET : 0; UINT numMips = mNumRequestedMipmaps + 1; // Check dynamic textures if (mUsage & TU_DYNAMIC) { if (_canUseDynamicTextures(usage, D3DRTYPE_VOLUMETEXTURE, d3dPF)) { usage |= D3DUSAGE_DYNAMIC; mDynamicTextures = true; } else { mDynamicTextures = false; } } // check if mip map volume textures are supported mMipmapsHardwareGenerated = false; if (mDevCaps.TextureCaps & D3DPTEXTURECAPS_MIPVOLUMEMAP) { if (mUsage & TU_AUTOMIPMAP && mNumRequestedMipmaps != 0) { // use auto.gen. if available mMipmapsHardwareGenerated = this->_canAutoGenMipmaps(usage, D3DRTYPE_VOLUMETEXTURE, d3dPF); if (mMipmapsHardwareGenerated) { usage |= D3DUSAGE_AUTOGENMIPMAP; numMips = 0; } } } else { // no mip map support for this kind of textures :( mNumMipmaps = 0; numMips = 1; } // create the texture hr = D3DXCreateVolumeTexture( mpDev, // device mWidth, // dimension mHeight, mDepth, numMips, // number of mip map levels usage, // usage d3dPF, // pixel format mD3DPool, &mpVolumeTex); // data pointer // check result and except if failed if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Error creating texture", "D3D9Texture::_createVolumeTex" ); } // set the base texture we'll use in the render system hr = mpVolumeTex->QueryInterface(IID_IDirect3DBaseTexture9, (void **)&mpTex); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get base texture", "D3D9Texture::_createVolumeTex" ); } // set final tex. attributes from tex. description // they may differ from the source image !!! D3DVOLUME_DESC desc; hr = mpVolumeTex->GetLevelDesc(0, &desc); if (FAILED(hr)) { this->freeInternalResources(); OGRE_EXCEPT( hr, "Can't get texture description", "D3D9Texture::_createVolumeTex" ); } this->_setFinalAttributes(desc.Width, desc.Height, desc.Depth, this->_getPF(desc.Format)); // create a depth stencil if this is a render target if (mUsage & TU_RENDERTARGET) this->_createDepthStencil(); // Set best filter type if(mMipmapsHardwareGenerated) { hr = mpTex->SetAutoGenFilterType(_getBestFilterMethod()); if(FAILED(hr)) { OGRE_EXCEPT( hr, "Could not set best autogen filter type", "D3D9Texture::_createCubeTex" ); } } } /****************************************************************************************/ void D3D9Texture::_initDevice(void) { assert(mpDev); HRESULT hr; // get device caps hr = mpDev->GetDeviceCaps(&mDevCaps); if (FAILED(hr)) OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Can't get device description", "D3D9Texture::_setDevice" ); // get D3D pointer hr = mpDev->GetDirect3D(&mpD3D); // decrement reference count mpD3D->Release(); if (FAILED(hr)) OGRE_EXCEPT( hr, "Failed to get D3D9 pointer", "D3D9Texture::_setDevice" ); // get our device creation parameters hr = mpDev->GetCreationParameters(&mDevCreParams); if (FAILED(hr)) OGRE_EXCEPT( hr, "Failed to get D3D9 device creation parameters", "D3D9Texture::_setDevice" ); // get our back buffer pixel format IDirect3DSurface9 *pSrf; D3DSURFACE_DESC srfDesc; hr = mpDev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pSrf); // decrement reference count pSrf->Release(); if (FAILED(hr)) OGRE_EXCEPT( hr, "Failed to get D3D9 device pixel format", "D3D9Texture::_setDevice" ); hr = pSrf->GetDesc(&srfDesc); if (FAILED(hr)) { OGRE_EXCEPT( hr, "Failed to get D3D9 device pixel format", "D3D9Texture::_setDevice" ); } mBBPixelFormat = srfDesc.Format; } /****************************************************************************************/ void D3D9Texture::_setFinalAttributes(unsigned long width, unsigned long height, unsigned long depth, PixelFormat format) { // set target texture attributes mHeight = height; mWidth = width; mDepth = depth; mFormat = format; // Update size (the final size, not including temp space) // this is needed in Resource class unsigned short bytesPerPixel = mFinalBpp >> 3; if( !mHasAlpha && mFinalBpp == 32 ) bytesPerPixel--; mSize = mWidth * mHeight * mDepth * bytesPerPixel * (mTextureType == TEX_TYPE_CUBE_MAP)? 6 : 1; // say to the world what we are doing if (mWidth != mSrcWidth || mHeight != mSrcHeight) { LogManager::getSingleton().logMessage("D3D9 : ***** Dimensions altered by the render system"); LogManager::getSingleton().logMessage("D3D9 : ***** Source image dimensions : " + StringConverter::toString(mSrcWidth) + "x" + StringConverter::toString(mSrcHeight)); LogManager::getSingleton().logMessage("D3D9 : ***** Texture dimensions : " + StringConverter::toString(mWidth) + "x" + StringConverter::toString(mHeight)); } // Create list of subsurfaces for getBuffer() _createSurfaceList(); } /****************************************************************************************/ void D3D9Texture::_setSrcAttributes(unsigned long width, unsigned long height, unsigned long depth, PixelFormat format) { // set source image attributes mSrcWidth = width; mSrcHeight = height; mSrcBpp = PixelUtil::getNumElemBits(format); mHasAlpha = PixelUtil::getFlags(format) & PFF_HASALPHA; // say to the world what we are doing switch (this->getTextureType()) { case TEX_TYPE_1D: if (mUsage & TU_RENDERTARGET) LogManager::getSingleton().logMessage("D3D9 : Creating 1D RenderTarget, name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); else LogManager::getSingleton().logMessage("D3D9 : Loading 1D Texture, image name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); break; case TEX_TYPE_2D: if (mUsage & TU_RENDERTARGET) LogManager::getSingleton().logMessage("D3D9 : Creating 2D RenderTarget, name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); else LogManager::getSingleton().logMessage("D3D9 : Loading 2D Texture, image name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); break; case TEX_TYPE_3D: if (mUsage & TU_RENDERTARGET) LogManager::getSingleton().logMessage("D3D9 : Creating 3D RenderTarget, name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); else LogManager::getSingleton().logMessage("D3D9 : Loading 3D Texture, image name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); break; case TEX_TYPE_CUBE_MAP: if (mUsage & TU_RENDERTARGET) LogManager::getSingleton().logMessage("D3D9 : Creating Cube map RenderTarget, name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); else LogManager::getSingleton().logMessage("D3D9 : Loading Cube Texture, base image name : '" + this->getName() + "' with " + StringConverter::toString(mNumMipmaps) + " mip map levels"); break; default: this->freeInternalResources(); OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Unknown texture type", "D3D9Texture::_setSrcAttributes" ); } } /****************************************************************************************/ D3DTEXTUREFILTERTYPE D3D9Texture::_getBestFilterMethod() { // those MUST be initialized !!! assert(mpDev); assert(mpD3D); assert(mpTex); DWORD filterCaps = 0; // Minification filter is used for mipmap generation // Pick the best one supported for this tex type switch (this->getTextureType()) { case TEX_TYPE_1D: // Same as 2D case TEX_TYPE_2D: filterCaps = mDevCaps.TextureFilterCaps; break; case TEX_TYPE_3D: filterCaps = mDevCaps.VolumeTextureFilterCaps; break; case TEX_TYPE_CUBE_MAP: filterCaps = mDevCaps.CubeTextureFilterCaps; break; } if(filterCaps & D3DPTFILTERCAPS_MINFGAUSSIANQUAD) return D3DTEXF_GAUSSIANQUAD; if(filterCaps & D3DPTFILTERCAPS_MINFPYRAMIDALQUAD) return D3DTEXF_PYRAMIDALQUAD; if(filterCaps & D3DPTFILTERCAPS_MINFANISOTROPIC) return D3DTEXF_ANISOTROPIC; if(filterCaps & D3DPTFILTERCAPS_MINFLINEAR) return D3DTEXF_LINEAR; if(filterCaps & D3DPTFILTERCAPS_MINFPOINT) return D3DTEXF_POINT; return D3DTEXF_POINT; } /****************************************************************************************/ bool D3D9Texture::_canUseDynamicTextures(DWORD srcUsage, D3DRESOURCETYPE srcType, D3DFORMAT srcFormat) { // those MUST be initialized !!! assert(mpDev); assert(mpD3D); // Check for dynamic texture support HRESULT hr; // check for auto gen. mip maps support hr = mpD3D->CheckDeviceFormat( mDevCreParams.AdapterOrdinal, mDevCreParams.DeviceType, mBBPixelFormat, srcUsage | D3DUSAGE_DYNAMIC, srcType, srcFormat); if (hr == D3D_OK) return true; else return false; } /****************************************************************************************/ bool D3D9Texture::_canAutoGenMipmaps(DWORD srcUsage, D3DRESOURCETYPE srcType, D3DFORMAT srcFormat) { // those MUST be initialized !!! assert(mpDev); assert(mpD3D); // Hacky override - many (all?) cards seem to not be able to autogen on // textures which are not a power of two // Can we even mipmap on 3D textures? Well if ((mWidth & mWidth-1) || (mHeight & mHeight-1) || (mDepth & mDepth-1)) return false; if (mDevCaps.Caps2 & D3DCAPS2_CANAUTOGENMIPMAP) { HRESULT hr; // check for auto gen. mip maps support hr = mpD3D->CheckDeviceFormat( mDevCreParams.AdapterOrdinal, mDevCreParams.DeviceType, mBBPixelFormat, srcUsage | D3DUSAGE_AUTOGENMIPMAP, srcType, srcFormat); // this HR could a SUCCES // but mip maps will not be generated if (hr == D3D_OK) return true; else return false; } else return false; } /****************************************************************************************/ D3DFORMAT D3D9Texture::_chooseD3DFormat() { // Choose frame buffer pixel format in case PF_UNKNOWN was requested if(mFormat == PF_UNKNOWN) return mBBPixelFormat; // Choose closest supported D3D format as a D3D format return _getPF(_getClosestSupportedPF(mFormat)); } /****************************************************************************************/ void D3D9Texture::_createDepthStencil() { IDirect3DSurface9 *pSrf; D3DSURFACE_DESC srfDesc; HRESULT hr; /* Get the format of the depth stencil surface of our main render target. */ hr = mpDev->GetDepthStencilSurface(&pSrf); if (FAILED(hr)) { String msg = DXGetErrorDescription9(hr); this->freeInternalResources(); OGRE_EXCEPT( hr, "Error GetDepthStencilSurface : " + msg, "D3D9Texture::_createDepthStencil" ); } // get it's description hr = pSrf->GetDesc(&srfDesc); if (FAILED(hr)) { SAFE_RELEASE(pSrf); this->freeInternalResources(); String msg = DXGetErrorDescription9(hr); OGRE_EXCEPT( hr, "Error GetDesc : " + msg, "D3D9Texture::_createDepthStencil" ); } // release the temp. surface SAFE_RELEASE(pSrf); /** Create a depth buffer for our render target, it must be of the same format as other targets !!! . */ hr = mpDev->CreateDepthStencilSurface( mSrcWidth, mSrcHeight, srfDesc.Format, srfDesc.MultiSampleType, NULL, FALSE, &mpZBuff, NULL); // cry if failed if (FAILED(hr)) { this->freeInternalResources(); String msg = DXGetErrorDescription9(hr); OGRE_EXCEPT( hr, "Error CreateDepthStencilSurface : " + msg, "D3D9Texture::_createDepthStencil" ); } } /****************************************************************************************/ // Macro to hide ugly cast #define GETLEVEL(face,mip) \ static_cast(mSurfaceList[face*(mNumMipmaps+1)+mip].get()) void D3D9Texture::_createSurfaceList(void) { IDirect3DSurface9 *surface; IDirect3DVolume9 *volume; D3D9HardwarePixelBuffer *buffer; size_t mip, face; assert(mpTex); // Make sure number of mips is right mNumMipmaps = mpTex->GetLevelCount() - 1; // Need to know static / dynamic HardwareBuffer::Usage bufusage; if ((mUsage & TU_DYNAMIC) && mDynamicTextures) { bufusage = HardwareBuffer::HBU_DYNAMIC; } else { bufusage = HardwareBuffer::HBU_STATIC; } bool updateOldList = mSurfaceList.size() == (getNumFaces() * (mNumMipmaps + 1)); if(!updateOldList) { // Create new list of surfaces mSurfaceList.clear(); for(size_t face=0; faceGetSurfaceLevel(mip, &surface) != D3D_OK) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Get surface level failed", "D3D9Texture::_createSurfaceList"); // decrement reference count, the GetSurfaceLevel call increments this // this is safe because the texture keeps a reference as well surface->Release(); GETLEVEL(0, mip)->bind(mpDev, surface); } break; case TEX_TYPE_CUBE_MAP: assert(mpCubeTex); // For all faces and mipmaps, store surfaces as HardwarePixelBufferSharedPtr for(face=0; face<6; ++face) { for(mip=0; mip<=mNumMipmaps; ++mip) { if(mpCubeTex->GetCubeMapSurface((D3DCUBEMAP_FACES)face, mip, &surface) != D3D_OK) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Get cubemap surface failed", "D3D9Texture::getBuffer"); // decrement reference count, the GetSurfaceLevel call increments this // this is safe because the texture keeps a reference as well surface->Release(); GETLEVEL(face, mip)->bind(mpDev, surface); } } break; case TEX_TYPE_3D: assert(mpVolumeTex); // For all mipmaps, store surfaces as HardwarePixelBufferSharedPtr for(mip=0; mip<=mNumMipmaps; ++mip) { if(mpVolumeTex->GetVolumeLevel(mip, &volume) != D3D_OK) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Get volume level failed", "D3D9Texture::getBuffer"); // decrement reference count, the GetSurfaceLevel call increments this // this is safe because the texture keeps a reference as well volume->Release(); GETLEVEL(0, mip)->bind(mpDev, volume); } break; }; // Set autogeneration of mipmaps for each face of the texture, if it is enabled if(mNumRequestedMipmaps != 0 && (mUsage & TU_AUTOMIPMAP)) { for(face=0; face_setMipmapping(true, mMipmapsHardwareGenerated, mpTex); } } } #undef GETLEVEL /****************************************************************************************/ HardwarePixelBufferSharedPtr D3D9Texture::getBuffer(size_t face, size_t mipmap) { if(face >= getNumFaces()) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "A three dimensional cube has six faces", "D3D9Texture::getBuffer"); if(mipmap > mNumMipmaps) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Mipmap index out of range", "D3D9Texture::getBuffer"); size_t idx = face*(mNumMipmaps+1) + mipmap; assert(idx < mSurfaceList.size()); return mSurfaceList[idx]; } /****************************************************************************************/ PixelFormat D3D9Texture::_getPF(D3DFORMAT d3dPF) { switch(d3dPF) { case D3DFMT_A8: return PF_A8; case D3DFMT_L8: return PF_L8; case D3DFMT_L16: return PF_L16; case D3DFMT_A4L4: return PF_A4L4; case D3DFMT_A8L8: return PF_BYTE_LA; // Assume little endian here case D3DFMT_R3G3B2: return PF_R3G3B2; case D3DFMT_A1R5G5B5: return PF_A1R5G5B5; case D3DFMT_A4R4G4B4: return PF_A4R4G4B4; case D3DFMT_R5G6B5: return PF_R5G6B5; case D3DFMT_R8G8B8: return PF_R8G8B8; case D3DFMT_X8R8G8B8: return PF_X8R8G8B8; case D3DFMT_A8R8G8B8: return PF_A8R8G8B8; case D3DFMT_X8B8G8R8: return PF_X8B8G8R8; case D3DFMT_A8B8G8R8: return PF_A8B8G8R8; case D3DFMT_A2R10G10B10: return PF_A2R10G10B10; case D3DFMT_A2B10G10R10: return PF_A2B10G10R10; case D3DFMT_R16F: return PF_FLOAT16_R; case D3DFMT_A16B16G16R16F: return PF_FLOAT16_RGBA; case D3DFMT_R32F: return PF_FLOAT32_R; case D3DFMT_A32B32G32R32F: return PF_FLOAT32_RGBA; case D3DFMT_A16B16G16R16: return PF_SHORT_RGBA; case D3DFMT_DXT1: return PF_DXT1; case D3DFMT_DXT2: return PF_DXT2; case D3DFMT_DXT3: return PF_DXT3; case D3DFMT_DXT4: return PF_DXT4; case D3DFMT_DXT5: return PF_DXT5; default: return PF_UNKNOWN; } } /****************************************************************************************/ D3DFORMAT D3D9Texture::_getPF(PixelFormat ogrePF) { switch(ogrePF) { case PF_L8: return D3DFMT_L8; case PF_L16: return D3DFMT_L16; case PF_A8: return D3DFMT_A8; case PF_A4L4: return D3DFMT_A4L4; case PF_BYTE_LA: return D3DFMT_A8L8; // Assume little endian here case PF_R3G3B2: return D3DFMT_R3G3B2; case PF_A1R5G5B5: return D3DFMT_A1R5G5B5; case PF_R5G6B5: return D3DFMT_R5G6B5; case PF_A4R4G4B4: return D3DFMT_A4R4G4B4; case PF_R8G8B8: return D3DFMT_R8G8B8; case PF_A8R8G8B8: return D3DFMT_A8R8G8B8; case PF_A8B8G8R8: return D3DFMT_A8B8G8R8; case PF_X8R8G8B8: return D3DFMT_X8R8G8B8; case PF_X8B8G8R8: return D3DFMT_X8B8G8R8; case PF_A2B10G10R10: return D3DFMT_A2B10G10R10; case PF_A2R10G10B10: return D3DFMT_A2R10G10B10; case PF_FLOAT16_R: return D3DFMT_R16F; case PF_FLOAT16_RGBA: return D3DFMT_A16B16G16R16F; case PF_FLOAT32_R: return D3DFMT_R32F; case PF_FLOAT32_RGBA: return D3DFMT_A32B32G32R32F; case PF_SHORT_RGBA: return D3DFMT_A16B16G16R16; case PF_DXT1: return D3DFMT_DXT1; case PF_DXT2: return D3DFMT_DXT2; case PF_DXT3: return D3DFMT_DXT3; case PF_DXT4: return D3DFMT_DXT4; case PF_DXT5: return D3DFMT_DXT5; case PF_UNKNOWN: default: return D3DFMT_UNKNOWN; } } /****************************************************************************************/ PixelFormat D3D9Texture::_getClosestSupportedPF(PixelFormat ogrePF) { if (_getPF(ogrePF) != D3DFMT_UNKNOWN) { return ogrePF; } switch(ogrePF) { case PF_B5G6R5: return PF_R5G6B5; case PF_B8G8R8: return PF_R8G8B8; case PF_B8G8R8A8: return PF_A8R8G8B8; case PF_FLOAT16_RGB: return PF_FLOAT16_RGBA; case PF_FLOAT32_RGB: return PF_FLOAT32_RGBA; case PF_UNKNOWN: default: return PF_A8R8G8B8; } } /****************************************************************************************/ bool D3D9Texture::releaseIfDefaultPool(void) { if(mD3DPool == D3DPOOL_DEFAULT) { LogManager::getSingleton().logMessage( "Releasing D3D9 default pool texture: " + mName); // Just free any internal resources, don't call unload() here // because we want the un-touched resource to keep its unloaded status // after device reset. freeInternalResources(); LogManager::getSingleton().logMessage( "Released D3D9 default pool texture: " + mName); return true; } return false; } /****************************************************************************************/ bool D3D9Texture::recreateIfDefaultPool(LPDIRECT3DDEVICE9 pDev) { bool ret = false; if(mD3DPool == D3DPOOL_DEFAULT) { ret = true; LogManager::getSingleton().logMessage( "Recreating D3D9 default pool texture: " + mName); // We just want to create the texture resources if: // 1. This is a render texture, or // 2. This is a manual texture with no loader, or // 3. This was an unloaded regular texture (preserve unloaded state) if ((mIsManual && !mLoader) || (mUsage & TU_RENDERTARGET) || !mIsLoaded) { // just recreate any internal resources createInternalResources(); } // Otherwise, this is a regular loaded texture, or a manual texture with a loader else { // The internal resources already freed, need unload/load here: // 1. Make sure resource memory usage statistic correction. // 2. Don't call unload() in releaseIfDefaultPool() because we want // the un-touched resource keep unload status after device reset. unload(); // if manual, we need to recreate internal resources since load() won't do that if (mIsManual) createInternalResources(); load(); } LogManager::getSingleton().logMessage( "Recreated D3D9 default pool texture: " + mName); } return ret; } /****************************************************************************************/ }