diff options
Diffstat (limited to 'materialsystem/shaderapidx9/texturedx8.cpp')
| -rw-r--r-- | materialsystem/shaderapidx9/texturedx8.cpp | 1545 |
1 files changed, 1545 insertions, 0 deletions
diff --git a/materialsystem/shaderapidx9/texturedx8.cpp b/materialsystem/shaderapidx9/texturedx8.cpp new file mode 100644 index 0000000..d4dbede --- /dev/null +++ b/materialsystem/shaderapidx9/texturedx8.cpp @@ -0,0 +1,1545 @@ +//========= 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 +} |