//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//===========================================================================//

#define DISABLE_PROTECTED_THINGS
#include "locald3dtypes.h"
#include "texturedx8.h"
#include "shaderapidx8_global.h"
#include "colorformatdx8.h"
#include "shaderapi/ishaderutil.h"
#include "materialsystem/imaterialsystem.h"
#include "utlvector.h"
#include "recording.h"
#include "shaderapi/ishaderapi.h"
#include "filesystem.h"
#include "locald3dtypes.h"
#include "textureheap.h"
#include "tier1/utlbuffer.h"
#include "tier1/callqueue.h"
#include "tier0/vprof.h"
#include "vtf/vtf.h"
#include "tier0/icommandline.h"

#include "tier0/memdbgon.h"

#ifdef _WIN32
#pragma warning (disable:4189 4701)
#endif

static int s_TextureCount = 0;
static bool s_bTestingVideoMemorySize = false;

//-----------------------------------------------------------------------------
// Stats...
//-----------------------------------------------------------------------------

int TextureCount()
{
	return s_TextureCount;
}

static bool IsVolumeTexture( IDirect3DBaseTexture* pBaseTexture )
{
	if ( !pBaseTexture )
	{
		return false;
	}

	return ( pBaseTexture->GetType() == D3DRTYPE_VOLUMETEXTURE );
}

static HRESULT GetLevelDesc( IDirect3DBaseTexture* pBaseTexture, UINT level, D3DSURFACE_DESC* pDesc )
{
	MEM_ALLOC_D3D_CREDIT();

	if ( !pBaseTexture )
	{
		return ( HRESULT )-1;
	}

	HRESULT hr;
	switch( pBaseTexture->GetType() )
	{
	case D3DRTYPE_TEXTURE:
		hr = ( ( IDirect3DTexture * )pBaseTexture )->GetLevelDesc( level, pDesc );
		break;
	case D3DRTYPE_CUBETEXTURE:
		hr = ( ( IDirect3DCubeTexture * )pBaseTexture )->GetLevelDesc( level, pDesc );
		break;
	default:
		return ( HRESULT )-1;
	}
	return hr;
}

static HRESULT GetSurfaceFromTexture( IDirect3DBaseTexture* pBaseTexture, UINT level, 
									  D3DCUBEMAP_FACES cubeFaceID, IDirect3DSurface** ppSurfLevel )
{
	MEM_ALLOC_D3D_CREDIT();

	if ( !pBaseTexture )
	{
		return ( HRESULT )-1;
	}

	HRESULT hr;

	switch( pBaseTexture->GetType() )
	{
	case D3DRTYPE_TEXTURE:
		hr = ( ( IDirect3DTexture * )pBaseTexture )->GetSurfaceLevel( level, ppSurfLevel );
		break;
	case D3DRTYPE_CUBETEXTURE:
		if (cubeFaceID !=0)
		{
			//Debugger();
		}
		
		hr = ( ( IDirect3DCubeTexture * )pBaseTexture )->GetCubeMapSurface( cubeFaceID, level, ppSurfLevel );
		break;
	default:
		Assert(0);
		return ( HRESULT )-1;
	}
	return hr;
}

//-----------------------------------------------------------------------------
// Gets the image format of a texture
//-----------------------------------------------------------------------------
static ImageFormat GetImageFormat( IDirect3DBaseTexture* pTexture )
{
	MEM_ALLOC_D3D_CREDIT();

	if ( pTexture )
	{
		HRESULT hr;
		if ( !IsVolumeTexture( pTexture ) )
		{
			D3DSURFACE_DESC desc;
			hr = GetLevelDesc( pTexture, 0, &desc );
			if ( !FAILED( hr ) )
				return ImageLoader::D3DFormatToImageFormat( desc.Format );
		}
		else
		{
			D3DVOLUME_DESC desc;
			IDirect3DVolumeTexture *pVolumeTexture = static_cast<IDirect3DVolumeTexture*>( pTexture );
			hr = pVolumeTexture->GetLevelDesc( 0, &desc );
			if ( !FAILED( hr ) )
				return ImageLoader::D3DFormatToImageFormat( desc.Format );
		}
	}

	// Bogus baby!
	return (ImageFormat)-1;
}


//-----------------------------------------------------------------------------
// Allocates the D3DTexture
//-----------------------------------------------------------------------------
IDirect3DBaseTexture* CreateD3DTexture( int width, int height, int nDepth, 
		ImageFormat dstFormat, int numLevels, int nCreationFlags, char *debugLabel )		// OK to skip the last param
{
	if ( nDepth <= 0 )
	{
		nDepth = 1;
	}

	bool isCubeMap = ( nCreationFlags & TEXTURE_CREATE_CUBEMAP ) != 0;
	bool bIsRenderTarget = ( nCreationFlags & TEXTURE_CREATE_RENDERTARGET ) != 0;
	bool bManaged = ( nCreationFlags & TEXTURE_CREATE_MANAGED ) != 0;
	bool bSysmem = ( nCreationFlags & TEXTURE_CREATE_SYSMEM ) != 0;
	bool bIsDepthBuffer = ( nCreationFlags & TEXTURE_CREATE_DEPTHBUFFER ) != 0;
	bool isDynamic = ( nCreationFlags & TEXTURE_CREATE_DYNAMIC ) != 0;
	bool bAutoMipMap = ( nCreationFlags & TEXTURE_CREATE_AUTOMIPMAP ) != 0;
	bool bVertexTexture = ( nCreationFlags & TEXTURE_CREATE_VERTEXTEXTURE ) != 0;
	bool bAllowNonFilterable = ( nCreationFlags & TEXTURE_CREATE_UNFILTERABLE_OK ) != 0;
	bool bVolumeTexture = ( nDepth > 1 );
	bool bIsFallback = ( nCreationFlags & TEXTURE_CREATE_FALLBACK ) != 0;
	bool bNoD3DBits = ( nCreationFlags & TEXTURE_CREATE_NOD3DMEMORY ) != 0;
	bool bSRGB = (nCreationFlags & TEXTURE_CREATE_SRGB) != 0;			// for Posix/GL only

	// NOTE: This function shouldn't be used for creating depth buffers!
	Assert( !bIsDepthBuffer );

	D3DFORMAT d3dFormat = D3DFMT_UNKNOWN;

	D3DPOOL pool = bManaged ? D3DPOOL_MANAGED : D3DPOOL_DEFAULT;
	if ( bSysmem )
		pool = D3DPOOL_SYSTEMMEM;

	if ( IsX360() )
	{
		// 360 does not support vertex textures
		// 360 render target creation path is for the target as a texture source (NOT the EDRAM version)
		// use normal texture format rules
		Assert( !bVertexTexture );
		if ( !bVertexTexture )
		{
			d3dFormat = ImageLoader::ImageFormatToD3DFormat( FindNearestSupportedFormat( dstFormat, false, false, false ) );
		}
	}
	else
	{
		d3dFormat = ImageLoader::ImageFormatToD3DFormat( FindNearestSupportedFormat( dstFormat, bVertexTexture, bIsRenderTarget, bAllowNonFilterable ) );
	}

	if ( d3dFormat == D3DFMT_UNKNOWN )
	{
		Warning( "ShaderAPIDX8::CreateD3DTexture: Invalid color format!\n" );
		Assert( 0 );
		return 0;
	}

	IDirect3DBaseTexture* pBaseTexture = NULL;
	IDirect3DTexture* pD3DTexture = NULL;
	IDirect3DCubeTexture* pD3DCubeTexture = NULL;
	IDirect3DVolumeTexture* pD3DVolumeTexture = NULL;
	HRESULT hr = S_OK;
	DWORD usage = 0;

	if ( bIsRenderTarget )
	{
		usage |= D3DUSAGE_RENDERTARGET;
	}
	if ( isDynamic )
	{
		usage |= D3DUSAGE_DYNAMIC;
	}
	if ( bAutoMipMap )
	{
		usage |= D3DUSAGE_AUTOGENMIPMAP;
	}

#ifdef DX_TO_GL_ABSTRACTION
	{
		if (bSRGB)
		{
			usage |= D3DUSAGE_TEXTURE_SRGB;		// does not exist in real DX9... just for GL to know that this is an SRGB tex
		}
	}
#endif

	if ( isCubeMap )
	{
#if !defined( _X360 )
		hr = Dx9Device()->CreateCubeTexture( 
				width,
				numLevels,
				usage,
				d3dFormat,
				pool, 
				&pD3DCubeTexture,
				NULL
	#if defined( DX_TO_GL_ABSTRACTION )			
				, debugLabel					// tex create funcs take extra arg for debug name on GL
	#endif
				   );
#else
		pD3DCubeTexture = g_TextureHeap.AllocCubeTexture( width, numLevels, usage, d3dFormat, bIsFallback, bNoD3DBits );
#endif
		pBaseTexture = pD3DCubeTexture;
	}
	else if ( bVolumeTexture )
	{
#if !defined( _X360 )
		hr = Dx9Device()->CreateVolumeTexture( 
				width, 
				height, 
				nDepth,
				numLevels, 
				usage, 
				d3dFormat, 
				pool, 
				&pD3DVolumeTexture,
				NULL
	#if defined( DX_TO_GL_ABSTRACTION )			
				, debugLabel					// tex create funcs take extra arg for debug name on GL
	#endif
				  );
#else
		Assert( !bIsFallback && !bNoD3DBits );
		pD3DVolumeTexture = g_TextureHeap.AllocVolumeTexture( width, height, nDepth, numLevels, usage, d3dFormat );
#endif
		pBaseTexture = pD3DVolumeTexture;
	}
	else
	{
#if !defined( _X360 )
		// Override usage and managed params if using special hardware shadow depth map formats...
		if ( ( d3dFormat == NVFMT_RAWZ ) || ( d3dFormat == NVFMT_INTZ   ) || 
		     ( d3dFormat == D3DFMT_D16 ) || ( d3dFormat == D3DFMT_D24S8 ) || 
			 ( d3dFormat == ATIFMT_D16 ) || ( d3dFormat == ATIFMT_D24S8 ) )
		{
			// Not putting D3DUSAGE_RENDERTARGET here causes D3D debug spew later, but putting the flag causes this create to fail...
			usage = D3DUSAGE_DEPTHSTENCIL;
			bManaged = false;
		}

		// Override managed param if using special null texture format
		if ( d3dFormat == NVFMT_NULL )
		{
			bManaged = false;
		}

		hr = Dx9Device()->CreateTexture(
				width,
				height,
				numLevels, 
				usage,
				d3dFormat,
				pool,
				&pD3DTexture,
				NULL
	#if defined( DX_TO_GL_ABSTRACTION )			
				, debugLabel					// tex create funcs take extra arg for debug name on GL
	#endif
				 );

#else
		pD3DTexture = g_TextureHeap.AllocTexture( width, height, numLevels, usage, d3dFormat, bIsFallback, bNoD3DBits );
#endif
		pBaseTexture = pD3DTexture;
	}

    if ( FAILED( hr ) )
	{
#ifdef ENABLE_NULLREF_DEVICE_SUPPORT
		if( CommandLine()->FindParm( "-nulldevice" ) )
		{
			Warning( "ShaderAPIDX8::CreateD3DTexture: Null device used. Texture not created.\n" );
			return 0;
		}
#endif

		switch ( hr )
		{
		case D3DERR_INVALIDCALL:
			Warning( "ShaderAPIDX8::CreateD3DTexture: D3DERR_INVALIDCALL\n" );
			break;
		case D3DERR_OUTOFVIDEOMEMORY:
			// This conditional is here so that we don't complain when testing
			// how much video memory we have. . this is kinda gross.
			if ( !s_bTestingVideoMemorySize )
			{
				Warning( "ShaderAPIDX8::CreateD3DTexture: D3DERR_OUTOFVIDEOMEMORY\n" );
			}
			break;
		case E_OUTOFMEMORY:
			Warning( "ShaderAPIDX8::CreateD3DTexture: E_OUTOFMEMORY\n" );
			break;
		default:
			break;
		}
		return 0;
	}

#ifdef MEASURE_DRIVER_ALLOCATIONS
	int nMipCount = numLevels;
	if ( !nMipCount )
	{
		while ( width > 1 || height > 1 )
		{
			width >>= 1;
			height >>= 1;
			++nMipCount;
		}
	}

	int nMemUsed = nMipCount * 1.1f * 1024;
	if ( isCubeMap )
	{
		nMemUsed *= 6;
	}

	VPROF_INCREMENT_GROUP_COUNTER( "texture count", COUNTER_GROUP_NO_RESET, 1 );
	VPROF_INCREMENT_GROUP_COUNTER( "texture driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
	VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
#endif

	++s_TextureCount;

	return pBaseTexture;
}


//-----------------------------------------------------------------------------
// Texture destruction
//-----------------------------------------------------------------------------
void ReleaseD3DTexture( IDirect3DBaseTexture* pD3DTex )
{
	int ref = pD3DTex->Release();
	Assert( ref == 0 );
}

void DestroyD3DTexture( IDirect3DBaseTexture* pD3DTex )
{
	if ( pD3DTex )
	{
#ifdef MEASURE_DRIVER_ALLOCATIONS
		D3DRESOURCETYPE type = pD3DTex->GetType();
		int nMipCount = pD3DTex->GetLevelCount();
		if ( type == D3DRTYPE_CUBETEXTURE )
		{
			nMipCount *= 6;
		}
		int nMemUsed = nMipCount * 1.1f * 1024;
		VPROF_INCREMENT_GROUP_COUNTER( "texture count", COUNTER_GROUP_NO_RESET, -1 );
		VPROF_INCREMENT_GROUP_COUNTER( "texture driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
		VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
#endif

#if !defined( _X360 )
		CMatRenderContextPtr pRenderContext( materials );
		ICallQueue *pCallQueue;
		if ( ( pCallQueue = pRenderContext->GetCallQueue() ) != NULL )
		{
			pCallQueue->QueueCall( ReleaseD3DTexture, pD3DTex );
		}
		else
		{
			ReleaseD3DTexture( pD3DTex );
		}
#else
		g_TextureHeap.FreeTexture( pD3DTex );
#endif
		--s_TextureCount;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTex - 
// Output : int
//-----------------------------------------------------------------------------
int GetD3DTextureRefCount( IDirect3DBaseTexture *pTex )
{
	if ( !pTex )
		return 0;

	pTex->AddRef();
	int ref = pTex->Release();

	return ref;
}

//-----------------------------------------------------------------------------
// See version 13 for a function that converts a texture to a mipmap (ConvertToMipmap)
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Lock, unlock a texture...
//-----------------------------------------------------------------------------

static RECT s_LockedSrcRect;
static D3DLOCKED_RECT s_LockedRect;
#ifdef DBGFLAG_ASSERT
static bool s_bInLock = false;
#endif

bool LockTexture( ShaderAPITextureHandle_t bindId, int copy, IDirect3DBaseTexture* pTexture, int level, 
	D3DCUBEMAP_FACES cubeFaceID, int xOffset, int yOffset, int width, int height, bool bDiscard,
	CPixelWriter& writer )
{
	Assert( !s_bInLock );
	
	IDirect3DSurface* pSurf;
	HRESULT hr = GetSurfaceFromTexture( pTexture, level, cubeFaceID, &pSurf );
	if ( FAILED( hr ) )
		return false;

	s_LockedSrcRect.left = xOffset;
	s_LockedSrcRect.right = xOffset + width;
	s_LockedSrcRect.top = yOffset;
	s_LockedSrcRect.bottom = yOffset + height;

	unsigned int flags = D3DLOCK_NOSYSLOCK;
	flags |= bDiscard ? D3DLOCK_DISCARD : 0;
	RECORD_COMMAND( DX8_LOCK_TEXTURE, 6 );
	RECORD_INT( bindId );
	RECORD_INT( copy );
	RECORD_INT( level );
	RECORD_INT( cubeFaceID );
	RECORD_STRUCT( &s_LockedSrcRect, sizeof(s_LockedSrcRect) );
	RECORD_INT( flags );

	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "D3DLockTexture" );

	hr = pSurf->LockRect( &s_LockedRect, &s_LockedSrcRect, flags );
	pSurf->Release();

	if ( FAILED( hr ) )
		return false;

	writer.SetPixelMemory( GetImageFormat(pTexture), s_LockedRect.pBits, s_LockedRect.Pitch );

#ifdef DBGFLAG_ASSERT
	s_bInLock = true;
#endif
	return true;
}

void UnlockTexture( ShaderAPITextureHandle_t bindId, int copy, IDirect3DBaseTexture* pTexture, int level, 
	D3DCUBEMAP_FACES cubeFaceID )
{
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );

	Assert( s_bInLock );

	IDirect3DSurface* pSurf;
	HRESULT hr = GetSurfaceFromTexture( pTexture, level, cubeFaceID, &pSurf );
	if (FAILED(hr))
		return;

#ifdef RECORD_TEXTURES 
	int width = s_LockedSrcRect.right - s_LockedSrcRect.left;
	int height = s_LockedSrcRect.bottom - s_LockedSrcRect.top;
	int imageFormatSize = ImageLoader::SizeInBytes( GetImageFormat( pTexture ) );
	Assert( imageFormatSize != 0 );
	int validDataBytesPerRow = imageFormatSize * width;
	int storeSize = validDataBytesPerRow * height;
	static CUtlVector< unsigned char > tmpMem;
	if( tmpMem.Size() < storeSize )
	{
		tmpMem.AddMultipleToTail( storeSize - tmpMem.Size() );
	}
	unsigned char *pDst = tmpMem.Base();
	unsigned char *pSrc = ( unsigned char * )s_LockedRect.pBits;
	RECORD_COMMAND( DX8_SET_TEXTURE_DATA, 3 );
	RECORD_INT( validDataBytesPerRow );
	RECORD_INT( height );
	int i;
	for( i = 0; i < height; i++ )
	{
		memcpy( pDst, pSrc, validDataBytesPerRow );
		pDst += validDataBytesPerRow;
		pSrc += s_LockedRect.Pitch;
	}
	RECORD_STRUCT( tmpMem.Base(), storeSize );
#endif // RECORD_TEXTURES 
	
	RECORD_COMMAND( DX8_UNLOCK_TEXTURE, 4 );
	RECORD_INT( bindId );
	RECORD_INT( copy );
	RECORD_INT( level );
	RECORD_INT( cubeFaceID );

	hr = pSurf->UnlockRect();
	pSurf->Release();
#ifdef DBGFLAG_ASSERT
	s_bInLock = false;
#endif
}

//-----------------------------------------------------------------------------
// Compute texture size based on compression
//-----------------------------------------------------------------------------

static inline int DetermineGreaterPowerOfTwo( int val )
{
	int num = 1;
	while (val > num)
	{
		num <<= 1;
	}

	return num;
}

inline int DeterminePowerOfTwo( int val )
{
	int pow = 0;
	while ((val & 0x1) == 0x0)
	{
		val >>= 1;
		++pow;
	}

	return pow;
}


//-----------------------------------------------------------------------------
// Blit in bits
//-----------------------------------------------------------------------------
// NOTE: IF YOU CHANGE THIS, CHANGE THE VERSION IN PLAYBACK.CPP!!!!
// OPTIMIZE??: could lock the texture directly instead of the surface in dx9.
#if !defined( _X360 )
static void BlitSurfaceBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride )
{
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );

	// Get the level of the texture we want to write into
	IDirect3DSurface* pTextureLevel;

	if (info.m_CubeFaceID !=0)
	{
		//Debugger();
	}


	HRESULT hr = GetSurfaceFromTexture( info.m_pTexture, info.m_nLevel, info.m_CubeFaceID, &pTextureLevel );
	if ( FAILED( hr ) )
		return;

	RECT			srcRect;
	RECT			*pSrcRect = NULL;
	D3DLOCKED_RECT	lockedRect;

	srcRect.left   = xOffset;
	srcRect.right  = xOffset + info.m_nWidth;
	srcRect.top    = yOffset;
	srcRect.bottom = yOffset + info.m_nHeight;

#if defined( SHADERAPIDX9 ) && !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
	if ( !info.m_bTextureIsLockable )
	{
		// Copy from system memory to video memory using D3D9Device->UpdateSurface
		bool bSuccess = false;

		D3DSURFACE_DESC desc;
		Verify( pTextureLevel->GetDesc( &desc ) == S_OK );
		ImageFormat dstFormat = ImageLoader::D3DFormatToImageFormat( desc.Format );
		D3DFORMAT dstFormatD3D = ImageLoader::ImageFormatToD3DFormat( dstFormat );

		IDirect3DSurface* pSrcSurface = NULL;
		bool bCopyBitsToSrcSurface = true;

#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9)
		// D3D9Ex fast path: create a texture wrapping our own system memory buffer
		// if the source and destination formats are exactly the same and the stride
		// is tightly packed. no locking/blitting required.
		// NOTE: the fast path does not work on sub-4x4 DXT compressed textures.
		extern bool g_ShaderDeviceUsingD3D9Ex;
		if ( g_ShaderDeviceUsingD3D9Ex &&
			( info.m_SrcFormat == dstFormat || ( info.m_SrcFormat == IMAGE_FORMAT_DXT1_ONEBITALPHA && dstFormat == IMAGE_FORMAT_DXT1 ) ) &&
			( !ImageLoader::IsCompressed( dstFormat ) || (info.m_nWidth >= 4 || info.m_nHeight >= 4) ) )
		{
			if ( srcStride == 0 || srcStride == info.m_nWidth * ImageLoader::SizeInBytes( info.m_SrcFormat ) )
			{
				IDirect3DTexture9* pTempTex = NULL;
				if ( Dx9Device()->CreateTexture( info.m_nWidth, info.m_nHeight, 1, 0, dstFormatD3D, D3DPOOL_SYSTEMMEM, &pTempTex, (HANDLE*) &info.m_pSrcData ) == S_OK )
				{
					IDirect3DSurface* pTempSurf = NULL;
					if ( pTempTex->GetSurfaceLevel( 0, &pTempSurf ) == S_OK )
					{
						pSrcSurface = pTempSurf;
						bCopyBitsToSrcSurface = false;
					}
					pTempTex->Release();
				}
			}
		}
#endif

		// If possible to create a texture of this size, create a temporary texture in
		// system memory and then use the UpdateSurface method to copy between textures.
		if ( !pSrcSurface && ( g_pHardwareConfig->Caps().m_SupportsNonPow2Textures ||
							( IsPowerOfTwo( info.m_nWidth ) && IsPowerOfTwo( info.m_nHeight ) ) ) )
		{
			int tempW = info.m_nWidth, tempH = info.m_nHeight, mip = 0;
			if ( info.m_nLevel > 0 && ( ( tempW | tempH ) & 3 ) && ImageLoader::IsCompressed( dstFormat ) )
			{
				// Loading lower mip levels of DXT compressed textures is sort of tricky
				// because we can't create textures that aren't multiples of 4, and we can't
				// pass subrectangles of DXT textures into UpdateSurface. Create a temporary
				// texture which is 1 or 2 mip levels larger and then lock the appropriate
				// mip level to grab its correctly-dimensioned surface. -henryg 11/18/2011
				mip = ( info.m_nLevel > 1 && ( ( tempW | tempH ) & 1 ) ) ? 2 : 1;
				tempW <<= mip;
				tempH <<= mip;
			}
			
			IDirect3DTexture9* pTempTex = NULL;
			IDirect3DSurface* pTempSurf = NULL;
			if ( Dx9Device()->CreateTexture( tempW, tempH, mip+1, 0, dstFormatD3D, D3DPOOL_SYSTEMMEM, &pTempTex, NULL ) == S_OK )
			{
				if ( pTempTex->GetSurfaceLevel( mip, &pTempSurf ) == S_OK )
				{
					pSrcSurface = pTempSurf;
					bCopyBitsToSrcSurface = true;
				}
				pTempTex->Release();
			}
		}

		// Create an offscreen surface if the texture path wasn't an option.
		if ( !pSrcSurface )
		{
			IDirect3DSurface* pTempSurf = NULL;
			if ( Dx9Device()->CreateOffscreenPlainSurface( info.m_nWidth, info.m_nHeight, dstFormatD3D, D3DPOOL_SYSTEMMEM, &pTempSurf, NULL ) == S_OK )
			{
				pSrcSurface = pTempSurf;
				bCopyBitsToSrcSurface = true;
			}
		}

		// Lock and fill the surface
		if ( bCopyBitsToSrcSurface && pSrcSurface )
		{
			if ( pSrcSurface->LockRect( &lockedRect, NULL, D3DLOCK_NOSYSLOCK ) == S_OK )
			{
				unsigned char *pImage = (unsigned char *)lockedRect.pBits;
				ShaderUtil()->ConvertImageFormat( info.m_pSrcData, info.m_SrcFormat,
					pImage, dstFormat, info.m_nWidth, info.m_nHeight, srcStride, lockedRect.Pitch );
				pSrcSurface->UnlockRect();
			}
			else
			{
				// Lock failed.
				pSrcSurface->Release();
				pSrcSurface = NULL;
			}
		}
	
		// Perform the UpdateSurface call that blits between system and video memory
		if ( pSrcSurface )
		{
			POINT pt = { xOffset, yOffset };
			bSuccess = ( Dx9Device()->UpdateSurface( pSrcSurface, NULL, pTextureLevel, &pt ) == S_OK );
			pSrcSurface->Release();
		}
		
		if ( !bSuccess )
		{
			Warning( "CShaderAPIDX8::BlitTextureBits: couldn't lock texture rect or use UpdateSurface\n" );
		}

		pTextureLevel->Release();
		return;
	}
#endif

	Assert( info.m_bTextureIsLockable );

#ifndef RECORD_TEXTURES
	RECORD_COMMAND( DX8_LOCK_TEXTURE, 6 );
	RECORD_INT( info.m_TextureHandle );
	RECORD_INT( info.m_nCopy );
	RECORD_INT( info.m_nLevel );
	RECORD_INT( info.m_CubeFaceID );
	RECORD_STRUCT( &srcRect, sizeof(srcRect) );
	RECORD_INT( D3DLOCK_NOSYSLOCK );
#endif

	{
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - D3DLockRect", __FUNCTION__ );

		// lock the region (could be the full surface or less)
		if ( FAILED( pTextureLevel->LockRect( &lockedRect, &srcRect, D3DLOCK_NOSYSLOCK ) ) )
		{
			Warning( "CShaderAPIDX8::BlitTextureBits: couldn't lock texture rect\n" );
			pTextureLevel->Release();
			return;
		}
	}

	{
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - ConvertImageFormat", __FUNCTION__ );

		// garymcthack : need to make a recording command for this.
		ImageFormat dstFormat = GetImageFormat( info.m_pTexture );
		unsigned char *pImage = (unsigned char *)lockedRect.pBits;
		ShaderUtil()->ConvertImageFormat( info.m_pSrcData, info.m_SrcFormat,
							pImage, dstFormat, info.m_nWidth, info.m_nHeight, srcStride, lockedRect.Pitch );
	}

#ifndef RECORD_TEXTURES
	RECORD_COMMAND( DX8_UNLOCK_TEXTURE, 4 );
	RECORD_INT( info.m_TextureHandle );
	RECORD_INT( info.m_nCopy );
	RECORD_INT( info.m_nLevel );
	RECORD_INT( info.m_CubeFaceID );
#endif

	{
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UnlockRect", __FUNCTION__ );

		if ( FAILED( pTextureLevel->UnlockRect() ) ) 
		{
			Warning( "CShaderAPIDX8::BlitTextureBits: couldn't unlock texture rect\n" );
			pTextureLevel->Release();
			return;
		}
	}

	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - pTextureLevel->Release", __FUNCTION__ );
	pTextureLevel->Release();
}
#endif

//-----------------------------------------------------------------------------
// Puts 2D texture data into 360 gpu memory.
//-----------------------------------------------------------------------------
#if defined( _X360 )
static void BlitSurfaceBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride )
{
	// xbox textures are NOT backed in gpu memory contiguously
	// stride details are critical - see [Xbox 360 Texture Storage]
	// a d3dformat identifier on the xbox is tiled, the same d3dformat on the pc is expected linear to the app
	// we purposely hide the tiling here, otherwise much confusion for the pc
	// the *entire* target must be un-tiled *only* before any *subrect* blitting linear work
	// the *entire* target must then be re-tiled after the *subrect* blit
	// procedural textures require this to subrect blit their new portions correctly
	// the tiling dance can be avoided if the source and target match in tiled state during a full rect blit

	if ( info.m_bSrcIsTiled )
	{
		// not supporting subrect blitting from a tiled source
		Assert( 0 );
		return;
	}

	CUtlBuffer formatConvertMemory;
	unsigned char *pSrcData = info.m_pSrcData;

	ImageFormat	dstFormat = GetImageFormat( info.m_pTexture );
	if ( dstFormat != info.m_SrcFormat )
	{
		if ( !info.m_bCanConvertFormat )
		{
			// texture is expected to be in target format
			// not supporting conversion of a tiled source
			Assert( 0 );
			return;
		}

		int srcSize = ImageLoader::GetMemRequired( info.m_nWidth, info.m_nHeight, 1, info.m_SrcFormat, false );
		int dstSize = ImageLoader::GetMemRequired( info.m_nWidth, info.m_nHeight, 1, dstFormat, false );
		formatConvertMemory.EnsureCapacity( dstSize );

		// due to format conversion, source is in non-native order
		ImageLoader::PreConvertSwapImageData( (unsigned char*)info.m_pSrcData, srcSize, info.m_SrcFormat, info.m_nWidth, srcStride );

		// slow conversion operation
		if ( !ShaderUtil()->ConvertImageFormat( 
				info.m_pSrcData,
				info.m_SrcFormat,
				(unsigned char*)formatConvertMemory.Base(),
				dstFormat,
				info.m_nWidth,
				info.m_nHeight,
				srcStride,
				0 ) )
		{
			// conversion failed
			Assert( 0 );
			return;
		}

		// due to format conversion, source must have been in non-native order
		ImageLoader::PostConvertSwapImageData( (unsigned char*)formatConvertMemory.Base(), dstSize, dstFormat );

		pSrcData = (unsigned char*)formatConvertMemory.Base();
	}

	// get the top mip level info (needed for proper sub mip access)
	XGTEXTURE_DESC baseDesc;
	XGGetTextureDesc( info.m_pTexture, 0, &baseDesc );
	bool bDstIsTiled = XGIsTiledFormat( baseDesc.Format ) == TRUE;

	// get the target mip level info
	XGTEXTURE_DESC mipDesc;
	XGGetTextureDesc( info.m_pTexture, info.m_nLevel, &mipDesc );
	bool bFullSurfBlit = ( mipDesc.Width == (unsigned)info.m_nWidth && mipDesc.Height == (unsigned)info.m_nHeight );

	// get the mip level of the texture we want to write into
	IDirect3DSurface* pTextureLevel;
	HRESULT hr = GetSurfaceFromTexture( info.m_pTexture, info.m_nLevel, info.m_CubeFaceID, &pTextureLevel );
	if ( FAILED( hr ) )
	{
		Warning( "CShaderAPIDX8::BlitTextureBits: GetSurfaceFromTexture() failure\n" );
		return;
	}

	CUtlBuffer scratchMemory;
	D3DLOCKED_RECT lockedRect;

	hr = pTextureLevel->LockRect( &lockedRect, NULL, D3DLOCK_NOSYSLOCK );
	if ( FAILED( hr ) )
	{
		Warning( "CShaderAPIDX8::BlitTextureBits: couldn't lock texture rect\n" );
		goto cleanUp;
	}
	unsigned char *pTargetImage = (unsigned char *)lockedRect.pBits;

	POINT p;
	p.x = xOffset;
	p.y = yOffset;

	RECT r;
	r.left = 0;
	r.top = 0;
	r.right = info.m_nWidth;
	r.bottom = info.m_nHeight;

	int blockSize = mipDesc.Width/mipDesc.WidthInBlocks;
	if ( !srcStride )
	{
		srcStride = (mipDesc.Width/blockSize)*mipDesc.BytesPerBlock;
	}

	// subrect blitting path
	if ( !bDstIsTiled )
	{
		// Copy the subrect without conversion
		hr = XGCopySurface(
				pTargetImage,
				mipDesc.RowPitch,
				mipDesc.Width,
				mipDesc.Height,
				mipDesc.Format,
				&p,
				pSrcData,
				srcStride,
				mipDesc.Format,
				&r,
				0,
				0 );
		if ( FAILED( hr ) )
		{
			Warning( "CShaderAPIDX8::BlitTextureBits: failed subrect copy\n" );
			goto cleanUp;
		}
	}
	else
	{
		int tileFlags = 0;
		if ( !( mipDesc.Flags & XGTDESC_PACKED ) )
			tileFlags |= XGTILE_NONPACKED;
		if ( mipDesc.Flags & XGTDESC_BORDERED )
			tileFlags |= XGTILE_BORDER;

		// tile the temp store back into the target surface
		XGTileTextureLevel(
			baseDesc.Width,
			baseDesc.Height,
			info.m_nLevel,
			XGGetGpuFormat( baseDesc.Format ),
			tileFlags,
			pTargetImage,
			&p,
			pSrcData,
			srcStride,
			&r );
	}

	hr = pTextureLevel->UnlockRect();
	if ( FAILED( hr ) ) 
	{
		Warning( "CShaderAPIDX8::BlitTextureBits: couldn't unlock texture rect\n" );
		goto cleanUp;
	}

cleanUp:
	pTextureLevel->Release();
}
#endif

//-----------------------------------------------------------------------------
// Blit in bits
//-----------------------------------------------------------------------------
#if !defined( _X360 )
static void BlitVolumeBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride )
{
	D3DBOX srcBox;
	D3DLOCKED_BOX lockedBox;
	srcBox.Left = xOffset;
	srcBox.Right = xOffset + info.m_nWidth;
	srcBox.Top = yOffset;
	srcBox.Bottom = yOffset + info.m_nHeight;
	srcBox.Front = info.m_nZOffset;
	srcBox.Back = info.m_nZOffset + 1;

#ifndef RECORD_TEXTURES
	RECORD_COMMAND( DX8_LOCK_TEXTURE, 6 );
	RECORD_INT( info.m_TextureHandle );
	RECORD_INT( info.m_nCopy );
	RECORD_INT( info.m_nLevel );
	RECORD_INT( info.m_CubeFaceID );
	RECORD_STRUCT( &srcRect, sizeof(srcRect) );
	RECORD_INT( D3DLOCK_NOSYSLOCK );
#endif

	IDirect3DVolumeTexture *pVolumeTexture = static_cast<IDirect3DVolumeTexture*>( info.m_pTexture );
	if ( FAILED( pVolumeTexture->LockBox( info.m_nLevel, &lockedBox, &srcBox, D3DLOCK_NOSYSLOCK ) ) )
	{
		Warning( "BlitVolumeBits: couldn't lock volume texture rect\n" );
		return;
	}

	// garymcthack : need to make a recording command for this.
	ImageFormat dstFormat = GetImageFormat( info.m_pTexture );
	unsigned char *pImage = (unsigned char *)lockedBox.pBits;
	ShaderUtil()->ConvertImageFormat( info.m_pSrcData, info.m_SrcFormat,
						pImage, dstFormat, info.m_nWidth, info.m_nHeight, srcStride, lockedBox.RowPitch );

#ifndef RECORD_TEXTURES
	RECORD_COMMAND( DX8_UNLOCK_TEXTURE, 4 );
	RECORD_INT( info.m_TextureHandle );
	RECORD_INT( info.m_nCopy );
	RECORD_INT( info.m_nLevel );
	RECORD_INT( info.m_CubeFaceID );
#endif

	if ( FAILED( pVolumeTexture->UnlockBox( info.m_nLevel ) ) ) 
	{
		Warning( "BlitVolumeBits: couldn't unlock volume texture rect\n" );
		return;
	}
}
#endif

//-----------------------------------------------------------------------------
// Puts 3D texture data into 360 gpu memory.
// Does not support any subvolume or slice blitting.
//-----------------------------------------------------------------------------
#if defined( _X360 )
static void BlitVolumeBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride )
{
	if ( xOffset || yOffset || info.m_nZOffset || srcStride )
	{
		// not supporting any subvolume blitting
		// the entire volume per mip must be blitted
		Assert( 0 );
		return;
	}

	ImageFormat	dstFormat = GetImageFormat( info.m_pTexture );
	if ( dstFormat != info.m_SrcFormat )
	{
		// texture is expected to be in target format
		// not supporting conversion
		Assert( 0 );
		return;
	}

	// get the top mip level info (needed for proper sub mip access)
	XGTEXTURE_DESC baseDesc;
	XGGetTextureDesc( info.m_pTexture, 0, &baseDesc );
	bool bDstIsTiled = XGIsTiledFormat( baseDesc.Format ) == TRUE;
	if ( info.m_bSrcIsTiled && !bDstIsTiled )
	{
		// not supporting a tiled source into an untiled target
		Assert( 0 );
		return;
	}

	// get the mip level info
	XGTEXTURE_DESC mipDesc;
	XGGetTextureDesc( info.m_pTexture, info.m_nLevel, &mipDesc );
	bool bFullSurfBlit = ( mipDesc.Width == (unsigned int)info.m_nWidth && mipDesc.Height == (unsigned int)info.m_nHeight );

	if ( !bFullSurfBlit )
	{
		// not supporting subrect blitting
		Assert( 0 );
		return;
	}

	D3DLOCKED_BOX lockedBox;

	// get the mip level of the volume we want to write into
	IDirect3DVolumeTexture *pVolumeTexture = static_cast<IDirect3DVolumeTexture*>( info.m_pTexture );
	HRESULT hr = pVolumeTexture->LockBox( info.m_nLevel, &lockedBox, NULL, D3DLOCK_NOSYSLOCK );
	if ( FAILED( hr ) )
	{
		Warning( "CShaderAPIDX8::BlitVolumeBits: Couldn't lock volume box\n" );
		return;
	}

	unsigned char *pSrcData = info.m_pSrcData;
	unsigned char *pTargetImage = (unsigned char *)lockedBox.pBits;

	int tileFlags = 0;
	if ( !( mipDesc.Flags & XGTDESC_PACKED ) )
		tileFlags |= XGTILE_NONPACKED;
	if ( mipDesc.Flags & XGTDESC_BORDERED )
		tileFlags |= XGTILE_BORDER;

	if ( !info.m_bSrcIsTiled && bDstIsTiled )
	{
		// tile the source directly into the target surface
		XGTileVolumeTextureLevel(
			baseDesc.Width,
			baseDesc.Height,
			baseDesc.Depth,
			info.m_nLevel,
			XGGetGpuFormat( baseDesc.Format ),
			tileFlags,
			pTargetImage,
			NULL,
			pSrcData,
			mipDesc.RowPitch,
			mipDesc.SlicePitch,
			NULL );
	}
	else if ( !info.m_bSrcIsTiled && !bDstIsTiled )
	{
		// not implemented yet
		Assert( 0 );
	}
	else
	{
		// not implemented yet
		Assert( 0 );
	}

	hr = pVolumeTexture->UnlockBox( info.m_nLevel );
	if ( FAILED( hr ) )
	{
		Warning( "CShaderAPIDX8::BlitVolumeBits: couldn't unlock volume box\n" );
		return;
	}
}
#endif

// FIXME: How do I blit from D3DPOOL_SYSTEMMEM to D3DPOOL_MANAGED?  I used to use CopyRects for this.  UpdateSurface doesn't work because it can't blit to anything besides D3DPOOL_DEFAULT.
// We use this only in the case where we need to create a < 4x4 miplevel for a compressed texture.  We end up creating a 4x4 system memory texture, and blitting it into the proper miplevel.
// 6) LockRects should be used for copying between SYSTEMMEM and
// MANAGED.  For such a small copy, you'd avoid a significant 
// amount of overhead from the old CopyRects code.  Ideally, you 
// should just lock the bottom of MANAGED and generate your 
// sub-4x4 data there.
	
// NOTE: IF YOU CHANGE THIS, CHANGE THE VERSION IN PLAYBACK.CPP!!!!
static void BlitTextureBits( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride )
{
#ifdef RECORD_TEXTURES
	RECORD_COMMAND( DX8_BLIT_TEXTURE_BITS, 14 );
	RECORD_INT( info.m_TextureHandle );
	RECORD_INT( info.m_nCopy );
	RECORD_INT( info.m_nLevel );
	RECORD_INT( info.m_CubeFaceID );
	RECORD_INT( xOffset );
	RECORD_INT( yOffset );
	RECORD_INT( info.m_nZOffset );
	RECORD_INT( info.m_nWidth );
	RECORD_INT( info.m_nHeight );
	RECORD_INT( info.m_SrcFormat );
	RECORD_INT( srcStride );
	RECORD_INT( GetImageFormat( info.m_pTexture ) );
	// strides are in bytes.
	int srcDataSize;
	if ( srcStride == 0 )
	{
		srcDataSize = ImageLoader::GetMemRequired( info.m_nWidth, info.m_nHeight, 1, info.m_SrcFormat, false );
	}
	else
	{
		srcDataSize = srcStride * info.m_nHeight;
	}
	RECORD_INT( srcDataSize );
	RECORD_STRUCT( info.m_pSrcData, srcDataSize );
#endif // RECORD_TEXTURES
	
	if ( !IsVolumeTexture( info.m_pTexture ) )
	{
		Assert( info.m_nZOffset == 0 );
		BlitSurfaceBits( info, xOffset, yOffset, srcStride );
	}
	else
	{
		BlitVolumeBits( info, xOffset, yOffset, srcStride );
	}
}

//-----------------------------------------------------------------------------
// Texture image upload
//-----------------------------------------------------------------------------
void LoadTexture( TextureLoadInfo_t &info )
{
	MEM_ALLOC_D3D_CREDIT();

	Assert( info.m_pSrcData );
	Assert( info.m_pTexture );

#ifdef _DEBUG
	ImageFormat format = GetImageFormat( info.m_pTexture );
	Assert( (format != -1) && (format == FindNearestSupportedFormat( format, false, false, false )) );
#endif

	// Copy in the bits...
	BlitTextureBits( info, 0, 0, 0 );
}

void LoadVolumeTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame )
{
	if ( !info.m_pTexture || info.m_pTexture->GetType() != D3DRTYPE_VOLUMETEXTURE )
	{
		Assert( 0 );
		return;
	}

	IDirect3DVolumeTexture9 *pVolTex = static_cast<IDirect3DVolumeTexture*>( info.m_pTexture );

	D3DVOLUME_DESC desc;
	if ( pVolTex->GetLevelDesc( 0, &desc ) != S_OK )
	{
		Warning( "LoadVolumeTextureFromVTF: couldn't get texture level description\n" );
		return;
	}

	int iMipCount = pVolTex->GetLevelCount();
	if ( pVTF->Depth() != (int)desc.Depth || pVTF->Width() != (int)desc.Width || pVTF->Height() != (int)desc.Height || pVTF->MipCount() < iMipCount )
	{
		Warning( "LoadVolumeTextureFromVTF: VTF dimensions do not match texture\n" );
		return;
	}

	TextureLoadInfo_t sliceInfo = info;

#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
	IDirect3DVolumeTexture9 *pStagingTexture = NULL;
	if ( !info.m_bTextureIsLockable )
	{
		IDirect3DVolumeTexture9 *pTemp;
		if ( Dx9Device()->CreateVolumeTexture( desc.Width, desc.Height, desc.Depth, iMipCount, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTemp, NULL ) != S_OK )
		{
			Warning( "LoadVolumeTextureFromVTF: failed to create temporary staging texture\n" );
			return;
		}
		sliceInfo.m_pTexture = static_cast<IDirect3DBaseTexture*>( pTemp );
		sliceInfo.m_bTextureIsLockable = true;
		pStagingTexture = pTemp;
	}
#endif

	for ( int iMip = 0; iMip < iMipCount; ++iMip )
	{
		int w, h, d;
		pVTF->ComputeMipLevelDimensions( iMip, &w, &h, &d );
		sliceInfo.m_nLevel = iMip;
		sliceInfo.m_nWidth = w;
		sliceInfo.m_nHeight = h;
		for ( int iSlice = 0; iSlice < d; ++iSlice )
		{
			sliceInfo.m_nZOffset = iSlice;
			sliceInfo.m_pSrcData = pVTF->ImageData( iVTFFrame, 0, iMip, 0, 0, iSlice );
			BlitTextureBits( sliceInfo, 0, 0, 0 );
		}
	}

#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
	if ( pStagingTexture )
	{
		if ( Dx9Device()->UpdateTexture( pStagingTexture, pVolTex ) != S_OK )
		{
			Warning( "LoadVolumeTextureFromVTF: volume UpdateTexture failed\n" );
		}
		pStagingTexture->Release();
	}
#endif
}

void LoadCubeTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame )
{
	if ( !info.m_pTexture || info.m_pTexture->GetType() != D3DRTYPE_CUBETEXTURE )
	{
		Assert( 0 );
		return;
	}

	IDirect3DCubeTexture9 *pCubeTex = static_cast<IDirect3DCubeTexture9*>( info.m_pTexture );

	D3DSURFACE_DESC desc;
	if ( pCubeTex->GetLevelDesc( 0, &desc ) != S_OK )
	{
		Warning( "LoadCubeTextureFromVTF: couldn't get texture level description\n" );
		return;
	}

	int iMipCount = pCubeTex->GetLevelCount();
	if ( pVTF->Depth() != 1 || pVTF->Width() != (int)desc.Width || pVTF->Height() != (int)desc.Height || pVTF->FaceCount() < 6 || pVTF->MipCount() < iMipCount )
	{
		Warning( "LoadCubeTextureFromVTF: VTF dimensions do not match texture\n" );
		return;
	}

	TextureLoadInfo_t faceInfo = info;

#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
	IDirect3DCubeTexture9 *pStagingTexture = NULL;
	if ( !info.m_bTextureIsLockable )
	{
		IDirect3DCubeTexture9 *pTemp;
		if ( Dx9Device()->CreateCubeTexture( desc.Width, iMipCount, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTemp, NULL ) != S_OK )
		{
			Warning( "LoadCubeTextureFromVTF: failed to create temporary staging texture\n" );
			return;
		}
		faceInfo.m_pTexture = static_cast<IDirect3DBaseTexture*>( pTemp );
		faceInfo.m_bTextureIsLockable = true;
		pStagingTexture = pTemp;
	}
#endif

	for ( int iMip = 0; iMip < iMipCount; ++iMip )
	{
		int w, h, d;
		pVTF->ComputeMipLevelDimensions( iMip, &w, &h, &d );
		faceInfo.m_nLevel = iMip;
		faceInfo.m_nWidth = w;
		faceInfo.m_nHeight = h;
		for ( int iFace = 0; iFace < 6; ++iFace )
		{
			faceInfo.m_CubeFaceID = (D3DCUBEMAP_FACES) iFace;
			faceInfo.m_pSrcData = pVTF->ImageData( iVTFFrame, iFace, iMip );
			BlitTextureBits( faceInfo, 0, 0, 0 );
		}
	}

#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
	if ( pStagingTexture )
	{
		if ( Dx9Device()->UpdateTexture( pStagingTexture, pCubeTex ) != S_OK )
		{
			Warning( "LoadCubeTextureFromVTF: cube UpdateTexture failed\n" );
		}
		pStagingTexture->Release();
	}
#endif
}

void LoadTextureFromVTF( TextureLoadInfo_t &info, IVTFTexture* pVTF, int iVTFFrame )
{
	TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );

	if ( !info.m_pTexture || info.m_pTexture->GetType() != D3DRTYPE_TEXTURE )
	{
		Assert( 0 );
		return;
	}

	IDirect3DTexture9 *pTex = static_cast<IDirect3DTexture9*>( info.m_pTexture );

	D3DSURFACE_DESC desc;
	{
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - GetLevelDesc", __FUNCTION__ );
		if ( pTex->GetLevelDesc( 0, &desc ) != S_OK )
		{
			Warning( "LoadTextureFromVTF: couldn't get texture level description\n" );
			return;
		}
	}

	int iMipCount = pTex->GetLevelCount();
	if ( pVTF->Depth() != 1 || pVTF->Width() != (int)desc.Width || pVTF->Height() != (int)desc.Height || pVTF->MipCount() < iMipCount || pVTF->FaceCount() <= (int)info.m_CubeFaceID )
	{
		Warning( "LoadTextureFromVTF: VTF dimensions do not match texture\n" );
		return;
	}

	// Info may have a cube face ID if we are falling back to 2D sphere map support
	TextureLoadInfo_t mipInfo = info;
	int iVTFFaceNum = info.m_CubeFaceID;
	mipInfo.m_CubeFaceID = (D3DCUBEMAP_FACES)0;

#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
	// If blitting more than one mip level of an unlockable texture, create a temporary
	// texture for all mip levels only call UpdateTexture once. For textures with
	// only a single mip level, fall back on the support in BlitSurfaceBits. -henryg
	IDirect3DTexture9 *pStagingTexture = NULL;
	if ( !info.m_bTextureIsLockable && iMipCount > 1 )
	{
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - CreateSysmemTexture", __FUNCTION__ );
		
		IDirect3DTexture9 *pTemp;
		if ( Dx9Device()->CreateTexture( desc.Width, desc.Height, iMipCount, 0, desc.Format, D3DPOOL_SYSTEMMEM, &pTemp, NULL ) != S_OK )
		{
			Warning( "LoadTextureFromVTF: failed to create temporary staging texture\n" );
			return;
		}

		mipInfo.m_pTexture = static_cast<IDirect3DBaseTexture*>( pTemp );
		mipInfo.m_bTextureIsLockable = true;
		pStagingTexture = pTemp;
	}
#endif

	// Get the clamped resolutions from the VTF, then apply any clamping we've done from the higher level code.
	// (For example, we chop off the bottom of the mipmap pyramid at 32x32--that is reflected in iMipCount, so 
	// honor that here).
	int finest = 0, coarsest = 0;
	pVTF->GetMipmapRange( &finest, &coarsest );
	finest = Min( finest, iMipCount - 1 );
	coarsest = Min( coarsest, iMipCount - 1 );
	Assert( finest <= coarsest && coarsest < iMipCount );

	{
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - BlitTextureBits", __FUNCTION__ );
		for ( int iMip = finest; iMip <= coarsest; ++iMip )
		{
			int w, h, d;
			pVTF->ComputeMipLevelDimensions( iMip, &w, &h, &d );
			mipInfo.m_nLevel = iMip;
			mipInfo.m_nWidth = w;
			mipInfo.m_nHeight = h;
			mipInfo.m_pSrcData = pVTF->ImageData( iVTFFrame, iVTFFaceNum, iMip );

			tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - BlitTextureBits - %d", __FUNCTION__, iMip );

			BlitTextureBits( mipInfo, 0, 0, 0 );
		}
	}

#if !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
	if ( pStagingTexture )
	{
		if ( ( coarsest - finest + 1 ) == iMipCount )
		{
			tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateTexture", __FUNCTION__ );
			if ( Dx9Device()->UpdateTexture( pStagingTexture, pTex ) != S_OK )
			{
				Warning( "LoadTextureFromVTF: UpdateTexture failed\n" );
			}
		}
		else
		{
			tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateSurface", __FUNCTION__ );

			for ( int mip = finest; mip <= coarsest; ++mip )
			{
				tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateSurface - %d", __FUNCTION__, mip );

				IDirect3DSurface9 *pSrcSurf = NULL, 
					              *pDstSurf = NULL;

				if ( pStagingTexture->GetSurfaceLevel( mip, &pSrcSurf ) != S_OK )
					Warning( "LoadTextureFromVTF: couldn't get surface level %d for system surface\n", mip );

				if ( pTex->GetSurfaceLevel( mip, &pDstSurf ) != S_OK )
					Warning( "LoadTextureFromVTF: couldn't get surface level %d for dest surface\n", mip );

				{
					tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - UpdateSurface - Call ", __FUNCTION__, mip );
					if ( !pSrcSurf || !pDstSurf || Dx9Device()->UpdateSurface( pSrcSurf, NULL, pDstSurf, NULL ) != S_OK ) 
						Warning( "LoadTextureFromVTF: surface update failed.\n" );
				}

				if ( pSrcSurf )
					pSrcSurf->Release();

				if ( pDstSurf )
					pDstSurf->Release();
			}
		}

		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Cleanup", __FUNCTION__ );
		pStagingTexture->Release();
	}
#endif
}


//-----------------------------------------------------------------------------
// Upload to a sub-piece of a texture
//-----------------------------------------------------------------------------
void LoadSubTexture( TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride )
{
	Assert( info.m_pSrcData );
	Assert( info.m_pTexture );

#if defined( _X360 )
	// xboxissue - not supporting subrect swizzling
	Assert( !info.m_bSrcIsTiled );
#endif

#ifdef _DEBUG
	ImageFormat format = GetImageFormat( info.m_pTexture );
	Assert( (format == FindNearestSupportedFormat(format, false, false, false )) && (format != -1) );
#endif

	// Copy in the bits...
	BlitTextureBits( info, xOffset, yOffset, srcStride );
}


//-----------------------------------------------------------------------------
// Returns the size of texture memory, in MB
//-----------------------------------------------------------------------------
// Helps with startup time.. we don't use the texture memory size for anything anyways
#define DONT_CHECK_MEM

int ComputeTextureMemorySize( const GUID &nDeviceGUID, D3DDEVTYPE deviceType )
{
#if defined( _X360 )
	return 0;
#elif defined( DONT_CHECK_MEM )
	return (deviceType == D3DDEVTYPE_REF) ? (64 * 1024 * 1024) : 102236160;
#else

	FileHandle_t file = g_pFullFileSystem->Open( "vidcfg.bin", "rb", "EXECUTABLE_PATH" );
	if ( file )
	{
		GUID deviceId;
		int texSize;
		g_pFullFileSystem->Read( &deviceId, sizeof(deviceId), file );
		g_pFullFileSystem->Read( &texSize, sizeof(texSize), file );
		g_pFullFileSystem->Close( file );
		if ( nDeviceGUID == deviceId )
		{
			return texSize;
		}
	}
	// How much texture memory?
	if (deviceType == D3DDEVTYPE_REF)
		return 64 * 1024 * 1024;

	// Sadly, the only way to compute texture memory size
	// is to allocate a crapload of textures until we can't any more
	ImageFormat fmt = FindNearestSupportedFormat( IMAGE_FORMAT_BGR565, false, false, false );
	int textureSize = ShaderUtil()->GetMemRequired( 256, 256, 1, fmt, false );

	int totalSize = 0;
	CUtlVector< IDirect3DBaseTexture* > textures;

	s_bTestingVideoMemorySize = true;
	while (true)
	{
		RECORD_COMMAND( DX8_CREATE_TEXTURE, 7 );
		RECORD_INT( textures.Count() );
		RECORD_INT( 256 );
		RECORD_INT( 256 );
		RECORD_INT( ImageLoader::ImageFormatToD3DFormat(fmt) );
		RECORD_INT( 1 );
		RECORD_INT( false );
		RECORD_INT( 1 );

		IDirect3DBaseTexture* pTex = CreateD3DTexture( 256, 256, 1, fmt, 1, 0 );
		if (!pTex)
			break;
		totalSize += textureSize;

		textures.AddToTail( pTex );
	} 
	s_bTestingVideoMemorySize = false;

	// Free all the temp textures
	for (int i = textures.Size(); --i >= 0; )
	{
		RECORD_COMMAND( DX8_DESTROY_TEXTURE, 1 );
		RECORD_INT( i );

		DestroyD3DTexture( textures[i] );
	}

	file = g_pFullFileSystem->Open( "vidcfg.bin", "wb", "EXECUTABLE_PATH" );
	if ( file )
	{
		g_pFullFileSystem->Write( &nDeviceGUID, sizeof(GUID), file );
		g_pFullFileSystem->Write( &totalSize, sizeof(totalSize), file );
		g_pFullFileSystem->Close( file );
	}

	return totalSize;
#endif
}