/* ----------------------------------------------------------------------------- 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 "OgreD3D9HardwarePixelBuffer.h" #include "OgreD3D9Texture.h" #include "OgreException.h" #include "OgreLogManager.h" #include "OgreStringConverter.h" #include "OgreBitwise.h" #include "OgreNoMemoryMacros.h" #include #include #include "OgreMemoryMacros.h" namespace Ogre { //----------------------------------------------------------------------------- D3D9HardwarePixelBuffer::D3D9HardwarePixelBuffer(HardwareBuffer::Usage usage): HardwarePixelBuffer(0, 0, 0, PF_UNKNOWN, usage, false, false), mpDev(0), mSurface(0), mVolume(0), mTempSurface(0), mTempVolume(0), mDoMipmapGen(0), mHWMipmaps(0), mMipTex(0) { } D3D9HardwarePixelBuffer::~D3D9HardwarePixelBuffer() { } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::bind(IDirect3DDevice9 *dev, IDirect3DSurface9 *surface) { mpDev = dev; mSurface = surface; D3DSURFACE_DESC desc; if(mSurface->GetDesc(&desc) != D3D_OK) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Could not get surface information", "D3D9HardwarePixelBuffer::D3D9HardwarePixelBuffer"); mWidth = desc.Width; mHeight = desc.Height; mDepth = 1; mFormat = D3D9Texture::_getPF(desc.Format); // Default mRowPitch = mWidth; mSlicePitch = mHeight*mWidth; mSizeInBytes = PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat); } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::bind(IDirect3DDevice9 *dev, IDirect3DVolume9 *volume) { mpDev = dev; mVolume = volume; D3DVOLUME_DESC desc; if(mVolume->GetDesc(&desc) != D3D_OK) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Could not get volume information", "D3D9HardwarePixelBuffer::D3D9HardwarePixelBuffer"); mWidth = desc.Width; mHeight = desc.Height; mDepth = desc.Depth; mFormat = D3D9Texture::_getPF(desc.Format); // Default mRowPitch = mWidth; mSlicePitch = mHeight*mWidth; mSizeInBytes = PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat); } //----------------------------------------------------------------------------- // Util functions to convert a D3D locked box to a pixel box void fromD3DLock(PixelBox &rval, const D3DLOCKED_RECT &lrect) { rval.rowPitch = lrect.Pitch / PixelUtil::getNumElemBytes(rval.format); rval.slicePitch = rval.rowPitch * rval.getHeight(); assert((lrect.Pitch % PixelUtil::getNumElemBytes(rval.format))==0); rval.data = lrect.pBits; } void fromD3DLock(PixelBox &rval, const D3DLOCKED_BOX &lbox) { rval.rowPitch = lbox.RowPitch / PixelUtil::getNumElemBytes(rval.format); rval.slicePitch = lbox.SlicePitch / PixelUtil::getNumElemBytes(rval.format); assert((lbox.RowPitch % PixelUtil::getNumElemBytes(rval.format))==0); assert((lbox.SlicePitch % PixelUtil::getNumElemBytes(rval.format))==0); rval.data = lbox.pBits; } // Convert Ogre integer Box to D3D rectangle RECT toD3DRECT(const Box &lockBox) { RECT prect; assert(lockBox.getDepth() == 1); prect.left = lockBox.left; prect.right = lockBox.right; prect.top = lockBox.top; prect.bottom = lockBox.bottom; return prect; } // Convert Ogre integer Box to D3D box D3DBOX toD3DBOX(const Box &lockBox) { D3DBOX pbox; pbox.Left = lockBox.left; pbox.Right = lockBox.right; pbox.Top = lockBox.top; pbox.Bottom = lockBox.bottom; pbox.Front = lockBox.front; pbox.Back = lockBox.back; return pbox; } // Convert Ogre pixelbox extent to D3D rectangle RECT toD3DRECTExtent(const PixelBox &lockBox) { RECT prect; assert(lockBox.getDepth() == 1); prect.left = 0; prect.right = lockBox.getWidth(); prect.top = 0; prect.bottom = lockBox.getHeight(); return prect; } // Convert Ogre pixelbox extent to D3D box D3DBOX toD3DBOXExtent(const PixelBox &lockBox) { D3DBOX pbox; pbox.Left = 0; pbox.Right = lockBox.getWidth(); pbox.Top = 0; pbox.Bottom = lockBox.getHeight(); pbox.Front = 0; pbox.Back = lockBox.getDepth(); return pbox; } //----------------------------------------------------------------------------- PixelBox D3D9HardwarePixelBuffer::lockImpl(const Image::Box lockBox, LockOptions options) { // Set extents and format PixelBox rval(lockBox, mFormat); // Set locking flags according to options DWORD flags = 0; switch(options) { case HBL_DISCARD: // D3D only likes D3DLOCK_DISCARD if you created the texture with D3DUSAGE_DYNAMIC // debug runtime flags this up, could cause problems on some drivers if (mUsage & HBU_DYNAMIC) flags |= D3DLOCK_DISCARD; break; case HBL_READ_ONLY: flags |= D3DLOCK_READONLY; break; default: break; }; if(mSurface) { // Surface D3DLOCKED_RECT lrect; // Filled in by D3D HRESULT hr; if (lockBox.left == 0 && lockBox.top == 0 && lockBox.right == mWidth && lockBox.bottom == mHeight) { // Lock whole surface hr = mSurface->LockRect(&lrect, NULL, flags); } else { RECT prect = toD3DRECT(lockBox); // specify range to lock hr = mSurface->LockRect(&lrect, &prect, flags); } if (FAILED(hr)) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Surface locking failed", "D3D9HardwarePixelBuffer::lockImpl"); fromD3DLock(rval, lrect); } else { // Volume D3DBOX pbox = toD3DBOX(lockBox); // specify range to lock D3DLOCKED_BOX lbox; // Filled in by D3D if(mVolume->LockBox(&lbox, &pbox, flags) != D3D_OK) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Volume locking failed", "D3D9HardwarePixelBuffer::lockImpl"); fromD3DLock(rval, lbox); } return rval; } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::unlockImpl(void) { if(mSurface) { // Surface mSurface->UnlockRect(); } else { // Volume mVolume->UnlockBox(); } if(mDoMipmapGen) _genMipmaps(); } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::blit(HardwarePixelBuffer *rsrc, const Image::Box &srcBox, const Image::Box &dstBox) { D3D9HardwarePixelBuffer *src = static_cast(rsrc); if(mSurface && src->mSurface) { // Surface-to-surface RECT dsrcRect = toD3DRECT(srcBox); RECT ddestRect = toD3DRECT(dstBox); // D3DXLoadSurfaceFromSurface if(D3DXLoadSurfaceFromSurface( mSurface, NULL, &ddestRect, src->mSurface, NULL, &dsrcRect, D3DX_DEFAULT, 0) != D3D_OK) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3DXLoadSurfaceFromSurface failed", "D3D9HardwarePixelBuffer::blit"); } } else if(mVolume && src->mVolume) { // Volume-to-volume D3DBOX dsrcBox = toD3DBOX(srcBox); D3DBOX ddestBox = toD3DBOX(dstBox); // D3DXLoadVolumeFromVolume if(D3DXLoadVolumeFromVolume( mVolume, NULL, &ddestBox, src->mVolume, NULL, &dsrcBox, D3DX_DEFAULT, 0) != D3D_OK) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3DXLoadVolumeFromVolume failed", "D3D9HardwarePixelBuffer::blit"); } } else { HardwarePixelBuffer::blit(src, srcBox, dstBox); } } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox) { // for scoped deletion of conversion buffer MemoryDataStreamPtr buf; PixelBox converted = src; // convert to pixelbuffer's native format if necessary if (D3D9Texture::_getPF(src.format) == D3DFMT_UNKNOWN) { buf.bind(new MemoryDataStream( PixelUtil::getMemorySize(src.getWidth(), src.getHeight(), src.getDepth(), mFormat))); converted = PixelBox(src.getWidth(), src.getHeight(), src.getDepth(), mFormat, buf->getPtr()); PixelUtil::bulkPixelConversion(src, converted); } if(mSurface) { RECT destRect, srcRect; srcRect = toD3DRECTExtent(converted); destRect = toD3DRECT(dstBox); if(D3DXLoadSurfaceFromMemory(mSurface, NULL, &destRect, converted.data, D3D9Texture::_getPF(converted.format), converted.rowPitch * PixelUtil::getNumElemBytes(converted.format), NULL, &srcRect, D3DX_DEFAULT, 0) != D3D_OK) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3DXLoadSurfaceFromMemory failed", "D3D9HardwarePixelBuffer::blitFromMemory"); } } else { D3DBOX destBox, srcBox; srcBox = toD3DBOXExtent(converted); destBox = toD3DBOX(dstBox); if(D3DXLoadVolumeFromMemory(mVolume, NULL, &destBox, converted.data, D3D9Texture::_getPF(converted.format), converted.rowPitch * PixelUtil::getNumElemBytes(converted.format), converted.slicePitch * PixelUtil::getNumElemBytes(converted.format), NULL, &srcBox, D3DX_DEFAULT, 0) != D3D_OK) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3DXLoadSurfaceFromMemory failed", "D3D9HardwarePixelBuffer::blitFromMemory"); } } if(mDoMipmapGen) _genMipmaps(); } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::blitToMemory(const Image::Box &srcBox, const PixelBox &dst) { // Decide on pixel format of temp surface PixelFormat tmpFormat = mFormat; if(D3D9Texture::_getPF(dst.format) != D3DFMT_UNKNOWN) { tmpFormat = dst.format; } if(mSurface) { assert(srcBox.getDepth() == 1 && dst.getDepth() == 1); // Create temp texture IDirect3DTexture9 *tmp; IDirect3DSurface9 *surface; if(D3DXCreateTexture( mpDev, dst.getWidth(), dst.getHeight(), 1, // 1 mip level ie topmost, generate no mipmaps 0, D3D9Texture::_getPF(tmpFormat), D3DPOOL_SCRATCH, &tmp ) != D3D_OK) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Create temporary texture failed", "D3D9HardwarePixelBuffer::blitToMemory"); } if(tmp->GetSurfaceLevel(0, &surface) != D3D_OK) { tmp->Release(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Get surface level failed", "D3D9HardwarePixelBuffer::blitToMemory"); } // Copy texture to this temp surface RECT destRect, srcRect; srcRect = toD3DRECT(srcBox); destRect = toD3DRECTExtent(dst); if(D3DXLoadSurfaceFromSurface( surface, NULL, &destRect, mSurface, NULL, &srcRect, D3DX_DEFAULT, 0) != D3D_OK) { surface->Release(); tmp->Release(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3DXLoadSurfaceFromSurface failed", "D3D9HardwarePixelBuffer::blitToMemory"); } // Lock temp surface and copy it to memory D3DLOCKED_RECT lrect; // Filled in by D3D if(surface->LockRect(&lrect, NULL, D3DLOCK_READONLY) != D3D_OK) { surface->Release(); tmp->Release(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "surface->LockRect", "D3D9HardwarePixelBuffer::blitToMemory"); } // Copy it PixelBox locked(dst.getWidth(), dst.getHeight(), dst.getDepth(), tmpFormat); fromD3DLock(locked, lrect); PixelUtil::bulkPixelConversion(locked, dst); surface->UnlockRect(); // Release temporary surface and texture surface->Release(); tmp->Release(); } else { // Create temp texture IDirect3DVolumeTexture9 *tmp; IDirect3DVolume9 *surface; if(D3DXCreateVolumeTexture( mpDev, dst.getWidth(), dst.getHeight(), dst.getDepth(), 0, 0, D3D9Texture::_getPF(tmpFormat), D3DPOOL_SCRATCH, &tmp ) != D3D_OK) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Create temporary texture failed", "D3D9HardwarePixelBuffer::blitToMemory"); } if(tmp->GetVolumeLevel(0, &surface) != D3D_OK) { tmp->Release(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Get volume level failed", "D3D9HardwarePixelBuffer::blitToMemory"); } // Volume D3DBOX ddestBox, dsrcBox; ddestBox = toD3DBOXExtent(dst); dsrcBox = toD3DBOX(srcBox); if(D3DXLoadVolumeFromVolume( surface, NULL, &ddestBox, mVolume, NULL, &dsrcBox, D3DX_DEFAULT, 0) != D3D_OK) { surface->Release(); tmp->Release(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3DXLoadVolumeFromVolume failed", "D3D9HardwarePixelBuffer::blitToMemory"); } // Lock temp surface and copy it to memory D3DLOCKED_BOX lbox; // Filled in by D3D if(surface->LockBox(&lbox, NULL, D3DLOCK_READONLY) != D3D_OK) { surface->Release(); tmp->Release(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "surface->LockBox", "D3D9HardwarePixelBuffer::blitToMemory"); } // Copy it PixelBox locked(dst.getWidth(), dst.getHeight(), dst.getDepth(), tmpFormat); fromD3DLock(locked, lbox); PixelUtil::bulkPixelConversion(locked, dst); surface->UnlockBox(); // Release temporary surface and texture surface->Release(); tmp->Release(); } } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::_genMipmaps() { assert(mMipTex); // Mipmapping if (mHWMipmaps) { // Hardware mipmaps mMipTex->GenerateMipSubLevels(); } else { // Software mipmaps if( D3DXFilterTexture( mMipTex, NULL, D3DX_DEFAULT, D3DX_DEFAULT ) != D3D_OK ) { OGRE_EXCEPT( Exception::ERR_RENDERINGAPI_ERROR, "Failed to filter texture (generate mipmaps)", "D3D9HardwarePixelBuffer::_genMipmaps" ); } } } //----------------------------------------------------------------------------- void D3D9HardwarePixelBuffer::_setMipmapping(bool doMipmapGen, bool HWMipmaps, IDirect3DBaseTexture9 *mipTex) { mDoMipmapGen = doMipmapGen; mHWMipmaps = HWMipmaps; mMipTex = mipTex; } };