You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1545 lines
44 KiB
1545 lines
44 KiB
//========= 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 |
|
}
|
|
|