//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "platform/platform.h" #include "gfx/gfxTextureObject.h" #include "gfx/gfxDevice.h" #include "gfx/gfxTextureManager.h" #include "core/util/safeDelete.h" #include "core/strings/stringFunctions.h" #include "core/stream/fileStream.h" #include "console/console.h" #include "console/engineAPI.h" // TODO: Change this to be in non-shipping builds maybe? #ifdef TORQUE_DEBUG GFXTextureObject *GFXTextureObject::smHead = NULL; U32 GFXTextureObject::smActiveTOCount = 0; U32 GFXTextureObject::dumpActiveTOs() { if(!smActiveTOCount) { Con::printf( "GFXTextureObject::dumpActiveTOs - no active TOs to dump." ); return 0; } Con::printf("GFXTextureObject Usage Report - %d active TOs", smActiveTOCount); Con::printf("---------------------------------------------------------------"); Con::printf(" Addr Dim. GFXTextureProfile ProfilerPath DebugDescription"); for(GFXTextureObject *walk = smHead; walk; walk=walk->mDebugNext) Con::printf(" %x (%4d, %4d) %s %s %s", walk, walk->getWidth(), walk->getHeight(), walk->mProfile->getName().c_str(), walk->mDebugCreationPath.c_str(), walk->mDebugDescription.c_str()); Con::printf("----- dump complete -------------------------------------------"); return smActiveTOCount; } #endif // TORQUE_DEBUG DefineEngineFunction( dumpTextureObjects, void, (),, "Dumps a list of all active texture objects to the console.\n" "@note This function is only available in debug builds.\n" "@ingroup GFX\n" ) { #ifdef TORQUE_DEBUG GFXTextureObject::dumpActiveTOs(); #endif } //----------------------------------------------------------------------------- // GFXTextureObject //----------------------------------------------------------------------------- GFXTextureObject::GFXTextureObject(GFXDevice *aDevice, GFXTextureProfile *aProfile) { mHashNext = mNext = mPrev = NULL; mDevice = aDevice; mProfile = aProfile; mBitmap = NULL; mMipLevels = 1; mAntialiasLevel = 0; mTextureSize.set( 0, 0, 0 ); mDead = false; mDeleteTime = 0; mBitmap = NULL; mDDS = NULL; mFormat = GFXFormatR8G8B8; mHasTransparency = false; #if defined(TORQUE_DEBUG) // Active object tracking. smActiveTOCount++; mDebugDescription = "Anonymous Texture Object"; #if defined(TORQUE_ENABLE_PROFILE_PATH) mDebugCreationPath = gProfiler->getProfilePath(); #endif mDebugNext = smHead; mDebugPrev = NULL; if(smHead) { AssertFatal(smHead->mDebugPrev == NULL, "GFXTextureObject::GFXTextureObject - found unexpected previous in current head!"); smHead->mDebugPrev = this; } smHead = this; #endif } //----------------------------------------------------------------------------- // Destructor //----------------------------------------------------------------------------- GFXTextureObject::~GFXTextureObject() { kill(); #ifdef TORQUE_DEBUG if(smHead == this) smHead = this->mDebugNext; if(mDebugNext) mDebugNext->mDebugPrev = mDebugPrev; if(mDebugPrev) mDebugPrev->mDebugNext = mDebugNext; mDebugPrev = mDebugNext = NULL; smActiveTOCount--; #endif } void GFXTextureObject::destroySelf() { mDevice->mTextureManager->requestDeleteTexture(this); } //----------------------------------------------------------------------------- // kill - this function clears out the data in texture object. It's done like // this because the texture object needs to release its pointers to textures // before the GFXDevice is shut down. The texture objects themselves get // deleted by the refcount structure - which may be after the GFXDevice has // been destroyed. //----------------------------------------------------------------------------- void GFXTextureObject::kill() { if( mDead ) return; #ifdef TORQUE_DEBUG // This makes sure that nobody is forgetting to call kill from the derived // destructor. If they are, then we should get a pure virtual function // call here. pureVirtualCrash(); #endif // If we're a dummy, don't do anything... if( !mDevice || !mDevice->mTextureManager ) { mDead = true; return; } // Remove ourselves from the texture list and hash mDevice->mTextureManager->deleteTexture(this); // Delete the stored bitmap. SAFE_DELETE(mBitmap) SAFE_DELETE(mDDS); // Clean up linked list if(mNext) mNext->mPrev = mPrev; if(mPrev) mPrev->mNext = mNext; mDead = true; } const String GFXTextureObject::describeSelf() const { return String::ToString(" (width: %4d, height: %4d) profile: %s creation path: %s", getWidth(), #if defined(TORQUE_DEBUG) && defined(TORQUE_ENABLE_PROFILER) getHeight(), mProfile->getName().c_str(), mDebugCreationPath.c_str()); #else getHeight(), mProfile->getName().c_str(), ""); #endif } F32 GFXTextureObject::getMaxUCoord() const { return 1.0f; } F32 GFXTextureObject::getMaxVCoord() const { return 1.0f; } U32 GFXTextureObject::getEstimatedSizeInBytes() const { // We have to deal with DDS specially. if ( mFormat >= GFXFormatBC1 ) return DDSFile::getSizeInBytes( mFormat, mTextureSize.x, mTextureSize.y, mMipLevels ); // Else we need to calculate the size ourselves. S32 texSizeX = mTextureSize.x; S32 texSizeY = mTextureSize.y; S32 volDepth = getMax( 1, mTextureSize.z ); U32 byteSize = GFXFormat_getByteSize( mFormat ); U32 totalBytes = texSizeX * texSizeY * volDepth * byteSize; // Without mips we're done. if ( mProfile->noMip() ) return totalBytes; // NOTE: While we have mMipLevels, at the time of this // comment it only stores the accessable mip levels and // not the count of the autogen mips. // // So we figure out the mip count ourselves assuming its // a complete mip chain. while ( texSizeX > 1 || texSizeY > 1 ) { texSizeX = getMax( texSizeX >> 1, 1 ); texSizeY = getMax( texSizeY >> 1, 1 ); volDepth = getMax( volDepth >> 1, 1 ); totalBytes += texSizeX * texSizeY * volDepth * byteSize; } return totalBytes; } bool GFXTextureObject::dumpToDisk( const String &bmType, const String &path ) { FileStream stream; if ( !stream.open( path, Torque::FS::File::Write ) ) return false; if ( mBitmap ) return mBitmap->writeBitmap( bmType, stream ); GBitmap bitmap( getWidth(), getHeight(), false, getFormat() ); copyToBmp( &bitmap ); return bitmap.writeBitmap( bmType, stream ); }