//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
# include <stdlib.h>
# include "materialsystem_global.h"
# include "string.h"
# include "shaderapi/ishaderapi.h"
# include "materialsystem/materialsystem_config.h"
# include "IHardwareConfigInternal.h"
# include "texturemanager.h"
# include "materialsystem/imaterialvar.h"
# include "materialsystem/IColorCorrection.h"
# include "tier1/strtools.h"
# include "utlvector.h"
# include "utldict.h"
# include "itextureinternal.h"
# include "vtf/vtf.h"
# include "pixelwriter.h"
# include "basetypes.h"
# include "utlbuffer.h"
# include "filesystem.h"
# include "materialsystem/imesh.h"
# include "materialsystem/ishaderapi.h"
# include "vstdlib/random.h"
# include "imorphinternal.h"
# include "tier1/utlrbtree.h"
# include "tier1/utlpair.h"
# include "ctype.h"
# include "utlqueue.h"
# include "tier0/icommandline.h"
# include "ctexturecompositor.h"
# include "vprof_telemetry.h"
// Need lightmaps access here
# define MATSYS_INTERNAL
# include "cmatlightmaps.h"
# include "cmaterialsystem.h"
# undef MATSYS_INTERNAL
# include "tier0/memdbgon.h"
# define ERROR_TEXTURE_SIZE 32
# define WHITE_TEXTURE_SIZE 1
# define BLACK_TEXTURE_SIZE 1
# define GREY_TEXTURE_SIZE 1
# define NORMALIZATION_CUBEMAP_SIZE 32
struct AsyncLoadJob_t ;
struct AsyncReadJob_t ;
class AsyncLoader ;
class AsyncReader ;
# define MAX_READS_OUTSTANDING 2
static ImageFormat GetImageFormatRawReadback ( ImageFormat fmt ) ;
# ifdef STAGING_ONLY
static ConVar mat_texture_list_dump ( " mat_texture_list_dump " , " 0 " ) ;
# endif
const char * cTextureCachePathDir = " __texture_cache " ;
// TODO: Relocate this somewhere else. It works like python's "strip" function,
// removing leading and trailing whitespace, including newlines. Whitespace between
// non-whitespace characters is preserved.
void V_StripWhitespace ( char * pBuffer )
{
Assert ( pBuffer ) ;
char * pSrc = pBuffer ;
char * pDst = pBuffer ;
char * pDstFirstTrailingWhitespace = NULL ;
// Remove leading whitespace
bool leading = true ;
while ( * pSrc )
{
if ( leading )
{
if ( V_isspace ( * pSrc ) )
{
+ + pSrc ;
continue ;
}
else
{
leading = false ;
// Drop through
}
}
if ( pDst ! = pSrc )
* pDst = * pSrc ;
if ( ! leading & & V_isspace ( * pDst ) & & pDstFirstTrailingWhitespace = = NULL )
pDstFirstTrailingWhitespace = pDst ;
else if ( ! leading & & ! V_isspace ( * pDst ) & & pDstFirstTrailingWhitespace ! = NULL )
pDstFirstTrailingWhitespace = NULL ;
+ + pSrc ;
+ + pDst ;
}
( * pDst ) = 0 ;
if ( pDstFirstTrailingWhitespace )
( * pDstFirstTrailingWhitespace ) = 0 ;
}
//-----------------------------------------------------------------------------
//
// Various procedural texture regeneration classes
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Creates a checkerboard texture
//-----------------------------------------------------------------------------
class CCheckerboardTexture : public ITextureRegenerator
{
public :
CCheckerboardTexture ( int nCheckerSize , color32 color1 , color32 color2 ) :
m_nCheckerSize ( nCheckerSize ) , m_Color1 ( color1 ) , m_Color2 ( color2 )
{
}
virtual ~ CCheckerboardTexture ( ) { }
virtual void RegenerateTextureBits ( ITexture * pTexture , IVTFTexture * pVTFTexture , Rect_t * pSubRect )
{
for ( int iFrame = 0 ; iFrame < pVTFTexture - > FrameCount ( ) ; + + iFrame )
{
for ( int iFace = 0 ; iFace < pVTFTexture - > FaceCount ( ) ; + + iFace )
{
int nWidth = pVTFTexture - > Width ( ) ;
int nHeight = pVTFTexture - > Height ( ) ;
int nDepth = pVTFTexture - > Depth ( ) ;
for ( int z = 0 ; z < nDepth ; + + z )
{
// Fill mip 0 with a checkerboard
CPixelWriter pixelWriter ;
pixelWriter . SetPixelMemory ( pVTFTexture - > Format ( ) ,
pVTFTexture - > ImageData ( iFrame , iFace , 0 , 0 , 0 , z ) , pVTFTexture - > RowSizeInBytes ( 0 ) ) ;
for ( int y = 0 ; y < nHeight ; + + y )
{
pixelWriter . Seek ( 0 , y ) ;
for ( int x = 0 ; x < nWidth ; + + x )
{
if ( ( ( x & m_nCheckerSize ) ^ ( y & m_nCheckerSize ) ) ^ ( z & m_nCheckerSize ) )
{
pixelWriter . WritePixel ( m_Color1 . r , m_Color1 . g , m_Color1 . b , m_Color1 . a ) ;
}
else
{
pixelWriter . WritePixel ( m_Color2 . r , m_Color2 . g , m_Color2 . b , m_Color2 . a ) ;
}
}
}
}
}
}
}
virtual void Release ( )
{
delete this ;
}
private :
int m_nCheckerSize ;
color32 m_Color1 ;
color32 m_Color2 ;
} ;
static void CreateCheckerboardTexture ( ITextureInternal * pTexture , int nCheckerSize , color32 color1 , color32 color2 )
{
ITextureRegenerator * pRegen = new CCheckerboardTexture ( nCheckerSize , color1 , color2 ) ;
pTexture - > SetTextureRegenerator ( pRegen ) ;
}
//-----------------------------------------------------------------------------
// Creates a solid texture
//-----------------------------------------------------------------------------
class CSolidTexture : public ITextureRegenerator
{
public :
CSolidTexture ( color32 color ) : m_Color ( color )
{
}
virtual ~ CSolidTexture ( ) { }
virtual void RegenerateTextureBits ( ITexture * pTexture , IVTFTexture * pVTFTexture , Rect_t * pSubRect )
{
int nMipCount = pTexture - > IsMipmapped ( ) ? pVTFTexture - > MipCount ( ) : 1 ;
for ( int iFrame = 0 ; iFrame < pVTFTexture - > FrameCount ( ) ; + + iFrame )
{
for ( int iFace = 0 ; iFace < pVTFTexture - > FaceCount ( ) ; + + iFace )
{
for ( int iMip = 0 ; iMip < nMipCount ; + + iMip )
{
int nWidth , nHeight , nDepth ;
pVTFTexture - > ComputeMipLevelDimensions ( iMip , & nWidth , & nHeight , & nDepth ) ;
for ( int z = 0 ; z < nDepth ; + + z )
{
CPixelWriter pixelWriter ;
pixelWriter . SetPixelMemory ( pVTFTexture - > Format ( ) ,
pVTFTexture - > ImageData ( iFrame , iFace , iMip , 0 , 0 , z ) , pVTFTexture - > RowSizeInBytes ( iMip ) ) ;
for ( int y = 0 ; y < nHeight ; + + y )
{
pixelWriter . Seek ( 0 , y ) ;
for ( int x = 0 ; x < nWidth ; + + x )
{
pixelWriter . WritePixel ( m_Color . r , m_Color . g , m_Color . b , m_Color . a ) ;
}
}
}
}
}
}
}
virtual void Release ( )
{
delete this ;
}
private :
color32 m_Color ;
} ;
static void CreateSolidTexture ( ITextureInternal * pTexture , color32 color )
{
ITextureRegenerator * pRegen = new CSolidTexture ( color ) ;
pTexture - > SetTextureRegenerator ( pRegen ) ;
}
//-----------------------------------------------------------------------------
// Creates a normalization cubemap texture
//-----------------------------------------------------------------------------
class CNormalizationCubemap : public ITextureRegenerator
{
public :
virtual void RegenerateTextureBits ( ITexture * pTexture , IVTFTexture * pVTFTexture , Rect_t * pSubRect )
{
// Normalization cubemap doesn't make sense on low-end hardware
// So we won't construct a spheremap out of this
CPixelWriter pixelWriter ;
Vector direction ;
for ( int iFace = 0 ; iFace < 6 ; + + iFace )
{
pixelWriter . SetPixelMemory ( pVTFTexture - > Format ( ) ,
pVTFTexture - > ImageData ( 0 , iFace , 0 ) , pVTFTexture - > RowSizeInBytes ( 0 ) ) ;
int nWidth = pVTFTexture - > Width ( ) ;
int nHeight = pVTFTexture - > Height ( ) ;
float flInvWidth = 2.0f / ( float ) ( nWidth - 1 ) ;
float flInvHeight = 2.0f / ( float ) ( nHeight - 1 ) ;
for ( int y = 0 ; y < nHeight ; + + y )
{
float v = y * flInvHeight - 1.0f ;
pixelWriter . Seek ( 0 , y ) ;
for ( int x = 0 ; x < nWidth ; + + x )
{
float u = x * flInvWidth - 1.0f ;
float oow = 1.0f / sqrt ( 1.0f + u * u + v * v ) ;
# ifdef DX_TO_GL_ABSTRACTION
float flX = ( 255.0f * 0.5 * ( u * oow + 1.0f ) + 0.5f ) ;
float flY = ( 255.0f * 0.5 * ( v * oow + 1.0f ) + 0.5f ) ;
float flZ = ( 255.0f * 0.5 * ( oow + 1.0f ) + 0.5f ) ;
flX / = 256.0f ;
flY / = 256.0f ;
flZ / = 256.0f ;
switch ( iFace )
{
case CUBEMAP_FACE_RIGHT :
pixelWriter . WritePixelF ( flZ , 1.f - flY , 1.f - flX , 1.f ) ;
break ;
case CUBEMAP_FACE_LEFT :
pixelWriter . WritePixelF ( 1.f - flZ , 1.f - flY , flX , 1.f ) ;
break ;
case CUBEMAP_FACE_BACK :
pixelWriter . WritePixelF ( flX , flZ , flY , 1.f ) ;
break ;
case CUBEMAP_FACE_FRONT :
pixelWriter . WritePixelF ( flX , 1.f - flZ , 1.f - flY , 1.f ) ;
break ;
case CUBEMAP_FACE_UP :
pixelWriter . WritePixelF ( flX , 1.f - flY , flZ , 1.f ) ;
break ;
case CUBEMAP_FACE_DOWN :
pixelWriter . WritePixelF ( 1.f - flX , 1.f - flY , 1.f - flZ , 1.f ) ;
break ;
default :
break ;
}
# else
int ix = ( int ) ( 255.0f * 0.5f * ( u * oow + 1.0f ) + 0.5f ) ;
ix = clamp ( ix , 0 , 255 ) ;
int iy = ( int ) ( 255.0f * 0.5f * ( v * oow + 1.0f ) + 0.5f ) ;
iy = clamp ( iy , 0 , 255 ) ;
int iz = ( int ) ( 255.0f * 0.5f * ( oow + 1.0f ) + 0.5f ) ;
iz = clamp ( iz , 0 , 255 ) ;
switch ( iFace )
{
case CUBEMAP_FACE_RIGHT :
pixelWriter . WritePixel ( iz , 255 - iy , 255 - ix , 255 ) ;
break ;
case CUBEMAP_FACE_LEFT :
pixelWriter . WritePixel ( 255 - iz , 255 - iy , ix , 255 ) ;
break ;
case CUBEMAP_FACE_BACK :
pixelWriter . WritePixel ( ix , iz , iy , 255 ) ;
break ;
case CUBEMAP_FACE_FRONT :
pixelWriter . WritePixel ( ix , 255 - iz , 255 - iy , 255 ) ;
break ;
case CUBEMAP_FACE_UP :
pixelWriter . WritePixel ( ix , 255 - iy , iz , 255 ) ;
break ;
case CUBEMAP_FACE_DOWN :
pixelWriter . WritePixel ( 255 - ix , 255 - iy , 255 - iz , 255 ) ;
break ;
default :
break ;
}
# endif
}
}
}
}
// NOTE: The normalization cubemap regenerator is stateless
// so there's no need to allocate + deallocate them
virtual void Release ( ) { }
} ;
//-----------------------------------------------------------------------------
// Creates a normalization cubemap texture
//-----------------------------------------------------------------------------
class CSignedNormalizationCubemap : public ITextureRegenerator
{
public :
virtual void RegenerateTextureBits ( ITexture * pTexture , IVTFTexture * pVTFTexture , Rect_t * pSubRect )
{
// Normalization cubemap doesn't make sense on low-end hardware
// So we won't construct a spheremap out of this
CPixelWriter pixelWriter ;
Vector direction ;
for ( int iFace = 0 ; iFace < 6 ; + + iFace )
{
pixelWriter . SetPixelMemory ( pVTFTexture - > Format ( ) ,
pVTFTexture - > ImageData ( 0 , iFace , 0 ) , pVTFTexture - > RowSizeInBytes ( 0 ) ) ;
int nWidth = pVTFTexture - > Width ( ) ;
int nHeight = pVTFTexture - > Height ( ) ;
float flInvWidth = 2.0f / ( float ) ( nWidth - 1 ) ;
float flInvHeight = 2.0f / ( float ) ( nHeight - 1 ) ;
for ( int y = 0 ; y < nHeight ; + + y )
{
float v = y * flInvHeight - 1.0f ;
pixelWriter . Seek ( 0 , y ) ;
for ( int x = 0 ; x < nWidth ; + + x )
{
float u = x * flInvWidth - 1.0f ;
float oow = 1.0f / sqrt ( 1.0f + u * u + v * v ) ;
# ifdef DX_TO_GL_ABSTRACTION
float flX = ( 255.0f * 0.5 * ( u * oow + 1.0f ) + 0.5f ) ;
float flY = ( 255.0f * 0.5 * ( v * oow + 1.0f ) + 0.5f ) ;
float flZ = ( 255.0f * 0.5 * ( oow + 1.0f ) + 0.5f ) ;
switch ( iFace )
{
case CUBEMAP_FACE_RIGHT :
flX = 255.0f - flX ;
flY = 255.0f - flY ;
break ;
case CUBEMAP_FACE_LEFT :
flY = 255.0f - flY ;
flZ = 255.0f - flZ ;
break ;
case CUBEMAP_FACE_BACK :
break ;
case CUBEMAP_FACE_FRONT :
flY = 255.0f - flY ;
flZ = 255.0f - flZ ;
break ;
case CUBEMAP_FACE_UP :
flY = 255.0f - flY ;
break ;
case CUBEMAP_FACE_DOWN :
flX = 255.0f - flX ;
flY = 255.0f - flY ;
flZ = 255.0f - flZ ;
break ;
default :
break ;
}
flX - = 128.0f ;
flY - = 128.0f ;
flZ - = 128.0f ;
flX / = 128.0f ;
flY / = 128.0f ;
flZ / = 128.0f ;
switch ( iFace )
{
case CUBEMAP_FACE_RIGHT :
pixelWriter . WritePixelF ( flZ , flY , flX , 0.0f ) ;
break ;
case CUBEMAP_FACE_LEFT :
pixelWriter . WritePixelF ( flZ , flY , flX , 0.0f ) ;
break ;
case CUBEMAP_FACE_BACK :
pixelWriter . WritePixelF ( flX , flZ , flY , 0.0f ) ;
break ;
case CUBEMAP_FACE_FRONT :
pixelWriter . WritePixelF ( flX , flZ , flY , 0.0f ) ;
break ;
case CUBEMAP_FACE_UP :
pixelWriter . WritePixelF ( flX , flY , flZ , 0.0f ) ;
break ;
case CUBEMAP_FACE_DOWN :
pixelWriter . WritePixelF ( flX , flY , flZ , 0.0f ) ;
break ;
default :
break ;
}
# else
int ix = ( int ) ( 255 * 0.5 * ( u * oow + 1.0f ) + 0.5f ) ;
ix = clamp ( ix , 0 , 255 ) ;
int iy = ( int ) ( 255 * 0.5 * ( v * oow + 1.0f ) + 0.5f ) ;
iy = clamp ( iy , 0 , 255 ) ;
int iz = ( int ) ( 255 * 0.5 * ( oow + 1.0f ) + 0.5f ) ;
iz = clamp ( iz , 0 , 255 ) ;
switch ( iFace )
{
case CUBEMAP_FACE_RIGHT :
ix = 255 - ix ;
iy = 255 - iy ;
break ;
case CUBEMAP_FACE_LEFT :
iy = 255 - iy ;
iz = 255 - iz ;
break ;
case CUBEMAP_FACE_BACK :
break ;
case CUBEMAP_FACE_FRONT :
iy = 255 - iy ;
iz = 255 - iz ;
break ;
case CUBEMAP_FACE_UP :
iy = 255 - iy ;
break ;
case CUBEMAP_FACE_DOWN :
ix = 255 - ix ;
iy = 255 - iy ;
iz = 255 - iz ;
break ;
default :
break ;
}
ix - = 128 ;
iy - = 128 ;
iz - = 128 ;
Assert ( ix > = - 128 & & ix < = 127 ) ;
Assert ( iy > = - 128 & & iy < = 127 ) ;
Assert ( iz > = - 128 & & iz < = 127 ) ;
switch ( iFace )
{
case CUBEMAP_FACE_RIGHT :
// correct
// pixelWriter.WritePixelSigned( -128, -128, -128, 0 );
pixelWriter . WritePixelSigned ( iz , iy , ix , 0 ) ;
break ;
case CUBEMAP_FACE_LEFT :
// correct
// pixelWriter.WritePixelSigned( -128, -128, -128, 0 );
pixelWriter . WritePixelSigned ( iz , iy , ix , 0 ) ;
break ;
case CUBEMAP_FACE_BACK :
// wrong
// pixelWriter.WritePixelSigned( -128, -128, -128, 0 );
pixelWriter . WritePixelSigned ( ix , iz , iy , 0 ) ;
// pixelWriter.WritePixelSigned( -127, -127, 127, 0 );
break ;
case CUBEMAP_FACE_FRONT :
// wrong
// pixelWriter.WritePixelSigned( -128, -128, -128, 0 );
pixelWriter . WritePixelSigned ( ix , iz , iy , 0 ) ;
break ;
case CUBEMAP_FACE_UP :
// correct
// pixelWriter.WritePixelSigned( -128, -128, -128, 0 );
pixelWriter . WritePixelSigned ( ix , iy , iz , 0 ) ;
break ;
case CUBEMAP_FACE_DOWN :
// correct
// pixelWriter.WritePixelSigned( -128, -128, -128, 0 );
pixelWriter . WritePixelSigned ( ix , iy , iz , 0 ) ;
break ;
default :
break ;
}
# endif
} // x
} // y
} // iFace
}
// NOTE: The normalization cubemap regenerator is stateless
// so there's no need to allocate + deallocate them
virtual void Release ( ) { }
} ;
static void CreateNormalizationCubemap ( ITextureInternal * pTexture )
{
// NOTE: The normalization cubemap regenerator is stateless
// so there's no need to allocate + deallocate them
static CNormalizationCubemap s_NormalizationCubemap ;
pTexture - > SetTextureRegenerator ( & s_NormalizationCubemap ) ;
}
static void CreateSignedNormalizationCubemap ( ITextureInternal * pTexture )
{
// NOTE: The normalization cubemap regenerator is stateless
// so there's no need to allocate + deallocate them
static CSignedNormalizationCubemap s_SignedNormalizationCubemap ;
pTexture - > SetTextureRegenerator ( & s_SignedNormalizationCubemap ) ;
}
//-----------------------------------------------------------------------------
// Creates a color correction texture
//-----------------------------------------------------------------------------
class CColorCorrectionTexture : public ITextureRegenerator
{
public :
CColorCorrectionTexture ( ColorCorrectionHandle_t handle ) : m_ColorCorrectionHandle ( handle )
{
}
virtual ~ CColorCorrectionTexture ( ) { }
virtual void RegenerateTextureBits ( ITexture * pTexture , IVTFTexture * pVTFTexture , Rect_t * pSubRect )
{
int nWidth = pVTFTexture - > Width ( ) ;
int nHeight = pVTFTexture - > Height ( ) ;
int nDepth = pVTFTexture - > Depth ( ) ;
Assert ( nWidth = = COLOR_CORRECTION_TEXTURE_SIZE & & nHeight = = COLOR_CORRECTION_TEXTURE_SIZE & & nDepth = = COLOR_CORRECTION_TEXTURE_SIZE ) ;
for ( int z = 0 ; z < nDepth ; + + z )
{
CPixelWriter pixelWriter ;
pixelWriter . SetPixelMemory ( pVTFTexture - > Format ( ) ,
pVTFTexture - > ImageData ( 0 , 0 , 0 , 0 , 0 , z ) , pVTFTexture - > RowSizeInBytes ( 0 ) ) ;
for ( int y = 0 ; y < nHeight ; + + y )
{
pixelWriter . Seek ( 0 , y ) ;
for ( int x = 0 ; x < nWidth ; + + x )
{
RGBX5551_t inColor ;
inColor . r = x ;
inColor . g = y ;
inColor . b = z ;
color24 col = ColorCorrectionSystem ( ) - > GetLookup ( m_ColorCorrectionHandle , inColor ) ;
pixelWriter . WritePixel ( col . r , col . g , col . b , 255 ) ;
}
}
}
}
virtual void Release ( )
{
delete this ;
}
private :
ColorCorrectionHandle_t m_ColorCorrectionHandle ;
} ;
void CreateColorCorrectionTexture ( ITextureInternal * pTexture , ColorCorrectionHandle_t handle )
{
ITextureRegenerator * pRegen = new CColorCorrectionTexture ( handle ) ;
pTexture - > SetTextureRegenerator ( pRegen ) ;
}
//-----------------------------------------------------------------------------
// Implementation of the texture manager
//-----------------------------------------------------------------------------
class CTextureManager : public ITextureManager
{
public :
CTextureManager ( void ) ;
// Initialization + shutdown
virtual void Init ( int nFlags ) OVERRIDE ;
virtual void Shutdown ( ) ;
virtual void AllocateStandardRenderTargets ( ) ;
virtual void FreeStandardRenderTargets ( ) ;
virtual void CacheExternalStandardRenderTargets ( ) ;
virtual ITextureInternal * CreateProceduralTexture ( const char * pTextureName , const char * pTextureGroupName , int w , int h , int d , ImageFormat fmt , int nFlags , ITextureRegenerator * generator = NULL ) ;
virtual ITextureInternal * FindOrLoadTexture ( const char * textureName , const char * pTextureGroupName , int nAdditionalCreationFlags = 0 ) ;
virtual bool IsTextureLoaded ( const char * pTextureName ) ;
virtual void AddTextureAlias ( const char * pAlias , const char * pRealName ) ;
virtual void RemoveTextureAlias ( const char * pAlias ) ;
virtual void SetExcludedTextures ( const char * pScriptName ) ;
virtual void UpdateExcludedTextures ( ) ;
virtual void ResetTextureFilteringState ( ) ;
void ReloadTextures ( void ) ;
// These are used when we lose our video memory due to a mode switch etc
void ReleaseTextures ( void ) ;
void RestoreNonRenderTargetTextures ( void ) ;
void RestoreRenderTargets ( void ) ;
// Suspend or resume texture streaming requests
void SuspendTextureStreaming ( void ) ;
void ResumeTextureStreaming ( void ) ;
// delete any texture that has a refcount <= 0
void RemoveUnusedTextures ( void ) ;
void DebugPrintUsedTextures ( void ) ;
// Request a texture ID
virtual int RequestNextTextureID ( ) ;
// Get at a couple standard textures
virtual ITextureInternal * ErrorTexture ( ) ;
virtual ITextureInternal * NormalizationCubemap ( ) ;
virtual ITextureInternal * SignedNormalizationCubemap ( ) ;
virtual ITextureInternal * ShadowNoise2D ( ) ;
virtual ITextureInternal * IdentityLightWarp ( ) ;
virtual ITextureInternal * ColorCorrectionTexture ( int i ) ;
virtual ITextureInternal * FullFrameDepthTexture ( ) ;
virtual ITextureInternal * DebugLuxels2D ( ) ;
// Generates an error texture pattern
virtual void GenerateErrorTexture ( ITexture * pTexture , IVTFTexture * pVTFTexture ) ;
// Updates the color correction state
virtual void SetColorCorrectionTexture ( int i , ITextureInternal * pTexture ) ;
virtual void ForceAllTexturesIntoHardware ( void ) ;
virtual ITextureInternal * CreateRenderTargetTexture (
const char * pRTName , // NULL for auto-generated name
int w ,
int h ,
RenderTargetSizeMode_t sizeMode ,
ImageFormat fmt ,
RenderTargetType_t type ,
unsigned int textureFlags ,
unsigned int renderTargetFlags ) ;
virtual bool HasPendingTextureDestroys ( ) const ;
virtual void MarkUnreferencedTextureForCleanup ( ITextureInternal * pTexture ) ;
virtual void RemoveTexture ( ITextureInternal * pTexture ) ;
virtual void ReloadFilesInList ( IFileList * pFilesToReload ) ;
// start with -1, list terminates with -1
virtual int FindNext ( int iIndex , ITextureInternal * * ppTexture ) ;
virtual void ReleaseTempRenderTargetBits ( void ) ;
// Called once per frame by material system "somewhere."
virtual void Update ( ) ;
// Load a texture asynchronously and then call the provided callback.
virtual void AsyncFindOrLoadTexture ( const char * pTextureName , const char * pTextureGroupName , IAsyncTextureOperationReceiver * pRecipient , void * pExtraArgs , bool bComplain , int nAdditionalCreationFlags ) ;
void CompleteAsyncLoad ( AsyncLoadJob_t * pJob ) ;
virtual void AsyncCreateTextureFromRenderTarget ( ITexture * pSrcRt , const char * pDstName , ImageFormat dstFmt , bool bGenMips , int nAdditionalCreationFlags , IAsyncTextureOperationReceiver * pRecipient , void * pExtraArgs ) ;
void CompleteAsyncRead ( AsyncReadJob_t * pJob ) ;
ITextureInternal * AcquireReadbackTexture ( int w , int h , ImageFormat fmt ) ;
void ReleaseReadbackTexture ( ITextureInternal * pTex ) ;
void WarmTextureCache ( ) ;
void CoolTextureCache ( ) ;
virtual void RequestAllMipmaps ( ITextureInternal * pTex ) ;
virtual void EvictAllTextures ( ) ;
virtual void UpdatePostAsync ( ) ;
virtual void ReleaseAsyncScratchVTF ( IVTFTexture * pScratchVTF ) ;
virtual bool ThreadInAsyncLoadThread ( ) const ;
virtual bool ThreadInAsyncReadThread ( ) const ;
virtual bool AddTextureCompositorTemplate ( const char * pName , KeyValues * pTmplDesc ) OVERRIDE ;
virtual bool VerifyTextureCompositorTemplates ( ) OVERRIDE ;
virtual CTextureCompositorTemplate * FindTextureCompositorTemplate ( const char * pName ) OVERRIDE ;
protected :
ITextureInternal * FindTexture ( const char * textureName ) ;
ITextureInternal * LoadTexture ( const char * textureName , const char * pTextureGroupName , int nAdditionalCreationFlags = 0 , bool bDownload = true ) ;
void AsyncLoad ( const AsyncLoadJob_t & job ) ;
void AsyncReadTexture ( AsyncReadJob_t * job ) ;
// Restores a single texture
void RestoreTexture ( ITextureInternal * pTex ) ;
void CleanupPossiblyUnreferencedTextures ( ) ;
# ifdef STAGING_ONLY
void DumpTextureList ( ) ;
# endif
void FindFilesToLoad ( CUtlDict < int > * pOutFilesToLoad , const char * pFilename ) ;
void ReadFilesToLoad ( CUtlDict < int > * pOutFilesToLoad , const char * pFilename ) ;
CUtlDict < ITextureInternal * , unsigned short > m_TextureList ;
CUtlDict < const char * , unsigned short > m_TextureAliases ;
CUtlDict < int , unsigned short > m_TextureExcludes ;
CUtlDict < CCopyableUtlVector < AsyncLoadJob_t > > m_PendingAsyncLoads ;
CUtlVector < ITextureInternal * > m_ReadbackTextures ;
CUtlVector < ITextureInternal * > m_preloadedTextures ;
CUtlMap < ITextureInternal * , int > m_textureStreamingRequests ;
CTSQueue < ITextureInternal * > m_asyncStreamingRequests ;
CTSQueue < ITextureInternal * > m_PossiblyUnreferencedTextures ;
CUtlDict < CTextureCompositorTemplate * , unsigned short > m_TexCompTemplates ;
int m_iNextTexID ;
int m_nFlags ;
ITextureInternal * m_pErrorTexture ;
ITextureInternal * m_pBlackTexture ;
ITextureInternal * m_pWhiteTexture ;
ITextureInternal * m_pGreyTexture ;
ITextureInternal * m_pGreyAlphaZeroTexture ;
ITextureInternal * m_pNormalizationCubemap ;
ITextureInternal * m_pFullScreenTexture ;
ITextureInternal * m_pSignedNormalizationCubemap ;
ITextureInternal * m_pShadowNoise2D ;
ITextureInternal * m_pIdentityLightWarp ;
ITextureInternal * m_pColorCorrectionTextures [ COLOR_CORRECTION_MAX_TEXTURES ] ;
ITextureInternal * m_pFullScreenDepthTexture ;
ITextureInternal * m_pDebugLuxels2D ;
// Used to generate various error texture patterns when necessary
CCheckerboardTexture * m_pErrorRegen ;
friend class AsyncLoader ;
AsyncLoader * m_pAsyncLoader ;
friend class AsyncReader ;
AsyncReader * m_pAsyncReader ;
ThreadId_t m_nAsyncLoadThread ;
ThreadId_t m_nAsyncReadThread ;
int m_iSuspendTextureStreaming ;
} ;
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CTextureManager s_TextureManager ;
ITextureManager * g_pTextureManager = & s_TextureManager ;
struct AsyncLoadJob_t
{
CUtlString m_TextureName ;
CUtlString m_TextureGroupName ;
IAsyncTextureOperationReceiver * m_pRecipient ;
void * m_pExtraArgs ;
bool m_bComplain ;
int m_nAdditionalCreationFlags ;
ITextureInternal * m_pResultData ;
AsyncLoadJob_t ( )
: m_pRecipient ( NULL )
, m_pExtraArgs ( NULL )
, m_bComplain ( false )
, m_nAdditionalCreationFlags ( 0 )
, m_pResultData ( NULL )
{ }
AsyncLoadJob_t ( const char * pTextureName , const char * pTextureGroupName , IAsyncTextureOperationReceiver * pRecipient , void * pExtraArgs , bool bComplain , int nAdditionalCreationFlags )
: m_TextureName ( pTextureName )
, m_TextureGroupName ( pTextureGroupName )
, m_pRecipient ( pRecipient )
, m_pExtraArgs ( pExtraArgs )
, m_bComplain ( bComplain )
, m_nAdditionalCreationFlags ( nAdditionalCreationFlags )
, m_pResultData ( NULL )
{
}
} ;
class CAsyncCopyRequest : public IAsyncTextureOperationReceiver
{
public :
CAsyncCopyRequest ( )
: m_nReferenceCount ( 0 )
, m_bSignalled ( false )
{ }
virtual ~ CAsyncCopyRequest ( ) { }
virtual int AddRef ( ) OVERRIDE { return + + m_nReferenceCount ; }
virtual int Release ( ) OVERRIDE
{
int retVal = - - m_nReferenceCount ;
if ( retVal = = 0 )
delete this ;
return retVal ;
}
virtual int GetRefCount ( ) const OVERRIDE { return m_nReferenceCount ; }
virtual void OnAsyncCreateComplete ( ITexture * pTex , void * pExtraArgs ) OVERRIDE { }
virtual void OnAsyncFindComplete ( ITexture * pTex , void * pExtraArgs ) OVERRIDE { }
virtual void OnAsyncMapComplete ( ITexture * pTex , void * pExtraArgs , void * pMemory , int nPitch ) OVERRIDE { }
virtual void OnAsyncReadbackBegin ( ITexture * pDst , ITexture * pSrc , void * pExtraArgs ) OVERRIDE
{
m_bSignalled = true ;
}
bool IsSignalled ( ) const { return m_bSignalled ; }
private :
CInterlockedInt m_nReferenceCount ;
volatile bool m_bSignalled ;
} ;
class CAsyncMapResult : public IAsyncTextureOperationReceiver
{
public :
CAsyncMapResult ( ITextureInternal * pTex )
: m_pTexToMap ( pTex )
, m_nReferenceCount ( 0 )
, m_pMemory ( NULL )
, m_nPitch ( 0 )
, m_bSignalled ( false )
{ }
virtual ~ CAsyncMapResult ( ) { }
virtual int AddRef ( ) OVERRIDE { return + + m_nReferenceCount ; }
virtual int Release ( ) OVERRIDE
{
int retVal = - - m_nReferenceCount ;
if ( retVal = = 0 )
delete this ;
return retVal ;
}
virtual int GetRefCount ( ) const OVERRIDE { return m_nReferenceCount ; }
virtual void OnAsyncCreateComplete ( ITexture * pTex , void * pExtraArgs ) OVERRIDE { }
virtual void OnAsyncFindComplete ( ITexture * pTex , void * pExtraArgs ) OVERRIDE { }
virtual void OnAsyncMapComplete ( ITexture * pTex , void * pExtraArgs , void * pMemory , int nPitch ) OVERRIDE
{
Assert ( pTex = = m_pTexToMap ) ;
m_pMemory = pMemory ;
m_nPitch = nPitch ;
m_bSignalled = true ;
}
virtual void OnAsyncReadbackBegin ( ITexture * pDst , ITexture * pSrc , void * pExtraArgs ) OVERRIDE { }
bool IsSignalled ( ) const { return m_bSignalled ; }
ITextureInternal * const m_pTexToMap ;
CInterlockedInt m_nReferenceCount ;
volatile void * m_pMemory ;
volatile int m_nPitch ;
private :
volatile bool m_bSignalled ;
} ;
struct AsyncReadJob_t
{
ITexture * m_pSrcRt ;
ITextureInternal * m_pSysmemTex ;
CAsyncCopyRequest * m_pAsyncRead ;
CAsyncMapResult * m_pAsyncMap ;
const char * m_pDstName ;
ImageFormat m_dstFmt ;
bool m_bGenMips ;
int m_nAdditionalCreationFlags ;
IAsyncTextureOperationReceiver * m_pRecipient ;
void * m_pExtraArgs ;
CUtlMemory < unsigned char > m_finalTexelData ;
AsyncReadJob_t ( )
: m_pSrcRt ( NULL )
, m_pSysmemTex ( NULL )
, m_pAsyncRead ( NULL )
, m_pAsyncMap ( NULL )
, m_pDstName ( NULL )
, m_dstFmt ( IMAGE_FORMAT_UNKNOWN )
, m_bGenMips ( false )
, m_nAdditionalCreationFlags ( 0 )
, m_pRecipient ( NULL )
, m_pExtraArgs ( NULL )
{ }
AsyncReadJob_t ( ITexture * pSrcRt , const char * pDstName , ImageFormat dstFmt , bool bGenMips , int nAdditionalCreationFlags , IAsyncTextureOperationReceiver * pRecipient , void * pExtraArgs )
: m_pSrcRt ( pSrcRt )
, m_pSysmemTex ( NULL )
, m_pAsyncRead ( NULL )
, m_pAsyncMap ( NULL )
, m_pDstName ( pDstName ) // We take ownership of this string.
, m_dstFmt ( dstFmt )
, m_bGenMips ( bGenMips )
, m_nAdditionalCreationFlags ( nAdditionalCreationFlags )
, m_pRecipient ( pRecipient )
, m_pExtraArgs ( pExtraArgs )
{
}
~ AsyncReadJob_t ( )
{
Assert ( ThreadInMainThread ( ) ) ;
delete [ ] m_pDstName ;
SafeRelease ( & m_pRecipient ) ;
if ( m_pSysmemTex )
{
if ( m_pAsyncMap )
{
extern CMaterialSystem g_MaterialSystem ;
g_MaterialSystem . GetRenderContextInternal ( ) - > AsyncUnmap ( m_pSysmemTex ) ;
}
assert_cast < CTextureManager * > ( g_pTextureManager ) - > ReleaseReadbackTexture ( m_pSysmemTex ) ;
m_pSysmemTex = NULL ;
}
SafeRelease ( & m_pAsyncMap ) ;
}
} ;
bool IsJobCancelled ( AsyncReadJob_t * pJob )
{
Assert ( pJob ! = NULL ) ;
// The texture manager holds a reference to the object, so if we're the only one who is holding a ref
// then the job has been abandoned. This gives us the opportunity to cleanup and skip some work.
if ( pJob - > m_pRecipient - > GetRefCount ( ) = = 1 )
{
return true ;
}
return false ;
}
bool IsJobCancelled ( AsyncLoadJob_t * pJob )
{
Assert ( pJob ! = NULL ) ;
// The texture manager holds a reference to the object, so if we're the only one who is holding a ref
// then the job has been abandoned. This gives us the opportunity to cleanup and skip some work.
if ( pJob - > m_pRecipient - > GetRefCount ( ) = = 1 )
{
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Functions can be called from any thread, unless they are prefixed with a thread name.
class AsyncLoader
{
public :
AsyncLoader ( )
: m_bQuit ( false )
{
for ( int i = 0 ; i < MAX_READS_OUTSTANDING ; + + i )
{
m_asyncScratchVTFs . PushItem ( CreateVTFTexture ( ) ) ;
}
// Do this after everything else.
m_LoaderThread = CreateSimpleThread ( AsyncLoader : : LoaderMain , this ) ;
}
~ AsyncLoader ( )
{
Assert ( m_asyncScratchVTFs . Count ( ) = = MAX_READS_OUTSTANDING ) ;
while ( m_asyncScratchVTFs . Count ( ) > 0 )
{
IVTFTexture * pScratchVTF = NULL ;
m_asyncScratchVTFs . PopItem ( & pScratchVTF ) ;
delete pScratchVTF ;
}
}
void AsyncLoad ( const AsyncLoadJob_t & job )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// TODO: This could be made faster by keeping a pool of these things.
m_pendingJobs . PushItem ( new AsyncLoadJob_t ( job ) ) ;
}
void Shutdown ( )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
m_bQuit = true ;
ThreadJoin ( m_LoaderThread ) ;
}
void ThreadMain_Update ( )
{
Assert ( ThreadInMainThread ( ) ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
AsyncLoadJob_t * pJob = NULL ;
if ( m_completedJobs . PopItem ( & pJob ) )
{
Assert ( pJob ! = NULL ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - CompleteAsyncLoad " , __FUNCTION__ ) ;
// Complete the load, then make the callback.
assert_cast < CTextureManager * > ( g_pTextureManager ) - > CompleteAsyncLoad ( pJob ) ;
delete pJob ;
pJob = NULL ;
}
}
void ReleaseAsyncReadBuffer ( IVTFTexture * pScratchVTF )
{
Assert ( pScratchVTF ! = NULL ) ;
m_asyncScratchVTFs . PushItem ( pScratchVTF ) ;
}
private :
inline bool ThreadInLoaderThread ( )
{
return s_TextureManager . ThreadInAsyncLoadThread ( ) ;
}
void ThreadLoader_Main ( )
{
Assert ( ThreadInLoaderThread ( ) ) ;
while ( ! m_bQuit )
{
AsyncLoadJob_t * pJob = NULL ;
IVTFTexture * pScratchVTF = NULL ;
while ( ! m_pendingJobs . PopItem ( & pJob ) )
{
// "awhile"
ThreadSleep ( 8 ) ;
if ( m_bQuit )
return ;
}
Assert ( pJob ! = NULL ) ;
while ( ! m_asyncScratchVTFs . PopItem ( & pScratchVTF ) )
{
// Also awhile, but not as long..
ThreadSleep ( 4 ) ;
if ( m_bQuit )
return ;
}
Assert ( pScratchVTF ! = NULL ) ;
ThreadLoader_ProcessLoad ( pJob , pScratchVTF ) ;
}
}
void ThreadLoader_ProcessLoad ( AsyncLoadJob_t * pJob , IVTFTexture * pScratchVTF )
{
Assert ( ThreadInLoaderThread ( ) ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
Assert ( pJob - > m_pResultData ) ;
if ( ! pJob - > m_pResultData - > AsyncReadTextureFromFile ( pScratchVTF , pJob - > m_nAdditionalCreationFlags ) )
m_asyncScratchVTFs . PushItem ( pScratchVTF ) ;
m_completedJobs . PushItem ( pJob ) ;
}
static uintp LoaderMain ( void * _this )
{
ThreadSetDebugName ( " Loader " ) ;
s_TextureManager . m_nAsyncLoadThread = ThreadGetCurrentId ( ) ;
( ( AsyncLoader * ) _this ) - > ThreadLoader_Main ( ) ;
s_TextureManager . m_nAsyncLoadThread = 0xFFFFFFFF ;
return 0 ;
}
ThreadHandle_t m_LoaderThread ;
volatile bool m_bQuit ;
CTSQueue < AsyncLoadJob_t * > m_pendingJobs ;
CTSQueue < AsyncLoadJob_t * > m_completedJobs ;
CTSQueue < IVTFTexture * > m_asyncScratchVTFs ;
} ;
//-----------------------------------------------------------------------------
// Functions can be called from any thread, unless they are prefixed with a thread name.
class AsyncReader
{
public :
AsyncReader ( )
: m_bQuit ( false )
{
// Do this after everything else.
m_HelperThread = CreateSimpleThread ( AsyncReader : : ReaderMain , this ) ;
}
void AsyncReadback ( AsyncReadJob_t * job )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
m_requestedCopies . PushItem ( job ) ;
}
void Shutdown ( )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
m_bQuit = true ;
ThreadJoin ( m_HelperThread ) ;
}
void ThreadMain_Update ( )
{
Assert ( ThreadInMainThread ( ) ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
while ( ! m_queuedMaps . IsEmpty ( ) )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " CompleteMap " ) ;
AsyncReadJob_t * pMapped = m_queuedMaps . Head ( ) ;
Assert ( pMapped ! = NULL ) ;
{
if ( IsJobCancelled ( pMapped ) )
{
// Remove the head, which is pMapped
m_queuedMaps . RemoveAtHead ( ) ;
delete pMapped ;
continue ;
}
if ( pMapped - > m_pAsyncMap - > IsSignalled ( ) )
{
if ( pMapped - > m_pAsyncMap - > m_pMemory ! = 0 & & pMapped - > m_pAsyncMap - > m_nPitch ! = 0 )
{
// Stick it in the queue for the other thread to work on it.
m_pendingJobs . PushItem ( pMapped ) ;
}
else
{
Assert ( ! " Failed to perform a map that shouldn't fail, need to deal with this if it ever happens. " ) ;
DevWarning ( " Failed to perform a map that shouldn't fail, need to deal with this if it ever happens. " ) ;
}
// Remove the head, which is pMapped
m_queuedMaps . RemoveAtHead ( ) ;
}
// Stop as soon as we complete one, regardless of success.
break ;
}
}
// This is ugly, but basically we need to do map and unmap on the main thread. Other
// stuff can (mostly) happen on the async thread
while ( ! m_queuedReads . IsEmpty ( ) )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " CompleteQueuedRead " ) ;
AsyncReadJob_t * pRead = NULL ;
if ( m_queuedReads . RemoveAtHead ( pRead ) )
{
if ( IsJobCancelled ( pRead ) )
{
delete pRead ;
continue ;
}
SafeAssign ( & pRead - > m_pAsyncMap , new CAsyncMapResult ( pRead - > m_pSysmemTex ) ) ;
// Trigger the map.
extern CMaterialSystem g_MaterialSystem ;
g_MaterialSystem . GetRenderContextInternal ( ) - > AsyncMap ( pRead - > m_pSysmemTex , pRead - > m_pAsyncMap , NULL ) ;
m_queuedMaps . Insert ( pRead ) ;
// Stop as soon as we complete one successfully.
break ;
}
}
if ( ! m_scheduledReads . IsEmpty ( ) )
{
if ( m_scheduledReads . Head ( ) - > m_pAsyncRead - > IsSignalled ( ) )
{
AsyncReadJob_t * pScheduledRead = m_scheduledReads . RemoveAtHead ( ) ;
SafeRelease ( & pScheduledRead - > m_pAsyncRead ) ;
m_queuedReads . Insert ( pScheduledRead ) ;
}
}
AsyncReadJob_t * pRequestCopy = NULL ;
if ( m_requestedCopies . PopItem ( & pRequestCopy ) )
{
SafeAssign ( & pRequestCopy - > m_pAsyncRead , new CAsyncCopyRequest ) ;
extern CMaterialSystem g_MaterialSystem ;
g_MaterialSystem . GetRenderContextInternal ( ) - > AsyncCopyRenderTargetToStagingTexture ( pRequestCopy - > m_pSysmemTex , pRequestCopy - > m_pSrcRt , pRequestCopy - > m_pAsyncRead , NULL ) ;
m_scheduledReads . Insert ( pRequestCopy ) ;
}
while ( m_completedJobs . Count ( ) > 0 )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " CreateTextureFromBits " ) ;
AsyncReadJob_t * pCreate = NULL ;
if ( m_completedJobs . PopItem ( & pCreate ) )
{
// Check after we do the unmap, we need to do that here.
if ( IsJobCancelled ( pCreate ) )
{
delete pCreate ;
continue ;
}
extern CMaterialSystem g_MaterialSystem ;
g_MaterialSystem . GetRenderContextInternal ( ) - > AsyncUnmap ( pCreate - > m_pSysmemTex ) ;
SafeRelease ( & pCreate - > m_pAsyncMap ) ;
assert_cast < CTextureManager * > ( g_pTextureManager ) - > CompleteAsyncRead ( pCreate ) ;
delete pCreate ;
pCreate = NULL ;
// Stop as soon as we complete one successfully.
break ;
}
}
}
private :
inline bool ThreadInReaderThread ( )
{
return s_TextureManager . ThreadInAsyncReadThread ( ) ;
}
void ThreadReader_Main ( )
{
Assert ( ThreadInReaderThread ( ) ) ;
while ( ! m_bQuit )
{
AsyncReadJob_t * pJob = NULL ;
if ( m_pendingJobs . PopItem ( & pJob ) )
{
Assert ( pJob ! = NULL ) ;
ThreadReader_ProcessRead ( pJob ) ;
}
else
{
// "awhile"
ThreadSleep ( 8 ) ;
}
}
}
void ThreadReader_ProcessRead ( AsyncReadJob_t * pJob )
{
Assert ( ThreadInReaderThread ( ) ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// This code does a few things:
// 1. Reads from a previously mapped scratch buffer texture and performs byte swapping (if necessary).
// 2. Uses byteswapped data to generate mipmaps
// 3. Encodes mipmapped data into the destination format.
const int h = pJob - > m_pSysmemTex - > GetActualHeight ( ) ;
const int w = pJob - > m_pSysmemTex - > GetActualWidth ( ) ;
const ImageFormat srcFmt = pJob - > m_pSysmemTex - > GetImageFormat ( ) ;
// Convert the data
CUtlMemory < unsigned char > srcBufferFinestMip ;
CUtlMemory < unsigned char > srcBufferAllMips ;
const int srcFinestMemRequired = ImageLoader : : GetMemRequired ( w , h , 1 , srcFmt , false ) ;
const int srcAllMemRequired = ImageLoader : : GetMemRequired ( w , h , 1 , srcFmt , pJob - > m_bGenMips ) ;
const int srcPitch = ImageLoader : : GetMemRequired ( w , 1 , 1 , srcFmt , false ) ;
const ImageFormat dstFmt = pJob - > m_dstFmt ;
CUtlMemory < unsigned char > dstBufferAllMips ;
const int dstMemRequried = ImageLoader : : GetMemRequired ( w , h , 1 , dstFmt , pJob - > m_bGenMips ) ;
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s-Allocations " , __FUNCTION__ ) ;
srcBufferFinestMip . EnsureCapacity ( srcFinestMemRequired ) ;
if ( srcFinestMemRequired ! = srcAllMemRequired )
{
srcBufferAllMips . EnsureCapacity ( srcAllMemRequired ) ;
}
else
{
Assert ( ! pJob - > m_bGenMips ) ;
}
if ( srcFmt ! = dstFmt )
{
dstBufferAllMips . EnsureCapacity ( dstMemRequried ) ;
}
}
// If this fires, you will get data corruption below. We can fix this case, it just doesn't seem
// to be needed right now.
Assert ( pJob - > m_pAsyncMap - > m_nPitch = = srcPitch ) ;
srcPitch ; // Hush compiler.
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s-ByteSwapInPlace " , __FUNCTION__ ) ;
ImageLoader : : ConvertImageFormat ( ( unsigned char * ) pJob - > m_pAsyncMap - > m_pMemory , GetImageFormatRawReadback ( srcFmt ) , srcBufferFinestMip . Base ( ) , srcFmt , w , h ) ;
}
if ( pJob - > m_bGenMips )
{
GenerateMipmaps ( & srcBufferAllMips , srcBufferFinestMip . Base ( ) , w , h , srcFmt ) ;
}
else
{
// If we're not generating mips, then allmips == finest mip, but the code below expects everything to
// be in all mips.
srcBufferAllMips . Swap ( srcBufferFinestMip ) ;
}
// Code below expects that the data is here one way or another.
Assert ( srcBufferAllMips . Count ( ) = = srcAllMemRequired ) ;
if ( srcFmt ! = dstFmt )
{
ConvertTexelData ( & dstBufferAllMips , dstFmt , srcBufferAllMips , w , h , srcFmt , pJob - > m_bGenMips ) ;
pJob - > m_finalTexelData . Swap ( dstBufferAllMips ) ;
}
else
{
// Just swap out the buffers.
pJob - > m_finalTexelData . Swap ( srcBufferAllMips ) ;
}
// At this point, the data should be ready to go. Quick sanity check.
Assert ( pJob - > m_finalTexelData . Count ( ) = = dstMemRequried ) ;
m_completedJobs . PushItem ( pJob ) ;
}
void GenerateMipmaps ( CUtlMemory < unsigned char > * outBuffer , unsigned char * pSrc , int w , int h , ImageFormat fmt ) const
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
ImageLoader : : GenerateMipmapLevelsLQ ( pSrc , outBuffer - > Base ( ) , w , h , fmt , 0 ) ;
}
void ConvertTexelData ( CUtlMemory < unsigned char > * outBuffer , ImageFormat dstFmt , /* const */ CUtlMemory < unsigned char > & inBuffer , int w , int h , ImageFormat srcFmt , bool bGenMips )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
const int mipmapCount = bGenMips ? ImageLoader : : GetNumMipMapLevels ( w , h ) : 1 ;
unsigned char * pSrc = inBuffer . Base ( ) ;
unsigned char * pDst = ( * outBuffer ) . Base ( ) ;
int mip_w = w ;
int mip_h = h ;
for ( int i = 0 ; i < mipmapCount ; + + i )
{
ImageLoader : : ConvertImageFormat ( pSrc , srcFmt , pDst , dstFmt , mip_w , mip_h ) ;
pSrc + = ImageLoader : : GetMemRequired ( mip_w , mip_h , 1 , srcFmt , false ) ;
pDst + = ImageLoader : : GetMemRequired ( mip_w , mip_h , 1 , dstFmt , false ) ;
mip_w = Max ( 1 , mip_w > > 1 ) ;
mip_h = Max ( 1 , mip_h > > 1 ) ;
}
}
static uintp ReaderMain ( void * _this )
{
ThreadSetDebugName ( " Helper " ) ;
s_TextureManager . m_nAsyncReadThread = ThreadGetCurrentId ( ) ;
( ( AsyncReader * ) _this ) - > ThreadReader_Main ( ) ;
s_TextureManager . m_nAsyncReadThread = 0xFFFFFFFF ;
return 0 ;
}
ThreadHandle_t m_HelperThread ;
volatile bool m_bQuit ;
CTSQueue < AsyncReadJob_t * > m_requestedCopies ;
CUtlQueue < AsyncReadJob_t * > m_queuedReads ;
CUtlQueue < AsyncReadJob_t * > m_scheduledReads ;
CUtlQueue < AsyncReadJob_t * > m_queuedMaps ;
CTSQueue < AsyncReadJob_t * > m_pendingJobs ;
CTSQueue < AsyncReadJob_t * > m_completedJobs ;
} ;
//-----------------------------------------------------------------------------
// Texture manager
//-----------------------------------------------------------------------------
CTextureManager : : CTextureManager ( void )
: m_TextureList ( true )
, m_TextureAliases ( true )
, m_TextureExcludes ( true )
, m_PendingAsyncLoads ( true )
, m_textureStreamingRequests ( DefLessFunc ( ITextureInternal * ) )
, m_nAsyncLoadThread ( 0xFFFFFFFF )
, m_nAsyncReadThread ( 0xFFFFFFFF )
{
m_pErrorTexture = NULL ;
m_pBlackTexture = NULL ;
m_pWhiteTexture = NULL ;
m_pGreyTexture = NULL ;
m_pGreyAlphaZeroTexture = NULL ;
m_pNormalizationCubemap = NULL ;
m_pErrorRegen = NULL ;
m_pFullScreenTexture = NULL ;
m_pSignedNormalizationCubemap = NULL ;
m_pShadowNoise2D = NULL ;
m_pIdentityLightWarp = NULL ;
m_pFullScreenDepthTexture = NULL ;
m_pDebugLuxels2D = NULL ;
m_pAsyncLoader = new AsyncLoader ;
m_pAsyncReader = new AsyncReader ;
m_iSuspendTextureStreaming = 0 ;
}
//-----------------------------------------------------------------------------
// Initialization + shutdown
//-----------------------------------------------------------------------------
void CTextureManager : : Init ( int nFlags )
{
m_nFlags = nFlags ;
color32 color , color2 ;
m_iNextTexID = 4096 ;
// setup the checkerboard generator for failed texture loading
color . r = color . g = color . b = 0 ; color . a = 128 ;
color2 . r = color2 . b = color2 . a = 255 ; color2 . g = 0 ;
m_pErrorRegen = new CCheckerboardTexture ( 4 , color , color2 ) ;
// Create an error texture
m_pErrorTexture = CreateProceduralTexture ( " error " , TEXTURE_GROUP_OTHER ,
ERROR_TEXTURE_SIZE , ERROR_TEXTURE_SIZE , 1 , IMAGE_FORMAT_BGRA8888 , TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY ) ;
CreateCheckerboardTexture ( m_pErrorTexture , 4 , color , color2 ) ;
m_pErrorTexture - > SetErrorTexture ( true ) ;
// Create a white texture
m_pWhiteTexture = CreateProceduralTexture ( " white " , TEXTURE_GROUP_OTHER ,
WHITE_TEXTURE_SIZE , WHITE_TEXTURE_SIZE , 1 , IMAGE_FORMAT_BGRX8888 , TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY ) ;
color . r = color . g = color . b = color . a = 255 ;
CreateSolidTexture ( m_pWhiteTexture , color ) ;
// Create a black texture
m_pBlackTexture = CreateProceduralTexture ( " black " , TEXTURE_GROUP_OTHER ,
BLACK_TEXTURE_SIZE , BLACK_TEXTURE_SIZE , 1 , IMAGE_FORMAT_BGRX8888 , TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY ) ;
color . r = color . g = color . b = 0 ;
CreateSolidTexture ( m_pBlackTexture , color ) ;
// Create a grey texture
m_pGreyTexture = CreateProceduralTexture ( " grey " , TEXTURE_GROUP_OTHER ,
GREY_TEXTURE_SIZE , GREY_TEXTURE_SIZE , 1 , IMAGE_FORMAT_BGRA8888 , TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY ) ;
color . r = color . g = color . b = 128 ;
color . a = 255 ;
CreateSolidTexture ( m_pGreyTexture , color ) ;
// Create a grey texture
m_pGreyAlphaZeroTexture = CreateProceduralTexture ( " greyalphazero " , TEXTURE_GROUP_OTHER ,
GREY_TEXTURE_SIZE , GREY_TEXTURE_SIZE , 1 , IMAGE_FORMAT_BGRA8888 , TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY ) ;
color . r = color . g = color . b = 128 ;
color . a = 0 ;
CreateSolidTexture ( m_pGreyAlphaZeroTexture , color ) ;
int nTextureFlags = TEXTUREFLAGS_ENVMAP | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_CLAMPU ;
if ( HardwareConfig ( ) - > GetMaxDXSupportLevel ( ) > = 80 )
{
ImageFormat fmt = IsOpenGL ( ) ? IMAGE_FORMAT_RGBA16161616F : IMAGE_FORMAT_BGRX8888 ;
// Create a normalization cubemap
m_pNormalizationCubemap = CreateProceduralTexture ( " normalize " , TEXTURE_GROUP_CUBE_MAP ,
NORMALIZATION_CUBEMAP_SIZE , NORMALIZATION_CUBEMAP_SIZE , 1 , fmt ,
nTextureFlags ) ;
CreateNormalizationCubemap ( m_pNormalizationCubemap ) ;
}
if ( HardwareConfig ( ) - > GetMaxDXSupportLevel ( ) > = 90 )
{
// In GL, we have poor format support, so we ask for signed float
ImageFormat fmt = IsOpenGL ( ) ? IMAGE_FORMAT_RGBA16161616F : IMAGE_FORMAT_UVWQ8888 ;
# ifdef OSX
// JasonM - ridiculous hack around R500 lameness...we never use this texture on OSX anyways (right?)
// Now assuming this was an OSX specific workaround.
nTextureFlags | = TEXTUREFLAGS_POINTSAMPLE ;
# endif
// Create a normalization cubemap
m_pSignedNormalizationCubemap = CreateProceduralTexture ( " normalizesigned " , TEXTURE_GROUP_CUBE_MAP ,
NORMALIZATION_CUBEMAP_SIZE , NORMALIZATION_CUBEMAP_SIZE , 1 , fmt , nTextureFlags ) ;
CreateSignedNormalizationCubemap ( m_pSignedNormalizationCubemap ) ;
m_pIdentityLightWarp = FindOrLoadTexture ( " dev/IdentityLightWarp " , TEXTURE_GROUP_OTHER ) ;
m_pIdentityLightWarp - > IncrementReferenceCount ( ) ;
}
// High end hardware needs this texture for shadow mapping
if ( HardwareConfig ( ) - > ActuallySupportsPixelShaders_2_b ( ) )
{
m_pShadowNoise2D = FindOrLoadTexture ( " engine/NormalizedRandomDirections2D " , TEXTURE_GROUP_OTHER ) ;
m_pShadowNoise2D - > IncrementReferenceCount ( ) ;
}
m_pDebugLuxels2D = FindOrLoadTexture ( " debug/debugluxelsnoalpha " , TEXTURE_GROUP_OTHER ) ;
m_pDebugLuxels2D - > IncrementReferenceCount ( ) ;
}
void CTextureManager : : Shutdown ( )
{
// Clean up any textures we have hanging around that are waiting to go.
CleanupPossiblyUnreferencedTextures ( ) ;
// Cool the texture cache first to drop all the refs back to 0 for the streamable things.
CoolTextureCache ( ) ;
if ( m_pAsyncLoader )
{
m_pAsyncLoader - > Shutdown ( ) ;
delete m_pAsyncLoader ;
m_pAsyncLoader = NULL ;
}
if ( m_pAsyncReader )
{
m_pAsyncReader - > Shutdown ( ) ;
delete m_pAsyncReader ;
m_pAsyncReader = NULL ;
}
FreeStandardRenderTargets ( ) ;
FOR_EACH_VEC ( m_ReadbackTextures , i )
{
m_ReadbackTextures [ i ] - > Release ( ) ;
}
if ( m_pDebugLuxels2D )
{
m_pDebugLuxels2D - > DecrementReferenceCount ( ) ;
m_pDebugLuxels2D = NULL ;
}
// These checks added because it's possible for shutdown to be called before the material system is
// fully initialized.
if ( m_pWhiteTexture )
{
m_pWhiteTexture - > DecrementReferenceCount ( ) ;
m_pWhiteTexture = NULL ;
}
if ( m_pBlackTexture )
{
m_pBlackTexture - > DecrementReferenceCount ( ) ;
m_pBlackTexture = NULL ;
}
if ( m_pGreyTexture )
{
m_pGreyTexture - > DecrementReferenceCount ( ) ;
m_pGreyTexture = NULL ;
}
if ( m_pGreyAlphaZeroTexture )
{
m_pGreyAlphaZeroTexture - > DecrementReferenceCount ( ) ;
m_pGreyAlphaZeroTexture = NULL ;
}
if ( m_pNormalizationCubemap )
{
m_pNormalizationCubemap - > DecrementReferenceCount ( ) ;
m_pNormalizationCubemap = NULL ;
}
if ( m_pSignedNormalizationCubemap )
{
m_pSignedNormalizationCubemap - > DecrementReferenceCount ( ) ;
m_pSignedNormalizationCubemap = NULL ;
}
if ( m_pShadowNoise2D )
{
m_pShadowNoise2D - > DecrementReferenceCount ( ) ;
m_pShadowNoise2D = NULL ;
}
if ( m_pIdentityLightWarp )
{
m_pIdentityLightWarp - > DecrementReferenceCount ( ) ;
m_pIdentityLightWarp = NULL ;
}
if ( m_pErrorTexture )
{
m_pErrorTexture - > DecrementReferenceCount ( ) ;
m_pErrorTexture = NULL ;
}
ReleaseTextures ( ) ;
if ( m_pErrorRegen )
{
m_pErrorRegen - > Release ( ) ;
m_pErrorRegen = NULL ;
}
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
ITextureInternal : : Destroy ( m_TextureList [ i ] , true ) ;
}
m_TextureList . RemoveAll ( ) ;
for ( int i = m_TextureAliases . First ( ) ; i ! = m_TextureAliases . InvalidIndex ( ) ; i = m_TextureAliases . Next ( i ) )
{
delete [ ] m_TextureAliases [ i ] ;
}
m_TextureAliases . RemoveAll ( ) ;
m_TextureExcludes . RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Allocate, free standard render target textures
//-----------------------------------------------------------------------------
void CTextureManager : : AllocateStandardRenderTargets ( )
{
bool bAllocateFullscreenTexture = ( m_nFlags & MATERIAL_INIT_ALLOCATE_FULLSCREEN_TEXTURE ) ! = 0 ;
bool bAllocateMorphAccumTexture = g_pMorphMgr - > ShouldAllocateScratchTextures ( ) ;
if ( IsPC ( ) & & ( bAllocateFullscreenTexture | | bAllocateMorphAccumTexture ) )
{
MaterialSystem ( ) - > BeginRenderTargetAllocation ( ) ;
// A offscreen render target which is the size + format of the back buffer (*not* HDR format!)
if ( bAllocateFullscreenTexture )
{
m_pFullScreenTexture = CreateRenderTargetTexture ( " _rt_FullScreen " , 1 , 1 , RT_SIZE_FULL_FRAME_BUFFER_ROUNDED_UP ,
MaterialSystem ( ) - > GetBackBufferFormat ( ) , RENDER_TARGET , TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT , 0 ) ;
m_pFullScreenTexture - > IncrementReferenceCount ( ) ;
}
// This texture is the one we accumulate morph deltas into
if ( bAllocateMorphAccumTexture )
{
g_pMorphMgr - > AllocateScratchTextures ( ) ;
g_pMorphMgr - > AllocateMaterials ( ) ;
}
MaterialSystem ( ) - > EndRenderTargetAllocation ( ) ;
}
}
void CTextureManager : : FreeStandardRenderTargets ( )
{
if ( m_pFullScreenTexture )
{
m_pFullScreenTexture - > DecrementReferenceCount ( ) ;
m_pFullScreenTexture = NULL ;
}
g_pMorphMgr - > FreeMaterials ( ) ;
g_pMorphMgr - > FreeScratchTextures ( ) ;
}
void CTextureManager : : CacheExternalStandardRenderTargets ( )
{
m_pFullScreenDepthTexture = FindTexture ( " _rt_FullFrameDepth " ) ; //created/destroyed in engine/matsys_interface.cpp to properly track hdr changes
}
//-----------------------------------------------------------------------------
// Generates an error texture pattern
//-----------------------------------------------------------------------------
void CTextureManager : : GenerateErrorTexture ( ITexture * pTexture , IVTFTexture * pVTFTexture )
{
m_pErrorRegen - > RegenerateTextureBits ( pTexture , pVTFTexture , NULL ) ;
}
//-----------------------------------------------------------------------------
// Updates the color correction state
//-----------------------------------------------------------------------------
ITextureInternal * CTextureManager : : ColorCorrectionTexture ( int i )
{
Assert ( i < COLOR_CORRECTION_MAX_TEXTURES ) ;
return m_pColorCorrectionTextures [ i ] ;
}
void CTextureManager : : SetColorCorrectionTexture ( int i , ITextureInternal * pTexture )
{
Assert ( i < COLOR_CORRECTION_MAX_TEXTURES ) ;
if ( m_pColorCorrectionTextures [ i ] )
{
m_pColorCorrectionTextures [ i ] - > DecrementReferenceCount ( ) ;
}
m_pColorCorrectionTextures [ i ] = pTexture ;
if ( pTexture )
pTexture - > IncrementReferenceCount ( ) ;
}
//-----------------------------------------------------------------------------
// Releases all textures (cause we've lost video memory)
//-----------------------------------------------------------------------------
void CTextureManager : : ReleaseTextures ( void )
{
g_pShaderAPI - > SetFullScreenTextureHandle ( INVALID_SHADERAPI_TEXTURE_HANDLE ) ;
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
// Release the texture...
m_TextureList [ i ] - > ReleaseMemory ( ) ;
}
}
//-----------------------------------------------------------------------------
// Request a texture ID
//-----------------------------------------------------------------------------
int CTextureManager : : RequestNextTextureID ( )
{
// FIXME: Deal better with texture ids
// The range between 19000 and 21000 are used for standard textures + lightmaps
if ( m_iNextTexID = = 19000 )
{
m_iNextTexID = 21000 ;
}
return m_iNextTexID + + ;
}
//-----------------------------------------------------------------------------
// Restores a single texture
//-----------------------------------------------------------------------------
void CTextureManager : : RestoreTexture ( ITextureInternal * pTexture )
{
// Put the texture back onto the board
pTexture - > OnRestore ( ) ; // Give render targets a chance to reinitialize themselves if necessary (due to AA changes).
pTexture - > Download ( ) ;
}
//-----------------------------------------------------------------------------
// Purges our complete list of textures that might currently be unreferenced
//-----------------------------------------------------------------------------
void CTextureManager : : CleanupPossiblyUnreferencedTextures ( )
{
if ( ! ThreadInMainThread ( ) | | MaterialSystem ( ) - > GetRenderThreadId ( ) ! = ( uintp ) - 1 )
{
Assert ( ! " CTextureManager::CleanupPossiblyUnreferencedTextures should never be called here " ) ;
// This is catastrophically bad, don't do this. Someone needs to fix this. See JohnS or McJohn
DebuggerBreakIfDebugging_StagingOnly ( ) ;
return ;
}
// It is perfectly valid for a texture to become referenced again (it lives on in our texture list, and can be
// re-loaded) and then free'd again, so ensure we don't have any duplicates in queue.
CUtlVector < ITextureInternal * > texturesToDelete ( /* growSize */ 0 , /* initialSize */ m_PossiblyUnreferencedTextures . Count ( ) ) ;
ITextureInternal * pMaybeUnreferenced = NULL ;
while ( m_PossiblyUnreferencedTextures . PopItem ( & pMaybeUnreferenced ) )
{
Assert ( pMaybeUnreferenced - > GetReferenceCount ( ) > = 0 ) ;
if ( pMaybeUnreferenced - > GetReferenceCount ( ) = = 0 & & texturesToDelete . Find ( pMaybeUnreferenced ) = = texturesToDelete . InvalidIndex ( ) )
{
texturesToDelete . AddToTail ( pMaybeUnreferenced ) ;
}
}
// Free them
FOR_EACH_VEC ( texturesToDelete , i )
{
RemoveTexture ( texturesToDelete [ i ] ) ;
}
}
//-----------------------------------------------------------------------------
// Restore all textures (cause we've got video memory again)
//-----------------------------------------------------------------------------
void CTextureManager : : RestoreNonRenderTargetTextures ( )
{
// 360 should not have gotten here
Assert ( ! IsX360 ( ) ) ;
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
if ( ! m_TextureList [ i ] - > IsRenderTarget ( ) )
{
RestoreTexture ( m_TextureList [ i ] ) ;
}
}
}
//-----------------------------------------------------------------------------
// Restore just the render targets (cause we've got video memory again)
//-----------------------------------------------------------------------------
void CTextureManager : : RestoreRenderTargets ( )
{
// 360 should not have gotten here
Assert ( ! IsX360 ( ) ) ;
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
if ( m_TextureList [ i ] - > IsRenderTarget ( ) )
{
RestoreTexture ( m_TextureList [ i ] ) ;
}
}
if ( m_pFullScreenTexture )
{
g_pShaderAPI - > SetFullScreenTextureHandle ( m_pFullScreenTexture - > GetTextureHandle ( 0 ) ) ;
}
CacheExternalStandardRenderTargets ( ) ;
}
//-----------------------------------------------------------------------------
// Reloads all textures
//-----------------------------------------------------------------------------
void CTextureManager : : ReloadTextures ( )
{
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
// Put the texture back onto the board
m_TextureList [ i ] - > Download ( ) ;
}
}
static void ForceTextureIntoHardware ( ITexture * pTexture , IMaterial * pMaterial , IMaterialVar * pBaseTextureVar )
{
if ( IsX360 ( ) )
return ;
pBaseTextureVar - > SetTextureValue ( pTexture ) ;
CMatRenderContextPtr pRenderContext ( MaterialSystem ( ) - > GetRenderContext ( ) ) ;
pRenderContext - > Bind ( pMaterial ) ;
IMesh * pMesh = pRenderContext - > GetDynamicMesh ( true ) ;
CMeshBuilder meshBuilder ;
meshBuilder . Begin ( pMesh , MATERIAL_TRIANGLES , 1 ) ;
meshBuilder . Position3f ( 0.0f , 0.0f , 0.0f ) ;
meshBuilder . TangentS3f ( 0.0f , 1.0f , 0.0f ) ;
meshBuilder . TangentT3f ( 1.0f , 0.0f , 0.0f ) ;
meshBuilder . Normal3f ( 0.0f , 0.0f , 1.0f ) ;
meshBuilder . TexCoord2f ( 0 , 0.0f , 0.0f ) ;
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . Position3f ( 0.0f , 0.0f , 0.0f ) ;
meshBuilder . TangentS3f ( 0.0f , 1.0f , 0.0f ) ;
meshBuilder . TangentT3f ( 1.0f , 0.0f , 0.0f ) ;
meshBuilder . Normal3f ( 0.0f , 0.0f , 1.0f ) ;
meshBuilder . TexCoord2f ( 0 , 0.0f , 0.0f ) ;
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . Position3f ( 0.0f , 0.0f , 0.0f ) ;
meshBuilder . TangentS3f ( 0.0f , 1.0f , 0.0f ) ;
meshBuilder . TangentT3f ( 1.0f , 0.0f , 0.0f ) ;
meshBuilder . Normal3f ( 0.0f , 0.0f , 1.0f ) ;
meshBuilder . TexCoord2f ( 0 , 0.0f , 0.0f ) ;
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . End ( ) ;
pMesh - > Draw ( ) ;
}
//-----------------------------------------------------------------------------
// Reloads all textures
//-----------------------------------------------------------------------------
void CTextureManager : : ForceAllTexturesIntoHardware ( void )
{
if ( IsX360 ( ) )
return ;
IMaterial * pMaterial = MaterialSystem ( ) - > FindMaterial ( " engine/preloadtexture " , " texture preload " ) ;
pMaterial = ( ( IMaterialInternal * ) pMaterial ) - > GetRealTimeVersion ( ) ; //always work with the realtime material internally
pMaterial - > IncrementReferenceCount ( ) ;
bool bFound ;
IMaterialVar * pBaseTextureVar = pMaterial - > FindVar ( " $basetexture " , & bFound ) ;
if ( ! bFound )
{
return ;
}
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
// Put the texture back onto the board
ForceTextureIntoHardware ( m_TextureList [ i ] , pMaterial , pBaseTextureVar ) ;
}
pMaterial - > DecrementReferenceCount ( ) ;
}
//-----------------------------------------------------------------------------
// Get at a couple standard textures
//-----------------------------------------------------------------------------
ITextureInternal * CTextureManager : : ErrorTexture ( )
{
return m_pErrorTexture ;
}
ITextureInternal * CTextureManager : : NormalizationCubemap ( )
{
return m_pNormalizationCubemap ;
}
ITextureInternal * CTextureManager : : SignedNormalizationCubemap ( )
{
return m_pSignedNormalizationCubemap ;
}
ITextureInternal * CTextureManager : : ShadowNoise2D ( )
{
return m_pShadowNoise2D ;
}
ITextureInternal * CTextureManager : : IdentityLightWarp ( )
{
return m_pIdentityLightWarp ;
}
ITextureInternal * CTextureManager : : FullFrameDepthTexture ( )
{
return m_pFullScreenDepthTexture ;
}
ITextureInternal * CTextureManager : : DebugLuxels2D ( )
{
return m_pDebugLuxels2D ;
}
//-----------------------------------------------------------------------------
// Creates a procedural texture
//-----------------------------------------------------------------------------
ITextureInternal * CTextureManager : : CreateProceduralTexture (
const char * pTextureName ,
const char * pTextureGroupName ,
int w ,
int h ,
int d ,
ImageFormat fmt ,
int nFlags ,
ITextureRegenerator * generator )
{
ITextureInternal * pNewTexture = ITextureInternal : : CreateProceduralTexture ( pTextureName , pTextureGroupName , w , h , d , fmt , nFlags , generator ) ;
if ( ! pNewTexture )
return NULL ;
// Add it to the list of textures so it can be restored, etc.
m_TextureList . Insert ( pNewTexture - > GetName ( ) , pNewTexture ) ;
// NOTE: This will download the texture only if the shader api is ready
pNewTexture - > Download ( ) ;
return pNewTexture ;
}
//-----------------------------------------------------------------------------
// FIXME: Need some better understanding of when textures should be added to
// the texture dictionary here. Is it only for files, for example?
// Texture dictionary...
//-----------------------------------------------------------------------------
ITextureInternal * CTextureManager : : LoadTexture ( const char * pTextureName , const char * pTextureGroupName , int nAdditionalCreationFlags /* = 0 */ , bool bDownload /* = true */ )
{
ITextureInternal * pNewTexture = ITextureInternal : : CreateFileTexture ( pTextureName , pTextureGroupName ) ;
if ( pNewTexture )
{
int iIndex = m_TextureExcludes . Find ( pNewTexture - > GetName ( ) ) ;
if ( m_TextureExcludes . IsValidIndex ( iIndex ) )
{
// mark the new texture as excluded
int nDimensionsLimit = m_TextureExcludes [ iIndex ] ;
pNewTexture - > MarkAsExcluded ( ( nDimensionsLimit = = 0 ) , nDimensionsLimit ) ;
}
// Stick the texture onto the board
if ( bDownload )
pNewTexture - > Download ( NULL , nAdditionalCreationFlags ) ;
// FIXME: If there's been an error loading, we don't also want this error...
}
return pNewTexture ;
}
ITextureInternal * CTextureManager : : FindTexture ( const char * pTextureName )
{
if ( ! pTextureName | | pTextureName [ 0 ] = = 0 )
return NULL ;
char szCleanName [ MAX_PATH ] ;
NormalizeTextureName ( pTextureName , szCleanName , sizeof ( szCleanName ) ) ;
int i = m_TextureList . Find ( szCleanName ) ;
if ( i ! = m_TextureList . InvalidIndex ( ) )
{
return m_TextureList [ i ] ;
}
i = m_TextureAliases . Find ( szCleanName ) ;
if ( i ! = m_TextureAliases . InvalidIndex ( ) )
{
return FindTexture ( m_TextureAliases [ i ] ) ;
}
// Special handling: lightmaps
if ( char const * szLightMapNum = StringAfterPrefix ( szCleanName , " [lightmap " ) )
{
int iLightMapNum = atoi ( szLightMapNum ) ;
extern CMaterialSystem g_MaterialSystem ;
CMatLightmaps * plm = g_MaterialSystem . GetLightmaps ( ) ;
if ( iLightMapNum > = 0 & &
iLightMapNum < plm - > GetNumLightmapPages ( ) )
{
ShaderAPITextureHandle_t hTex = plm - > GetLightmapPageTextureHandle ( iLightMapNum ) ;
if ( hTex ! = INVALID_SHADERAPI_TEXTURE_HANDLE )
{
// Establish the lookup linking in the dictionary
ITextureInternal * pTxInt = ITextureInternal : : CreateReferenceTextureFromHandle ( pTextureName , TEXTURE_GROUP_LIGHTMAP , hTex ) ;
m_TextureList . Insert ( pTextureName , pTxInt ) ;
return pTxInt ;
}
}
}
return NULL ;
}
void CTextureManager : : AddTextureAlias ( const char * pAlias , const char * pRealName )
{
if ( ( pAlias = = NULL ) | | ( pRealName = = NULL ) )
return ; //invalid alias
char szCleanName [ MAX_PATH ] ;
int index = m_TextureAliases . Find ( NormalizeTextureName ( pAlias , szCleanName , sizeof ( szCleanName ) ) ) ;
if ( index ! = m_TextureAliases . InvalidIndex ( ) )
{
AssertMsg ( Q_stricmp ( pRealName , m_TextureAliases [ index ] ) = = 0 , " Trying to use one name to alias two different textures. " ) ;
RemoveTextureAlias ( pAlias ) ; //remove the old alias to make room for the new one.
}
size_t iRealNameLength = strlen ( pRealName ) + 1 ;
char * pRealNameCopy = new char [ iRealNameLength ] ;
memcpy ( pRealNameCopy , pRealName , iRealNameLength ) ;
m_TextureAliases . Insert ( szCleanName , pRealNameCopy ) ;
}
void CTextureManager : : RemoveTextureAlias ( const char * pAlias )
{
if ( pAlias = = NULL )
return ;
char szCleanName [ MAX_PATH ] ;
int index = m_TextureAliases . Find ( NormalizeTextureName ( pAlias , szCleanName , sizeof ( szCleanName ) ) ) ;
if ( index = = m_TextureAliases . InvalidIndex ( ) )
return ; //not found
delete [ ] m_TextureAliases [ index ] ;
m_TextureAliases . RemoveAt ( index ) ;
}
void CTextureManager : : SetExcludedTextures ( const char * pScriptName )
{
// clear all exisiting texture's exclusion
for ( int i = m_TextureExcludes . First ( ) ; i ! = m_TextureExcludes . InvalidIndex ( ) ; i = m_TextureExcludes . Next ( i ) )
{
ITextureInternal * pTexture = FindTexture ( m_TextureExcludes . GetElementName ( i ) ) ;
if ( pTexture )
{
pTexture - > MarkAsExcluded ( false , 0 ) ;
}
}
m_TextureExcludes . RemoveAll ( ) ;
MEM_ALLOC_CREDIT ( ) ;
// get optional script
CUtlBuffer excludeBuffer ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
if ( g_pFullFileSystem - > ReadFile ( pScriptName , NULL , excludeBuffer ) )
{
char szToken [ MAX_PATH ] ;
while ( 1 )
{
// must support spaces in names without quotes
// have to brute force parse up to a valid line
while ( 1 )
{
excludeBuffer . EatWhiteSpace ( ) ;
if ( ! excludeBuffer . EatCPPComment ( ) )
{
// not a comment
break ;
}
}
excludeBuffer . GetLine ( szToken , sizeof ( szToken ) ) ;
int tokenLength = strlen ( szToken ) ;
if ( ! tokenLength )
{
// end of list
break ;
}
// remove all trailing whitespace
while ( tokenLength > 0 )
{
tokenLength - - ;
if ( isgraph ( szToken [ tokenLength ] ) )
{
break ;
}
szToken [ tokenLength ] = ' \0 ' ;
}
// first optional token may be a dimension limit hint
int nDimensionsLimit = 0 ;
char * pTextureName = szToken ;
if ( pTextureName [ 0 ] ! = 0 & & isdigit ( pTextureName [ 0 ] ) )
{
nDimensionsLimit = atoi ( pTextureName ) ;
// skip forward to name
for ( ; ; )
{
char ch = * pTextureName ;
if ( ! ch | | ( ! isdigit ( ch ) & & ! isspace ( ch ) ) )
{
break ;
}
pTextureName + + ;
}
}
char szCleanName [ MAX_PATH ] ;
NormalizeTextureName ( pTextureName , szCleanName , sizeof ( szCleanName ) ) ;
if ( m_TextureExcludes . Find ( szCleanName ) ! = m_TextureExcludes . InvalidIndex ( ) )
{
// avoid duplicates
continue ;
}
m_TextureExcludes . Insert ( szCleanName , nDimensionsLimit ) ;
// set any existing texture's exclusion
// textures that don't exist yet will get caught during their creation path
ITextureInternal * pTexture = FindTexture ( szCleanName ) ;
if ( pTexture )
{
pTexture - > MarkAsExcluded ( ( nDimensionsLimit = = 0 ) , nDimensionsLimit ) ;
}
}
}
}
void CTextureManager : : UpdateExcludedTextures ( void )
{
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
m_TextureList [ i ] - > UpdateExcludedState ( ) ;
}
}
ITextureInternal * CTextureManager : : FindOrLoadTexture ( const char * pTextureName , const char * pTextureGroupName , int nAdditionalCreationFlags /* = 0 */ )
{
ITextureInternal * pTexture = FindTexture ( pTextureName ) ;
if ( ! pTexture )
{
pTexture = LoadTexture ( pTextureName , pTextureGroupName , nAdditionalCreationFlags ) ;
if ( pTexture )
{
// insert into the dictionary using the processed texture name
m_TextureList . Insert ( pTexture - > GetName ( ) , pTexture ) ;
}
}
return pTexture ;
}
bool CTextureManager : : IsTextureLoaded ( const char * pTextureName )
{
ITextureInternal * pTexture = FindTexture ( pTextureName ) ;
return ( pTexture ! = NULL ) ;
}
//-----------------------------------------------------------------------------
// Creates a texture that's a render target
//-----------------------------------------------------------------------------
ITextureInternal * CTextureManager : : CreateRenderTargetTexture (
const char * pRTName , // NULL for auto-generated name
int w ,
int h ,
RenderTargetSizeMode_t sizeMode ,
ImageFormat fmt ,
RenderTargetType_t type ,
unsigned int textureFlags ,
unsigned int renderTargetFlags )
{
MEM_ALLOC_CREDIT_ ( __FILE__ " : Render target " ) ;
ITextureInternal * pTexture ;
if ( pRTName )
{
// caller is re-initing or changing
pTexture = FindTexture ( pRTName ) ;
if ( pTexture )
{
// Changing the underlying render target, but leaving the pointer and refcount
// alone fixes callers that have exisiting references to this object.
ITextureInternal : : ChangeRenderTarget ( pTexture , w , h , sizeMode , fmt , type ,
textureFlags , renderTargetFlags ) ;
// download if ready
pTexture - > Download ( ) ;
return pTexture ;
}
}
pTexture = ITextureInternal : : CreateRenderTarget ( pRTName , w , h , sizeMode , fmt , type ,
textureFlags , renderTargetFlags ) ;
if ( ! pTexture )
return NULL ;
// Add the render target to the list of textures
// that way it'll get cleaned up correctly in case of a task switch
m_TextureList . Insert ( pTexture - > GetName ( ) , pTexture ) ;
// NOTE: This will download the texture only if the shader api is ready
pTexture - > Download ( ) ;
return pTexture ;
}
void CTextureManager : : ResetTextureFilteringState ( )
{
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
m_TextureList [ i ] - > SetFilteringAndClampingMode ( ) ;
}
}
void CTextureManager : : SuspendTextureStreaming ( void )
{
m_iSuspendTextureStreaming + + ;
}
void CTextureManager : : ResumeTextureStreaming ( void )
{
AssertMsg ( m_iSuspendTextureStreaming , " Mismatched Suspend/Resume texture streaming calls " ) ;
if ( m_iSuspendTextureStreaming )
{
m_iSuspendTextureStreaming - - ;
}
}
void CTextureManager : : RemoveUnusedTextures ( void )
{
// First, need to flush all of our textures that are pending cleanup.
CleanupPossiblyUnreferencedTextures ( ) ;
int iNext ;
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = iNext )
{
iNext = m_TextureList . Next ( i ) ;
# ifdef _DEBUG
if ( m_TextureList [ i ] - > GetReferenceCount ( ) < 0 )
{
Warning ( " RemoveUnusedTextures: pTexture->m_referenceCount < 0 for %s \n " , m_TextureList [ i ] - > GetName ( ) ) ;
}
# endif
if ( m_TextureList [ i ] - > GetReferenceCount ( ) < = 0 )
{
ITextureInternal : : Destroy ( m_TextureList [ i ] ) ;
m_TextureList . RemoveAt ( i ) ;
}
}
}
void CTextureManager : : MarkUnreferencedTextureForCleanup ( ITextureInternal * pTexture )
{
Assert ( pTexture - > GetReferenceCount ( ) = = 0 ) ;
m_PossiblyUnreferencedTextures . PushItem ( pTexture ) ;
}
void CTextureManager : : RemoveTexture ( ITextureInternal * pTexture )
{
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
Assert ( pTexture - > GetReferenceCount ( ) < = 0 ) ;
if ( ! ThreadInMainThread ( ) | | MaterialSystem ( ) - > GetRenderThreadId ( ) ! = ( uintp ) - 1 )
{
Assert ( ! " CTextureManager::RemoveTexture should never be called here " ) ;
// This is catastrophically bad, don't do this. Someone needs to fix this.
DebuggerBreakIfDebugging_StagingOnly ( ) ;
return ;
}
bool bTextureFound = false ;
// If the queue'd rendering thread is running, RemoveTexture() is going to explode. If it isn't, calling
// RemoveTexture while still dealing with immediate removal textures seems fishy, but could be legit, in which case
// this assert could be softened.
int nUnreferencedQueue = m_PossiblyUnreferencedTextures . Count ( ) ;
if ( nUnreferencedQueue )
{
Assert ( ! " RemoveTexture() being called while textures sitting in possibly unreferenced queue " ) ;
// Assuming that this is all a wholesome main-thread misunderstanding, we can try to continue after filtering
// this texture from the queue.
ITextureInternal * pPossiblyUnreferenced = NULL ;
for ( int i = 0 ; i < nUnreferencedQueue & & m_PossiblyUnreferencedTextures . PopItem ( & pPossiblyUnreferenced ) ; i + + )
{
m_PossiblyUnreferencedTextures . PushItem ( pPossiblyUnreferenced ) ;
if ( pPossiblyUnreferenced = = pTexture )
{
bTextureFound = true ;
break ;
}
}
}
if ( bTextureFound )
{
Assert ( ! " CTextureManager::RemoveTexture has been called for a texture that has already requested cleanup. That's a paddlin'. " ) ;
// This is catastrophically bad, don't do this. Someone needs to fix this.
DebuggerBreakIfDebugging_StagingOnly ( ) ;
return ;
}
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
// search by object
if ( m_TextureList [ i ] = = pTexture )
{
// This code is always sure that the texture we're tryign to clean up is no longer in the the possibly unreferenced list,
// So let Destroy work without checking.
ITextureInternal : : Destroy ( m_TextureList [ i ] , true ) ;
m_TextureList . RemoveAt ( i ) ;
break ;
}
}
}
void CTextureManager : : ReloadFilesInList ( IFileList * pFilesToReload )
{
if ( ! IsPC ( ) )
return ;
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
ITextureInternal * pTex = m_TextureList [ i ] ;
pTex - > ReloadFilesInList ( pFilesToReload ) ;
}
}
void CTextureManager : : ReleaseTempRenderTargetBits ( void )
{
if ( IsX360 ( ) ) //only sane on 360
{
int iNext ;
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = iNext )
{
iNext = m_TextureList . Next ( i ) ;
if ( m_TextureList [ i ] - > IsTempRenderTarget ( ) )
{
m_TextureList [ i ] - > ReleaseMemory ( ) ;
}
}
}
}
void CTextureManager : : DebugPrintUsedTextures ( void )
{
for ( int i = m_TextureList . First ( ) ; i ! = m_TextureList . InvalidIndex ( ) ; i = m_TextureList . Next ( i ) )
{
ITextureInternal * pTexture = m_TextureList [ i ] ;
Msg ( " Texture: '%s' RefCount: %d \n " , pTexture - > GetName ( ) , pTexture - > GetReferenceCount ( ) ) ;
}
if ( m_TextureExcludes . Count ( ) )
{
Msg ( " \n Excluded Textures: (%d) \n " , m_TextureExcludes . Count ( ) ) ;
for ( int i = m_TextureExcludes . First ( ) ; i ! = m_TextureExcludes . InvalidIndex ( ) ; i = m_TextureExcludes . Next ( i ) )
{
char buff [ 256 ] ;
const char * pName = m_TextureExcludes . GetElementName ( i ) ;
V_snprintf ( buff , sizeof ( buff ) , " Excluded: %d '%s' \n " , m_TextureExcludes [ i ] , pName ) ;
// an excluded texture is valid, but forced tiny
if ( IsTextureLoaded ( pName ) )
{
Msg ( " %s " , buff ) ;
}
else
{
// warn as unknown, could be a spelling error
Warning ( " %s " , buff ) ;
}
}
}
}
int CTextureManager : : FindNext ( int iIndex , ITextureInternal * * pTexInternal )
{
if ( iIndex = = - 1 & & m_TextureList . Count ( ) )
{
iIndex = m_TextureList . First ( ) ;
}
else if ( ! m_TextureList . Count ( ) | | ! m_TextureList . IsValidIndex ( iIndex ) )
{
* pTexInternal = NULL ;
return - 1 ;
}
* pTexInternal = m_TextureList [ iIndex ] ;
iIndex = m_TextureList . Next ( iIndex ) ;
if ( iIndex = = m_TextureList . InvalidIndex ( ) )
{
// end of list
iIndex = - 1 ;
}
return iIndex ;
}
void CTextureManager : : Update ( )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
# ifdef STAGING_ONLY
if ( mat_texture_list_dump . GetBool ( ) )
{
DumpTextureList ( ) ;
mat_texture_list_dump . SetValue ( 0 ) ;
}
# endif
if ( m_pAsyncReader )
m_pAsyncReader - > ThreadMain_Update ( ) ;
}
// Load a texture asynchronously and then call the provided callback.
void CTextureManager : : AsyncFindOrLoadTexture ( const char * pTextureName , const char * pTextureGroupName , IAsyncTextureOperationReceiver * pRecipient , void * pExtraArgs , bool bComplain , int nAdditionalCreationFlags )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
bool bStreamingRequest = ( nAdditionalCreationFlags & TEXTUREFLAGS_STREAMABLE ) ! = 0 ;
ITextureInternal * pLoadedTex = FindTexture ( pTextureName ) ;
// It'd be weird to indicate that we're streaming and not actually have a texture that already exists.
Assert ( ! bStreamingRequest | | pLoadedTex ! = NULL ) ;
if ( pLoadedTex )
{
if ( ! bStreamingRequest )
{
if ( pLoadedTex - > IsError ( ) & & bComplain )
DevWarning ( " Texture '%s' not found. \n " , pTextureName ) ;
pRecipient - > OnAsyncFindComplete ( pLoadedTex , pExtraArgs ) ;
SafeRelease ( pRecipient ) ;
return ;
}
}
AsyncLoadJob_t asyncLoad ( pTextureName , pTextureGroupName , pRecipient , pExtraArgs , bComplain , nAdditionalCreationFlags ) ;
// If this is the first person asking to load this, then remember so we don't load the same thing over and over again.
int pendingIndex = m_PendingAsyncLoads . Find ( pTextureName ) ;
if ( pendingIndex = = m_PendingAsyncLoads . InvalidIndex ( ) )
{
// Create the texture here, we'll load the data in the async thread. Load is a misnomer, because it doesn't actually
// load the data--Download does.
if ( bStreamingRequest )
asyncLoad . m_pResultData = pLoadedTex ;
else
asyncLoad . m_pResultData = LoadTexture ( pTextureName , pTextureGroupName , nAdditionalCreationFlags , false ) ;
AsyncLoad ( asyncLoad ) ;
pendingIndex = m_PendingAsyncLoads . Insert ( pTextureName ) ;
}
else
{
// If this is a thing we've seen before, just note that we also need it.
m_PendingAsyncLoads [ pendingIndex ] . AddToTail ( asyncLoad ) ;
}
}
void CTextureManager : : CompleteAsyncLoad ( AsyncLoadJob_t * pJob )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
Assert ( pJob ) ;
bool bDownloaded = false ;
if ( ! IsJobCancelled ( pJob ) )
{
// Perform the download. We did the read already.
pJob - > m_pResultData - > Download ( NULL , pJob - > m_nAdditionalCreationFlags ) ;
bDownloaded = true ;
}
// Then notify the caller that they're finished.
pJob - > m_pRecipient - > OnAsyncFindComplete ( pJob - > m_pResultData , pJob - > m_pExtraArgs ) ;
// Finally, deal with any other stragglers that asked for the same surface we did.
int pendingIndex = m_PendingAsyncLoads . Find ( pJob - > m_TextureName . Get ( ) ) ;
Assert ( pendingIndex ! = m_PendingAsyncLoads . InvalidIndex ( ) ) ;
FOR_EACH_VEC ( m_PendingAsyncLoads [ pendingIndex ] , i )
{
AsyncLoadJob_t & straggler = m_PendingAsyncLoads [ pendingIndex ] [ i ] ;
straggler . m_pResultData = pJob - > m_pResultData ;
if ( ! bDownloaded & & ! IsJobCancelled ( & straggler ) )
{
bDownloaded = true ;
straggler . m_pResultData - > Download ( NULL , straggler . m_nAdditionalCreationFlags ) ;
}
straggler . m_pRecipient - > OnAsyncFindComplete ( straggler . m_pResultData , straggler . m_pExtraArgs ) ;
SafeRelease ( & straggler . m_pRecipient ) ;
}
// Add ourselves to the list of loaded things.
if ( bDownloaded )
{
// The texture list has to be protected by the materials lock.
MaterialLock_t hMaterialLock = materials - > Lock ( ) ;
// It's possible that the texture wasn't actually unloaded, so we may have reloaded something unnecessarily.
// If so, just don't re-add it.
if ( m_TextureList . Find ( pJob - > m_pResultData - > GetName ( ) ) = = m_TextureList . InvalidIndex ( ) )
m_TextureList . Insert ( pJob - > m_pResultData - > GetName ( ) , pJob - > m_pResultData ) ;
materials - > Unlock ( hMaterialLock ) ;
}
else
{
// If we didn't download, need to clean up the leftover file data that we loaded on the other thread
pJob - > m_pResultData - > AsyncCancelReadTexture ( ) ;
}
// Can't release the Recipient until after we tell the stragglers, because the recipient may be the only
// ref to the texture, and cleaning it up may clean up the texture but leave us with a seemingly valid pointer.
SafeRelease ( & pJob - > m_pRecipient ) ;
// Dump out the whole lot.
m_PendingAsyncLoads . RemoveAt ( pendingIndex ) ;
}
void CTextureManager : : AsyncLoad ( const AsyncLoadJob_t & job )
{
Assert ( m_pAsyncLoader ) ;
m_pAsyncLoader - > AsyncLoad ( job ) ;
}
void CTextureManager : : AsyncCreateTextureFromRenderTarget ( ITexture * pSrcRt , const char * pDstName , ImageFormat dstFmt , bool bGenMips , int nAdditionalCreationFlags , IAsyncTextureOperationReceiver * pRecipient , void * pExtraArgs )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
Assert ( pSrcRt ) ;
AsyncReadJob_t * pAsyncRead = new AsyncReadJob_t ( pSrcRt , pDstName , dstFmt , bGenMips , nAdditionalCreationFlags , pRecipient , pExtraArgs ) ;
AsyncReadTexture ( pAsyncRead ) ;
}
void CTextureManager : : CompleteAsyncRead ( AsyncReadJob_t * pJob )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// Release the texture back into the pool.
ReleaseReadbackTexture ( pJob - > m_pSysmemTex ) ;
pJob - > m_pSysmemTex = NULL ;
int w = pJob - > m_pSrcRt - > GetActualWidth ( ) ;
int h = pJob - > m_pSrcRt - > GetActualHeight ( ) ;
int mips = pJob - > m_bGenMips ? ImageLoader : : GetNumMipMapLevels ( w , h ) : 1 ;
int nFlags = pJob - > m_nAdditionalCreationFlags
| TEXTUREFLAGS_SINGLECOPY
| TEXTUREFLAGS_IGNORE_PICMIP
| ( mips > 1
? TEXTUREFLAGS_ALL_MIPS
: TEXTUREFLAGS_NOMIP
)
;
// Create the texture
ITexture * pFinalTex = materials - > CreateNamedTextureFromBitsEx ( pJob - > m_pDstName , TEXTURE_GROUP_RUNTIME_COMPOSITE , w , h , mips , pJob - > m_dstFmt , pJob - > m_finalTexelData . Count ( ) , pJob - > m_finalTexelData . Base ( ) , nFlags ) ;
Assert ( pFinalTex ) ;
// Make the callback!
pJob - > m_pRecipient - > OnAsyncCreateComplete ( pFinalTex , pJob - > m_pExtraArgs ) ;
SafeRelease ( & pJob - > m_pSrcRt ) ;
SafeRelease ( & pJob - > m_pRecipient ) ;
SafeRelease ( & pFinalTex ) ;
}
void CTextureManager : : AsyncReadTexture ( AsyncReadJob_t * pJob )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
Assert ( m_pAsyncReader ) ;
Assert ( pJob ) ;
pJob - > m_pSysmemTex = AcquireReadbackTexture ( pJob - > m_pSrcRt - > GetActualWidth ( ) , pJob - > m_pSrcRt - > GetActualHeight ( ) , pJob - > m_pSrcRt - > GetImageFormat ( ) ) ;
Assert ( pJob - > m_pSysmemTex ) ;
if ( ! pJob - > m_pSysmemTex )
{
Assert ( ! " Need to deal with this error case " ) ; // TODOERROR
return ;
}
m_pAsyncReader - > AsyncReadback ( pJob ) ;
}
ITextureInternal * CTextureManager : : AcquireReadbackTexture ( int w , int h , ImageFormat fmt )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s-TryExisting " , __FUNCTION__ ) ;
MaterialLock_t hMaterialLock = materials - > Lock ( ) ;
FOR_EACH_VEC ( m_ReadbackTextures , i )
{
ITextureInternal * pTex = m_ReadbackTextures [ i ] ;
Assert ( pTex ) ;
if ( pTex - > GetActualWidth ( ) = = w
& & pTex - > GetActualHeight ( ) = = h
& & pTex - > GetImageFormat ( ) = = fmt )
{
// Found one in the cache already
pTex - > AddRef ( ) ;
m_ReadbackTextures . Remove ( i ) ;
materials - > Unlock ( hMaterialLock ) ;
return pTex ;
}
}
materials - > Unlock ( hMaterialLock ) ;
}
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s-CreateNew " , __FUNCTION__ ) ;
ITextureInternal * stagingTex = CreateProceduralTexture ( " readbacktex " , TEXTURE_GROUP_OTHER , w , h , 1 , fmt , TEXTUREFLAGS_STAGING_MEMORY | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_IMMEDIATE_CLEANUP ) ;
// AddRef here for caller.
stagingTex - > AddRef ( ) ;
return stagingTex ;
}
void CTextureManager : : ReleaseReadbackTexture ( ITextureInternal * pTex )
{
Assert ( pTex ) ;
MaterialLock_t hMaterialLock = materials - > Lock ( ) ;
// Release matching AddRef in AcquireReadbackTexture
pTex - > Release ( ) ;
m_ReadbackTextures . AddToTail ( pTex ) ;
materials - > Unlock ( hMaterialLock ) ;
}
# ifdef STAGING_ONLY
static int SortTexturesForDump ( const CUtlPair < CUtlString , void * > * sz1 , const CUtlPair < CUtlString , void * > * sz2 )
{
int sortVal = CUtlString : : SortCaseSensitive ( & sz1 - > first , & sz2 - > first ) ;
if ( sortVal ! = 0 )
return sortVal ;
return int ( ( int ) sz1 - > second - ( int ) sz2 - > second ) ;
}
void CTextureManager : : DumpTextureList ( )
{
CUtlVector < CUtlPair < CUtlString , void * > > textures ;
MaterialLock_t hMaterialLock = materials - > Lock ( ) ;
FOR_EACH_DICT ( m_TextureList , i )
{
textures . AddToTail ( MakeUtlPair ( CUtlString ( m_TextureList [ i ] - > GetName ( ) ) , ( void * ) m_TextureList [ i ] ) ) ;
}
materials - > Unlock ( hMaterialLock ) ;
// Now dump them out, sorted first by the texture name, then by address.
textures . Sort ( SortTexturesForDump ) ;
FOR_EACH_VEC ( textures , i )
{
CUtlPair < CUtlString , void * > & pair = textures [ i ] ;
Warning ( " [%p]: %s \n " , pair . second , pair . first . Get ( ) ) ;
}
}
# endif
//-----------------------------------------------------------------------------
// Warms the texture cache from a vpk. This will cause coarse mipmaps to be
// available all the time, starting with mipmap level 3. This allows us to have
// all the textures available all the time, but we only pay for fine levels when
// we actually need them.
//-----------------------------------------------------------------------------
void CTextureManager : : WarmTextureCache ( )
{
// Disable cache for osx/linux for now.
if ( CommandLine ( ) - > CheckParm ( " -no_texture_stream " ) )
return ;
MemoryInformation memInfo ;
if ( GetMemoryInformation ( & memInfo ) )
{
if ( memInfo . m_nPhysicalRamMbTotal < = 3584 )
return ;
}
COM_TimestampedLog ( " WarmTextureCache() - Begin " ) ;
// If this fires, we need to relocate this elsewhere--there's no point in doing the loading
// if we're not going to be able to download them right now.
Assert ( g_pShaderAPI - > CanDownloadTextures ( ) ) ;
g_pFullFileSystem - > AddSearchPath ( " tf2_texture_cache.vpk " , cTextureCachePathDir , PATH_ADD_TO_TAIL ) ;
CUtlDict < int > filesToLoad ( k_eDictCompareTypeCaseSensitive ) ;
// TODO: Maybe work directly with VPK (still need to add to the filesystem for LoadTexture)?
// CPackFile
// Add the pak and then walk through the contents.
FindFilesToLoad ( & filesToLoad , " *.* " ) ;
// Then add the list of files from the cache, which will deal with running without a VPK and also
// allow us to add late stragglers.
ReadFilesToLoad ( & filesToLoad , " texture_preload_list.txt " ) ;
if ( filesToLoad . Count ( ) = = 0 )
{
COM_TimestampedLog ( " WarmTextureCache() - End (No files loaded) " ) ;
return ;
}
Assert ( filesToLoad . Count ( ) > 0 ) ;
// Now read all of the files.
// TODO: This needs to read in specific order to ensure peak performance.
FOR_EACH_DICT ( filesToLoad , i )
{
const char * pFilename = filesToLoad . GetElementName ( i ) ;
// Load the texture. This will only load the lower mipmap levels because that's the file we'll find now.
ITextureInternal * pTex = LoadTexture ( pFilename , TEXTURE_GROUP_PRECACHED , TEXTUREFLAGS_STREAMABLE_COARSE ) ;
COM_TimestampedLog ( " WarmTextureCache(): LoadTexture( %s ): Complete " , pFilename ) ;
if ( ( pTex - > GetFlags ( ) & TEXTUREFLAGS_STREAMABLE ) = = 0 )
{
STAGING_ONLY_EXEC ( Warning ( " %s is listed in texture_preload_list.txt or is otherwise marked for streaming. It cannot be streamed and should be removed from the streaming system. \n " , pFilename ) ) ;
ITextureInternal : : Destroy ( pTex ) ;
continue ;
}
if ( ! pTex - > IsError ( ) )
{
m_TextureList . Insert ( pTex - > GetName ( ) , pTex ) ;
pTex - > AddRef ( ) ;
m_preloadedTextures . AddToTail ( pTex ) ;
}
else
{
// Don't preload broken textures
ITextureInternal : : Destroy ( pTex ) ;
}
}
g_pFullFileSystem - > RemoveSearchPath ( " tf2_texture_cache.vpk " , cTextureCachePathDir ) ;
COM_TimestampedLog ( " WarmTextureCache() - End " ) ;
}
//-----------------------------------------------------------------------------
// Reads the list of files contained in the vpk loaded above, and adds them to the
// list of files we need to load (passing in as pOutFilesToLoad). The map contains
// the
//-----------------------------------------------------------------------------
void CTextureManager : : FindFilesToLoad ( CUtlDict < int > * pOutFilesToLoad , const char * pFilename )
{
Assert ( pOutFilesToLoad ! = NULL ) ;
FileFindHandle_t fh ;
pFilename = g_pFullFileSystem - > FindFirstEx ( pFilename , cTextureCachePathDir , & fh ) ;
while ( pFilename ! = NULL )
{
if ( g_pFullFileSystem - > FindIsDirectory ( fh ) )
{
if ( pFilename [ 0 ] ! = ' . ' )
{
char childFilename [ _MAX_PATH ] ;
V_sprintf_safe ( childFilename , " %s/*.* " , pFilename ) ;
FindFilesToLoad ( pOutFilesToLoad , childFilename ) ;
}
}
else
{
char filenameNoExtension [ _MAX_PATH ] ;
V_StripExtension ( pFilename , filenameNoExtension , _MAX_PATH ) ;
// Add the file to the list, which we will later traverse in order to ensure we're hitting these in the expected order for the VPK.
( * pOutFilesToLoad ) . Insert ( CUtlString ( filenameNoExtension ) , 0 ) ;
}
pFilename = g_pFullFileSystem - > FindNext ( fh ) ;
}
}
//-----------------------------------------------------------------------------
// Read the contents of pFilename, which should just be a list of texture names
// that we should load.
//-----------------------------------------------------------------------------
void CTextureManager : : ReadFilesToLoad ( CUtlDict < int > * pOutFilesToLoad , const char * pFilename )
{
Assert ( pOutFilesToLoad ! = NULL ) ;
FileHandle_t fh = g_pFullFileSystem - > Open ( pFilename , " r " ) ;
if ( ! fh )
return ;
CUtlBuffer fileContents ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
if ( ! g_pFullFileSystem - > ReadToBuffer ( fh , fileContents ) )
goto cleanup ;
char buffer [ _MAX_PATH + 1 ] ;
while ( 1 )
{
fileContents . GetLine ( buffer , _MAX_PATH ) ;
if ( buffer [ 0 ] = = 0 )
break ;
V_StripWhitespace ( buffer ) ;
if ( buffer [ 0 ] = = 0 )
continue ;
// If it's not in the map already, add it.
if ( pOutFilesToLoad - > Find ( buffer ) = = pOutFilesToLoad - > InvalidIndex ( ) )
( * pOutFilesToLoad ) . Insert ( buffer , 0 ) ;
}
cleanup :
g_pFullFileSystem - > Close ( fh ) ;
}
void CTextureManager : : UpdatePostAsync ( )
{
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
// Update the async loader, which affects streaming in (streaming out is handled below).
// Both stream in and stream out have to happen while the async job is not running because
// they muck with shaderapi texture handles which could be in use if the async job is currently
// being run
if ( m_pAsyncLoader )
m_pAsyncLoader - > ThreadMain_Update ( ) ;
// First, move everything from the async request queue to active list
ITextureInternal * pRequest = NULL ;
while ( m_asyncStreamingRequests . PopItem ( & pRequest ) )
{
Assert ( pRequest ! = NULL ) ;
// Update the LOD bias to smoothly stream the texture in. We only need to do this on frames that
// we actually have been requested to draw--other frames it doesn't matter (see, because we're not drawing?)
pRequest - > UpdateLodBias ( ) ;
m_textureStreamingRequests . InsertOrReplace ( pRequest , g_FrameNum ) ;
}
// Then update streaming
const int cThirtySecondsOrSoInFrames = 2000 ;
// First, remove old stuff.
FOR_EACH_MAP_FAST ( m_textureStreamingRequests , i )
{
if ( m_textureStreamingRequests [ i ] + cThirtySecondsOrSoInFrames < g_FrameNum )
{
ITextureInternal * pTex = m_textureStreamingRequests . Key ( i ) ;
// It's been awhile since we were asked to full res this texture, so let's evict
// if it's still full res.
if ( pTex - > GetTargetResidence ( ) = = RESIDENT_FULL )
pTex - > MakeResident ( RESIDENT_PARTIAL ) ;
m_textureStreamingRequests . RemoveAt ( i ) ;
}
}
// Then, start allowing new stuff to ask for data.
FOR_EACH_MAP_FAST ( m_textureStreamingRequests , i )
{
int requestFrame = m_textureStreamingRequests [ i ] ;
if ( g_FrameNum = = requestFrame )
{
ITextureInternal * pTex = m_textureStreamingRequests . Key ( i ) ;
if ( pTex - > GetTargetResidence ( ) = = RESIDENT_FULL )
continue ;
// TODO: What to do if this fails? Auto-reask next frame?
pTex - > MakeResident ( RESIDENT_FULL ) ;
}
}
// Finally, flush any immediate release textures marked for cleanup that are still unreferenced.
CleanupPossiblyUnreferencedTextures ( ) ;
}
void CTextureManager : : ReleaseAsyncScratchVTF ( IVTFTexture * pScratchVTF )
{
Assert ( m_pAsyncLoader ! = NULL & & pScratchVTF ! = NULL ) ;
m_pAsyncLoader - > ReleaseAsyncReadBuffer ( pScratchVTF ) ;
}
bool CTextureManager : : ThreadInAsyncLoadThread ( ) const
{
return ThreadGetCurrentId ( ) = = m_nAsyncLoadThread ;
}
bool CTextureManager : : ThreadInAsyncReadThread ( ) const
{
return ThreadGetCurrentId ( ) = = m_nAsyncReadThread ;
}
bool CTextureManager : : AddTextureCompositorTemplate ( const char * pName , KeyValues * pTmplDesc )
{
Assert ( pName & & pTmplDesc ) ;
int ndx = m_TexCompTemplates . Find ( pName ) ;
if ( ndx ! = m_TexCompTemplates . InvalidIndex ( ) )
{
// Later definitions stomp earlier ones. This lets the GC win.
delete m_TexCompTemplates [ ndx ] ;
m_TexCompTemplates . RemoveAt ( ndx ) ;
}
CTextureCompositorTemplate * pNewTmpl = CTextureCompositorTemplate : : Create ( pName , pTmplDesc ) ;
// If this is the case, the logging has already been done.
if ( pNewTmpl = = NULL )
return false ;
m_TexCompTemplates . Insert ( pName , pNewTmpl ) ;
return true ;
}
bool CTextureManager : : VerifyTextureCompositorTemplates ( )
{
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL1 ) ;
bool allSuccess = true ;
FOR_EACH_DICT_FAST ( m_TexCompTemplates , i )
{
if ( m_TexCompTemplates [ i ] - > ResolveDependencies ( ) )
{
if ( m_TexCompTemplates [ i ] - > HasDependencyCycles ( ) )
{
allSuccess = false ;
}
}
else
{
allSuccess = false ;
}
}
return allSuccess ;
}
CTextureCompositorTemplate * CTextureManager : : FindTextureCompositorTemplate ( const char * pName )
{
unsigned short i = m_TexCompTemplates . Find ( pName ) ;
if ( m_TexCompTemplates . IsValidIndex ( i ) )
return m_TexCompTemplates [ i ] ;
return NULL ;
}
bool CTextureManager : : HasPendingTextureDestroys ( ) const
{
return m_PossiblyUnreferencedTextures . Count ( ) ! = 0 ;
}
void CTextureManager : : CoolTextureCache ( )
{
FOR_EACH_VEC ( m_preloadedTextures , i )
{
m_preloadedTextures [ i ] - > Release ( ) ;
}
m_preloadedTextures . RemoveAll ( ) ;
}
void CTextureManager : : RequestAllMipmaps ( ITextureInternal * pTex )
{
Assert ( pTex ) ;
// Don't mark these for load if suspended
if ( m_iSuspendTextureStreaming )
return ;
unsigned int nTexFlags = pTex - > GetFlags ( ) ;
// If this isn't a streamable texture or if there are no mipmaps, there's nothing to do.
if ( ! ( nTexFlags & TEXTUREFLAGS_STREAMABLE ) | | ( nTexFlags & TEXTUREFLAGS_NOMIP ) )
return ;
m_asyncStreamingRequests . PushItem ( pTex ) ;
}
void CTextureManager : : EvictAllTextures ( )
{
FOR_EACH_DICT_FAST ( m_TextureList , i )
{
ITextureInternal * pTex = m_TextureList [ i ] ;
if ( ! pTex )
continue ;
// If the fine mipmaps are present
if ( ( ( pTex - > GetFlags ( ) & TEXTUREFLAGS_STREAMABLE ) ! = 0 ) & & pTex - > GetTargetResidence ( ) = = RESIDENT_FULL )
pTex - > MakeResident ( RESIDENT_PARTIAL ) ;
}
}
CON_COMMAND ( mat_evict_all , " Evict all fine mipmaps from the gpu " )
{
TextureManager ( ) - > EvictAllTextures ( ) ;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
static ImageFormat GetImageFormatRawReadback ( ImageFormat fmt )
{
switch ( fmt )
{
case IMAGE_FORMAT_RGBA8888 :
return IMAGE_FORMAT_BGRA8888 ;
case IMAGE_FORMAT_BGRA8888 :
return IMAGE_FORMAT_BGRA8888 ;
default :
Assert ( ! " Unsupported format in GetImageFormatRawReadback, this will likely result in color-swapped textures " ) ;
} ;
return fmt ;
}