//========= 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( 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( 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( 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( 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( 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( 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( 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( 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( 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 }