2020-04-23 00:56:21 +08:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=====================================================================================//
# ifdef PROTECTED_THINGS_ENABLE
# undef PROTECTED_THINGS_ENABLE
# endif
# include "platform.h"
// HACK: Need ShellExecute for PSD updates
# ifdef IS_WINDOWS_PC
# include <windows.h>
# include <shellapi.h>
# pragma comment ( lib, "shell32" )
# endif
# include "materialsystem_global.h"
# include "shaderapi/ishaderapi.h"
# include "itextureinternal.h"
# include "utlsymbol.h"
# include "time.h"
# include <sys/types.h>
# include <sys/stat.h>
# include "bitmap/imageformat.h"
# include "bitmap/tgaloader.h"
# include "bitmap/tgawriter.h"
# ifdef _WIN32
# include "direct.h"
# endif
# include "colorspace.h"
# include "string.h"
# include <stdlib.h>
# include "utlmemory.h"
# include "IHardwareConfigInternal.h"
# include "filesystem.h"
# include "tier1/strtools.h"
# include "vtf/vtf.h"
# include "materialsystem/materialsystem_config.h"
# include "mempool.h"
# include "texturemanager.h"
# include "utlbuffer.h"
# include "pixelwriter.h"
# include "tier1/callqueue.h"
# include "tier1/UtlStringMap.h"
# include "filesystem/IQueuedLoader.h"
# include "tier2/fileutils.h"
# include "filesystem.h"
# include "tier2/p4helpers.h"
# include "tier2/tier2.h"
# include "p4lib/ip4.h"
# include "ctype.h"
# include "ifilelist.h"
# include "tier0/icommandline.h"
# include "tier0/vprof.h"
// NOTE: This must be the last file included!!!
# include "tier0/memdbgon.h"
// this allows the command line to force the "all mips" flag to on for all textures
bool g_bForceTextureAllMips = false ;
# if defined(IS_WINDOWS_PC)
static void ConVarChanged_mat_managedtextures ( IConVar * var , const char * pOldValue , float flOldValue ) ;
static ConVar mat_managedtextures ( " mat_managedtextures " , " 1 " , FCVAR_ARCHIVE , " If set, allows Direct3D to manage texture uploading at the cost of extra system memory " , & ConVarChanged_mat_managedtextures ) ;
static void ConVarChanged_mat_managedtextures ( IConVar * var , const char * pOldValue , float flOldValue )
{
if ( mat_managedtextures . GetBool ( ) ! = ( flOldValue ! = 0 ) )
{
materials - > ReleaseResources ( ) ;
materials - > ReacquireResources ( ) ;
}
}
# endif
static ConVar mat_spew_on_texture_size ( " mat_spew_on_texture_size " , " 0 " , 0 , " Print warnings about vtf content that isn't of the expected size " ) ;
static ConVar mat_lodin_time ( " mat_lodin_time " , " 5.0 " , FCVAR_DEVELOPMENTONLY ) ;
static ConVar mat_lodin_hidden_pop ( " mat_lodin_hidden_pop " , " 1 " , FCVAR_DEVELOPMENTONLY ) ;
# define TEXTURE_FNAME_EXTENSION ".vtf"
# define TEXTURE_FNAME_EXTENSION_LEN 4
# define TEXTURE_FNAME_EXTENSION_NORMAL "_normal.vtf"
# ifdef STAGING_ONLY
ConVar mat_spewalloc ( " mat_spewalloc " , " 0 " ) ;
# else
ConVar mat_spewalloc ( " mat_spewalloc " , " 0 " , FCVAR_ARCHIVE | FCVAR_DEVELOPMENTONLY ) ;
# endif
struct TexDimensions_t
{
uint16 m_nWidth ;
uint16 m_nHeight ;
uint16 m_nMipCount ;
uint16 m_nDepth ;
TexDimensions_t ( uint16 nWidth = 0 , uint nHeight = 0 , uint nMipCount = 0 , uint16 nDepth = 1 )
: m_nWidth ( nWidth )
, m_nHeight ( nHeight )
, m_nMipCount ( nMipCount )
, m_nDepth ( nDepth )
{ }
} ;
# ifdef STAGING_ONLY
struct TexInfo_t
{
CUtlString m_Name ;
unsigned short m_nWidth ;
unsigned short m_nHeight ;
unsigned short m_nDepth ;
unsigned short m_nMipCount ;
unsigned short m_nFrameCount ;
unsigned short m_nCopies ;
ImageFormat m_Format ;
uint64 ComputeTexSize ( ) const
{
uint64 total = 0 ;
unsigned short width = m_nWidth ;
unsigned short height = m_nHeight ;
unsigned short depth = m_nDepth ;
for ( int mip = 0 ; mip < m_nMipCount ; + + mip )
{
// Make sure that mip count lines up with the count
Assert ( width > 1 | | height > 1 | | depth > 1 | | ( mip = = ( m_nMipCount - 1 ) ) ) ;
total + = ImageLoader : : GetMemRequired ( width , height , depth , m_Format , false ) ;
width = Max ( 1 , width > > 1 ) ;
height = Max ( 1 , height > > 1 ) ;
depth = Max ( 1 , depth > > 1 ) ;
}
return total * Min ( ( unsigned short ) 1 , m_nFrameCount ) * Min ( ( unsigned short ) 1 , m_nCopies ) ;
}
TexInfo_t ( const char * name = " " , unsigned short w = 0 , unsigned short h = 0 , unsigned short d = 0 , unsigned short mips = 0 , unsigned short frames = 0 , unsigned short copies = 0 , ImageFormat fmt = IMAGE_FORMAT_UNKNOWN )
: m_nWidth ( w )
, m_nHeight ( h )
, m_nDepth ( d )
, m_nMipCount ( mips )
, m_nFrameCount ( frames )
, m_nCopies ( copies )
, m_Format ( fmt )
{
if ( name & & name [ 0 ] )
m_Name = name ;
else
m_Name = " <unnamed> " ;
}
} ;
CUtlMap < ITexture * , TexInfo_t > g_currentTextures ( DefLessFunc ( ITexture * ) ) ;
# endif
//-----------------------------------------------------------------------------
// Internal texture flags
//-----------------------------------------------------------------------------
enum InternalTextureFlags
{
TEXTUREFLAGSINTERNAL_ERROR = 0x00000001 ,
TEXTUREFLAGSINTERNAL_ALLOCATED = 0x00000002 ,
TEXTUREFLAGSINTERNAL_PRELOADED = 0x00000004 , // 360: textures that went through the preload process
TEXTUREFLAGSINTERNAL_QUEUEDLOAD = 0x00000008 , // 360: load using the queued loader
TEXTUREFLAGSINTERNAL_EXCLUDED = 0x00000020 , // actual exclusion state
TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE = 0x00000040 , // desired exclusion state
TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET = 0x00000080 , // 360: should only allocate texture bits upon first resolve, destroy at level end
} ;
static int GetThreadId ( ) ;
static bool SLoadTextureBitsFromFile ( IVTFTexture * * ppOutVtfTexture , FileHandle_t hFile , unsigned int nFlags , TextureLODControlSettings_t * pInOutCachedFileLodSettings , int nDesiredDimensionLimit , unsigned short * pOutStreamedMips , const char * pName , const char * pCacheFileName , TexDimensions_t * pOptOutMappingDims = NULL , TexDimensions_t * pOptOutActualDims = NULL , TexDimensions_t * pOptOutAllocatedDims = NULL , unsigned int * pOptOutStripFlags = NULL ) ;
static int ComputeActualMipCount ( const TexDimensions_t & actualDims , unsigned int nFlags ) ;
static int ComputeMipSkipCount ( const char * pName , const TexDimensions_t & mappingDims , bool bIgnorePicmip , IVTFTexture * pOptVTFTexture , unsigned int nFlags , int nDesiredDimensionLimit , unsigned short * pOutStreamedMips , TextureLODControlSettings_t * pInOutCachedFileLodSettings , TexDimensions_t * pOptOutActualDims , TexDimensions_t * pOptOutAllocatedDims , unsigned int * pOptOutStripFlags ) ;
static int GetOptimalReadBuffer ( CUtlBuffer * pOutOptimalBuffer , FileHandle_t hFile , int nFileSize ) ;
static void FreeOptimalReadBuffer ( int nMaxSize ) ;
//-----------------------------------------------------------------------------
// Use Warning to show texture flags.
//-----------------------------------------------------------------------------
static void PrintFlags ( unsigned int flags )
{
if ( flags & TEXTUREFLAGS_NOMIP )
{
Warning ( " TEXTUREFLAGS_NOMIP| " ) ;
}
if ( flags & TEXTUREFLAGS_NOLOD )
{
Warning ( " TEXTUREFLAGS_NOLOD| " ) ;
}
if ( flags & TEXTUREFLAGS_SRGB )
{
Warning ( " TEXTUREFLAGS_SRGB| " ) ;
}
if ( flags & TEXTUREFLAGS_POINTSAMPLE )
{
Warning ( " TEXTUREFLAGS_POINTSAMPLE| " ) ;
}
if ( flags & TEXTUREFLAGS_TRILINEAR )
{
Warning ( " TEXTUREFLAGS_TRILINEAR| " ) ;
}
if ( flags & TEXTUREFLAGS_CLAMPS )
{
Warning ( " TEXTUREFLAGS_CLAMPS| " ) ;
}
if ( flags & TEXTUREFLAGS_CLAMPT )
{
Warning ( " TEXTUREFLAGS_CLAMPT| " ) ;
}
if ( flags & TEXTUREFLAGS_HINT_DXT5 )
{
Warning ( " TEXTUREFLAGS_HINT_DXT5| " ) ;
}
if ( flags & TEXTUREFLAGS_ANISOTROPIC )
{
Warning ( " TEXTUREFLAGS_ANISOTROPIC| " ) ;
}
if ( flags & TEXTUREFLAGS_PROCEDURAL )
{
Warning ( " TEXTUREFLAGS_PROCEDURAL| " ) ;
}
if ( flags & TEXTUREFLAGS_ALL_MIPS )
{
Warning ( " TEXTUREFLAGS_ALL_MIPS| " ) ;
}
if ( flags & TEXTUREFLAGS_SINGLECOPY )
{
Warning ( " TEXTUREFLAGS_SINGLECOPY| " ) ;
}
if ( flags & TEXTUREFLAGS_STAGING_MEMORY )
{
Warning ( " TEXTUREFLAGS_STAGING_MEMORY| " ) ;
}
if ( flags & TEXTUREFLAGS_IGNORE_PICMIP )
{
Warning ( " TEXTUREFLAGS_IGNORE_PICMIP| " ) ;
}
if ( flags & TEXTUREFLAGS_IMMEDIATE_CLEANUP )
{
Warning ( " TEXTUREFLAGS_IMMEDIATE_CLEANUP| " ) ;
}
}
namespace TextureLodOverride
{
struct OverrideInfo
{
OverrideInfo ( ) : x ( 0 ) , y ( 0 ) { }
OverrideInfo ( int8 x_ , int8 y_ ) : x ( x_ ) , y ( y_ ) { }
int8 x , y ;
} ;
// Override map
typedef CUtlStringMap < OverrideInfo > OverrideMap_t ;
OverrideMap_t s_OverrideMap ;
// Retrieves the override info adjustments
OverrideInfo Get ( char const * szName )
{
UtlSymId_t idx = s_OverrideMap . Find ( szName ) ;
if ( idx ! = s_OverrideMap . InvalidIndex ( ) )
return s_OverrideMap [ idx ] ;
else
return OverrideInfo ( ) ;
}
// Combines the existing override info adjustments with the given one
void Add ( char const * szName , OverrideInfo oi )
{
OverrideInfo oiex = Get ( szName ) ;
oiex . x + = oi . x ;
oiex . y + = oi . y ;
s_OverrideMap [ szName ] = oiex ;
}
} ; // end namespace TextureLodOverride
class CTextureStreamingJob ;
//-----------------------------------------------------------------------------
// Base texture class
//-----------------------------------------------------------------------------
class CTexture : public ITextureInternal
{
public :
CTexture ( ) ;
virtual ~ CTexture ( ) ;
virtual const char * GetName ( void ) const ;
const char * GetTextureGroupName ( void ) const ;
// Stats about the texture itself
virtual ImageFormat GetImageFormat ( ) const ;
NormalDecodeMode_t GetNormalDecodeMode ( ) const { return NORMAL_DECODE_NONE ; }
virtual int GetMappingWidth ( ) const ;
virtual int GetMappingHeight ( ) const ;
virtual int GetActualWidth ( ) const ;
virtual int GetActualHeight ( ) const ;
virtual int GetNumAnimationFrames ( ) const ;
virtual bool IsTranslucent ( ) const ;
virtual void GetReflectivity ( Vector & reflectivity ) ;
// Reference counting
virtual void IncrementReferenceCount ( ) ;
virtual void DecrementReferenceCount ( ) ;
virtual int GetReferenceCount ( ) ;
// Used to modify the texture bits (procedural textures only)
virtual void SetTextureRegenerator ( ITextureRegenerator * pTextureRegen ) ;
// Little helper polling methods
virtual bool IsNormalMap ( ) const ;
virtual bool IsCubeMap ( void ) const ;
virtual bool IsRenderTarget ( ) const ;
virtual bool IsTempRenderTarget ( void ) const ;
virtual bool IsProcedural ( ) const ;
virtual bool IsMipmapped ( ) const ;
virtual bool IsError ( ) const ;
// For volume textures
virtual bool IsVolumeTexture ( ) const ;
virtual int GetMappingDepth ( ) const ;
virtual int GetActualDepth ( ) const ;
// Various ways of initializing the texture
void InitFileTexture ( const char * pTextureFile , const char * pTextureGroupName ) ;
void InitProceduralTexture ( const char * pTextureName , const char * pTextureGroupName , int w , int h , int d , ImageFormat fmt , int nFlags , ITextureRegenerator * generator = NULL ) ;
// Releases the texture's hw memory
void ReleaseMemory ( ) ;
virtual void OnRestore ( ) ;
// Sets the filtering modes on the texture we're modifying
void SetFilteringAndClampingMode ( bool bOnlyLodValues = false ) ;
void Download ( Rect_t * pRect = NULL , int nAdditionalCreationFlags = 0 ) ;
// Loads up information about the texture
virtual void Precache ( ) ;
// FIXME: Bogus methods... can we please delete these?
virtual void GetLowResColorSample ( float s , float t , float * color ) const ;
// Gets texture resource data of the specified type.
// Params:
// eDataType type of resource to retrieve.
// pnumBytes on return is the number of bytes available in the read-only data buffer or is undefined
// Returns:
// pointer to the resource data, or NULL. Note that the data from this pointer can disappear when
// the texture goes away - you want to copy this data!
virtual void * GetResourceData ( uint32 eDataType , size_t * pNumBytes ) const ;
virtual int GetApproximateVidMemBytes ( void ) const ;
// Stretch blit the framebuffer into this texture.
virtual void CopyFrameBufferToMe ( int nRenderTargetID = 0 , Rect_t * pSrcRect = NULL , Rect_t * pDstRect = NULL ) ;
virtual void CopyMeToFrameBuffer ( int nRenderTargetID = 0 , Rect_t * pSrcRect = NULL , Rect_t * pDstRect = NULL ) ;
virtual ITexture * GetEmbeddedTexture ( int nIndex ) ;
// Get the shaderapi texture handle associated w/ a particular frame
virtual ShaderAPITextureHandle_t GetTextureHandle ( int nFrame , int nChannel = 0 ) ;
// Sets the texture as the render target
virtual void Bind ( Sampler_t sampler ) ;
virtual void Bind ( Sampler_t sampler1 , int nFrame , Sampler_t sampler2 = ( Sampler_t ) - 1 ) ;
virtual void BindVertexTexture ( VertexTextureSampler_t stage , int nFrame ) ;
// Set this texture as a render target
bool SetRenderTarget ( int nRenderTargetID ) ;
// Set this texture as a render target (optionally set depth texture as depth buffer as well)
bool SetRenderTarget ( int nRenderTargetID , ITexture * pDepthTexture ) ;
virtual void MarkAsPreloaded ( bool bSet ) ;
virtual bool IsPreloaded ( ) const ;
virtual void MarkAsExcluded ( bool bSet , int nDimensionsLimit ) ;
virtual bool UpdateExcludedState ( void ) ;
// Retrieve the vtf flags mask
virtual unsigned int GetFlags ( void ) const ;
virtual void ForceLODOverride ( int iNumLodsOverrideUpOrDown ) ;
void GetFilename ( char * pOut , int maxLen ) const ;
virtual void ReloadFilesInList ( IFileList * pFilesToReload ) ;
// Save texture to a file.
virtual bool SaveToFile ( const char * fileName ) ;
// Load the texture from a file.
bool AsyncReadTextureFromFile ( IVTFTexture * pVTFTexture , unsigned int nAdditionalCreationFlags ) ;
void AsyncCancelReadTexture ( ) ;
virtual void Map ( void * * pOutBits , int * pOutPitch ) ;
virtual void Unmap ( ) ;
virtual ResidencyType_t GetCurrentResidence ( ) const { return m_residenceCurrent ; }
virtual ResidencyType_t GetTargetResidence ( ) const { return m_residenceTarget ; }
virtual bool MakeResident ( ResidencyType_t newResidence ) ;
virtual void UpdateLodBias ( ) ;
protected :
bool IsDepthTextureFormat ( ImageFormat fmt ) ;
void ReconstructTexture ( bool bCopyFromCurrent ) ;
void GetCacheFilename ( char * pOutBuffer , int bufferSize ) const ;
bool GetFileHandle ( FileHandle_t * pOutFileHandle , char * pCacheFilename , char * * ppResolvedFilename ) const ;
void ReconstructPartialTexture ( const Rect_t * pRect ) ;
bool HasBeenAllocated ( ) const ;
void WriteDataToShaderAPITexture ( int nFrameCount , int nFaceCount , int nFirstFace , int nMipCount , IVTFTexture * pVTFTexture , ImageFormat fmt ) ;
// Initializes/shuts down the texture
void Init ( int w , int h , int d , ImageFormat fmt , int iFlags , int iFrameCount ) ;
void Shutdown ( ) ;
// Sets the texture name
void SetName ( const char * pName ) ;
// Assigns/releases texture IDs for our animation frames
void AllocateTextureHandles ( ) ;
void ReleaseTextureHandles ( ) ;
// Calculates info about whether we can make the texture smaller and by how much
// Returns the number of skipped mip levels
int ComputeActualSize ( bool bIgnorePicmip = false , IVTFTexture * pVTFTexture = NULL , bool bTextureMigration = false ) ;
// Computes the actual format of the texture given a desired src format
ImageFormat ComputeActualFormat ( ImageFormat srcFormat ) ;
// Creates/releases the shader api texture
bool AllocateShaderAPITextures ( ) ;
void FreeShaderAPITextures ( ) ;
void MigrateShaderAPITextures ( ) ;
void NotifyUnloadedFile ( ) ;
// Download bits
void DownloadTexture ( Rect_t * pRect , bool bCopyFromCurrent = false ) ;
void ReconstructTextureBits ( Rect_t * pRect ) ;
// Gets us modifying a particular frame of our texture
void Modify ( int iFrame ) ;
// Sets the texture clamping state on the currently modified frame
void SetWrapState ( ) ;
// Sets the texture filtering state on the currently modified frame
void SetFilterState ( ) ;
// Sets the lod state on the currently modified frame
void SetLodState ( ) ;
// Loads the texture bits from a file. Optionally provides absolute path
IVTFTexture * LoadTextureBitsFromFile ( char * pCacheFileName , char * * pResolvedFilename ) ;
IVTFTexture * HandleFileLoadFailedTexture ( IVTFTexture * pVTFTexture ) ;
// Generates the procedural bits
IVTFTexture * ReconstructProceduralBits ( ) ;
IVTFTexture * ReconstructPartialProceduralBits ( const Rect_t * pRect , Rect_t * pActualRect ) ;
// Sets up debugging texture bits, if appropriate
bool SetupDebuggingTextures ( IVTFTexture * pTexture ) ;
// Generate a texture that shows the various mip levels
void GenerateShowMipLevelsTextures ( IVTFTexture * pTexture ) ;
void Cleanup ( void ) ;
// Converts a source image read from disk into its actual format
bool ConvertToActualFormat ( IVTFTexture * pTexture ) ;
// Builds the low-res image from the texture
void LoadLowResTexture ( IVTFTexture * pTexture ) ;
void CopyLowResImageToTexture ( IVTFTexture * pTexture ) ;
void GetDownloadFaceCount ( int & nFirstFace , int & nFaceCount ) ;
void ComputeMipLevelSubRect ( const Rect_t * pSrcRect , int nMipLevel , Rect_t * pSubRect ) ;
IVTFTexture * GetScratchVTFTexture ( ) ;
void ReleaseScratchVTFTexture ( IVTFTexture * tex ) ;
void ApplyRenderTargetSizeMode ( int & width , int & height , ImageFormat fmt ) ;
virtual void CopyToStagingTexture ( ITexture * pDstTex ) ;
virtual void SetErrorTexture ( bool _isErrorTexture ) ;
// Texture streaming
void MakeNonResident ( ) ;
void MakePartiallyResident ( ) ;
bool MakeFullyResident ( ) ;
void CancelStreamingJob ( bool bJobMustExist = true ) ;
void OnStreamingJobComplete ( ResidencyType_t newResidenceCurrent ) ;
protected :
# ifdef _DEBUG
char * m_pDebugName ;
# endif
// Reflectivity vector
Vector m_vecReflectivity ;
CUtlSymbol m_Name ;
// What texture group this texture is in (winds up setting counters based on the group name,
// then the budget panel views the counters).
CUtlSymbol m_TextureGroupName ;
unsigned int m_nFlags ;
unsigned int m_nInternalFlags ;
CInterlockedInt m_nRefCount ;
// This is the *desired* image format, which may or may not represent reality
ImageFormat m_ImageFormat ;
// mapping dimensions and actual dimensions can/will vary due to user settings, hardware support, etc.
// Allocated is what is physically allocated on the hardware at this instant, and considers texture streaming.
TexDimensions_t m_dimsMapping ;
TexDimensions_t m_dimsActual ;
TexDimensions_t m_dimsAllocated ;
// This is the iWidth/iHeight for whatever is downloaded to the card, ignoring current streaming settings
// Some callers want to know how big the texture is if all data was present, and that's this.
// TODO: Rename this before check in.
unsigned short m_nFrameCount ;
// These are the values for what is truly allocated on the card, including streaming settings.
unsigned short m_nStreamingMips ;
unsigned short m_nOriginalRTWidth ; // The values they initially specified. We generated a different width
unsigned short m_nOriginalRTHeight ; // and height based on screen size and the flags they specify.
unsigned char m_LowResImageWidth ;
unsigned char m_LowResImageHeight ;
unsigned short m_nDesiredDimensionLimit ; // part of texture exclusion
unsigned short m_nActualDimensionLimit ; // value not necessarily accurate, but mismatch denotes dirty state
// m_pStreamingJob is refcounted, but it is not safe to call SafeRelease directly on it--you must call
// CancelStreamingJob to ensure that releasing it doesn't cause a crash.
CTextureStreamingJob * m_pStreamingJob ;
IVTFTexture * m_pStreamingVTF ;
ResidencyType_t m_residenceTarget ;
ResidencyType_t m_residenceCurrent ;
int m_lodClamp ;
int m_lastLodBiasAdjustFrame ;
float m_lodBiasInitial ;
float m_lodBiasCurrent ;
double m_lodBiasStartTime ;
// If the read failed, this will be true. We can't just return from the function because the call may
// happen in the async thread.
bool m_bStreamingFileReadFailed ;
// The set of texture ids for each animation frame
ShaderAPITextureHandle_t * m_pTextureHandles ;
TextureLODControlSettings_t m_cachedFileLodSettings ;
// lowresimage info - used for getting color data from a texture
// without having a huge system mem overhead.
// FIXME: We should keep this in compressed form. .is currently decompressed at load time.
unsigned char * m_pLowResImage ;
ITextureRegenerator * m_pTextureRegenerator ;
// Used to help decide whether or not to recreate the render target if AA changes.
RenderTargetType_t m_nOriginalRenderTargetType ;
RenderTargetSizeMode_t m_RenderTargetSizeMode ;
// Fixed-size allocator
// DECLARE_FIXEDSIZE_ALLOCATOR( CTexture );
public :
void InitRenderTarget ( const char * pRTName , int w , int h , RenderTargetSizeMode_t sizeMode ,
ImageFormat fmt , RenderTargetType_t type , unsigned int textureFlags ,
unsigned int renderTargetFlags ) ;
virtual void DeleteIfUnreferenced ( ) ;
void FixupTexture ( const void * pData , int nSize , LoaderError_t loaderError ) ;
void SwapContents ( ITexture * pOther ) ;
protected :
// private data, generally from VTF resource extensions
struct DataChunk
{
void Allocate ( unsigned int numBytes )
{
m_pvData = new unsigned char [ numBytes ] ;
m_numBytes = numBytes ;
}
void Deallocate ( ) const { delete [ ] m_pvData ; }
unsigned int m_eType ;
unsigned int m_numBytes ;
unsigned char * m_pvData ;
} ;
CUtlVector < DataChunk > m_arrDataChunks ;
struct ScratchVTF
{
ScratchVTF ( CTexture * _tex ) : m_pParent ( _tex ) , m_pScratchVTF ( _tex - > GetScratchVTFTexture ( ) ) { }
~ ScratchVTF ( )
{
if ( m_pScratchVTF )
m_pParent - > ReleaseScratchVTFTexture ( m_pScratchVTF ) ;
m_pScratchVTF = NULL ;
}
IVTFTexture * Get ( ) const { return m_pScratchVTF ; }
void TakeOwnership ( ) { m_pScratchVTF = NULL ; }
CTexture * m_pParent ;
IVTFTexture * m_pScratchVTF ;
} ;
friend class CTextureStreamingJob ;
} ;
class CTextureStreamingJob : public IAsyncTextureOperationReceiver
{
public :
CTextureStreamingJob ( CTexture * pTex ) : m_referenceCount ( 0 ) , m_pOwner ( pTex ) { Assert ( m_pOwner ! = NULL ) ; m_pOwner - > AddRef ( ) ; }
virtual ~ CTextureStreamingJob ( ) { SafeRelease ( & m_pOwner ) ; }
virtual int AddRef ( ) OVERRIDE { return + + m_referenceCount ; }
virtual int Release ( ) OVERRIDE { int retVal = - - m_referenceCount ; Assert ( retVal > = 0 ) ; if ( retVal = = 0 ) { delete this ; } return retVal ; }
virtual int GetRefCount ( ) const OVERRIDE { return m_referenceCount ; }
virtual void OnAsyncCreateComplete ( ITexture * pTex , void * pExtraArgs ) OVERRIDE { Assert ( ! " unimpl " ) ; }
virtual void OnAsyncFindComplete ( ITexture * pTex , void * pExtraArgs ) OVERRIDE ;
virtual void OnAsyncMapComplete ( ITexture * pTex , void * pExtraArgs , void * pMemory , int nPitch ) { Assert ( ! " unimpl " ) ; }
virtual void OnAsyncReadbackBegin ( ITexture * pDst , ITexture * pSrc , void * pExtraArgs ) OVERRIDE { Assert ( ! " unimpl " ) ; }
void ForgetOwner ( ITextureInternal * pTex ) { Assert ( pTex = = m_pOwner ) ; SafeRelease ( & m_pOwner ) ; }
private :
CInterlockedInt m_referenceCount ;
CTexture * m_pOwner ;
} ;
//////////////////////////////////////////////////////////////////////////
//
// CReferenceToHandleTexture is a special implementation of ITexture
// to be used solely for binding the texture handle when rendering.
// It is used when a D3D texture handle is available, but should be used
// at a higher level of abstraction requiring an ITexture or ITextureInternal.
//
//////////////////////////////////////////////////////////////////////////
class CReferenceToHandleTexture : public ITextureInternal
{
public :
CReferenceToHandleTexture ( ) ;
virtual ~ CReferenceToHandleTexture ( ) ;
virtual const char * GetName ( void ) const { return m_Name . String ( ) ; }
const char * GetTextureGroupName ( void ) const { return m_TextureGroupName . String ( ) ; }
// Stats about the texture itself
virtual ImageFormat GetImageFormat ( ) const { return IMAGE_FORMAT_UNKNOWN ; }
virtual NormalDecodeMode_t GetNormalDecodeMode ( ) const { return NORMAL_DECODE_NONE ; }
virtual int GetMappingWidth ( ) const { return 1 ; }
virtual int GetMappingHeight ( ) const { return 1 ; }
virtual int GetActualWidth ( ) const { return 1 ; }
virtual int GetActualHeight ( ) const { return 1 ; }
virtual int GetNumAnimationFrames ( ) const { return 1 ; }
virtual bool IsTranslucent ( ) const { return false ; }
virtual void GetReflectivity ( Vector & reflectivity ) { reflectivity . Zero ( ) ; }
// Reference counting
virtual void IncrementReferenceCount ( ) { + + m_nRefCount ; }
virtual void DecrementReferenceCount ( ) { - - m_nRefCount ; }
virtual int GetReferenceCount ( ) { return m_nRefCount ; }
// Used to modify the texture bits (procedural textures only)
virtual void SetTextureRegenerator ( ITextureRegenerator * pTextureRegen ) { NULL ; }
// Little helper polling methods
virtual bool IsNormalMap ( ) const { return false ; }
virtual bool IsCubeMap ( void ) const { return false ; }
virtual bool IsRenderTarget ( ) const { return false ; }
virtual bool IsTempRenderTarget ( void ) const { return false ; }
virtual bool IsProcedural ( ) const { return true ; }
virtual bool IsMipmapped ( ) const { return false ; }
virtual bool IsError ( ) const { return false ; }
// For volume textures
virtual bool IsVolumeTexture ( ) const { return false ; }
virtual int GetMappingDepth ( ) const { return 1 ; }
virtual int GetActualDepth ( ) const { return 1 ; }
// Releases the texture's hw memory
void ReleaseMemory ( ) { NULL ; }
virtual void OnRestore ( ) { NULL ; }
// Sets the filtering modes on the texture we're modifying
void SetFilteringAndClampingMode ( bool bOnlyLodValues = false ) { NULL ; }
void Download ( Rect_t * pRect = NULL , int nAdditionalCreationFlags = 0 ) { NULL ; }
// Loads up information about the texture
virtual void Precache ( ) { NULL ; }
// FIXME: Bogus methods... can we please delete these?
virtual void GetLowResColorSample ( float s , float t , float * color ) const { NULL ; }
// Gets texture resource data of the specified type.
// Params:
// eDataType type of resource to retrieve.
// pnumBytes on return is the number of bytes available in the read-only data buffer or is undefined
// Returns:
// pointer to the resource data, or NULL. Note that the data from this pointer can disappear when
// the texture goes away - you want to copy this data!
virtual void * GetResourceData ( uint32 eDataType , size_t * pNumBytes ) const { return NULL ; }
virtual int GetApproximateVidMemBytes ( void ) const { return 32 ; }
// Stretch blit the framebuffer into this texture.
virtual void CopyFrameBufferToMe ( int nRenderTargetID = 0 , Rect_t * pSrcRect = NULL , Rect_t * pDstRect = NULL ) { NULL ; }
virtual void CopyMeToFrameBuffer ( int nRenderTargetID = 0 , Rect_t * pSrcRect = NULL , Rect_t * pDstRect = NULL ) { NULL ; }
virtual ITexture * GetEmbeddedTexture ( int nIndex ) { return ( nIndex = = 0 ) ? this : NULL ; }
// Get the shaderapi texture handle associated w/ a particular frame
virtual ShaderAPITextureHandle_t GetTextureHandle ( int nFrame , int nTextureChannel = 0 ) { return m_hTexture ; }
// Bind the texture
virtual void Bind ( Sampler_t sampler ) ;
virtual void Bind ( Sampler_t sampler1 , int nFrame , Sampler_t sampler2 = ( Sampler_t ) - 1 ) ;
virtual void BindVertexTexture ( VertexTextureSampler_t stage , int nFrame ) ;
// Set this texture as a render target
bool SetRenderTarget ( int nRenderTargetID ) { return SetRenderTarget ( nRenderTargetID , NULL ) ; }
// Set this texture as a render target (optionally set depth texture as depth buffer as well)
bool SetRenderTarget ( int nRenderTargetID , ITexture * pDepthTexture ) { return false ; }
virtual void MarkAsPreloaded ( bool bSet ) { NULL ; }
virtual bool IsPreloaded ( ) const { return true ; }
virtual void MarkAsExcluded ( bool bSet , int nDimensionsLimit ) { NULL ; }
virtual bool UpdateExcludedState ( void ) { return true ; }
// Retrieve the vtf flags mask
virtual unsigned int GetFlags ( void ) const { return 0 ; }
virtual void ForceLODOverride ( int iNumLodsOverrideUpOrDown ) { NULL ; }
virtual void ReloadFilesInList ( IFileList * pFilesToReload ) { }
// Save texture to a file.
virtual bool SaveToFile ( const char * fileName ) { return false ; }
virtual bool AsyncReadTextureFromFile ( IVTFTexture * pVTFTexture , unsigned int nAdditionalCreationFlags ) { Assert ( ! " Should never get here. " ) ; return false ; }
virtual void AsyncCancelReadTexture ( ) { Assert ( ! " Should never get here. " ) ; }
virtual void CopyToStagingTexture ( ITexture * pDstTex ) { Assert ( ! " Should never get here. " ) ; } ;
// Map and unmap. These can fail. And can cause a very significant perf penalty. Be very careful with them.
virtual void Map ( void * * pOutBits , int * pOutPitch ) { }
virtual void Unmap ( ) { }
virtual ResidencyType_t GetCurrentResidence ( ) const { return RESIDENT_FULL ; }
virtual ResidencyType_t GetTargetResidence ( ) const { return RESIDENT_FULL ; }
virtual bool MakeResident ( ResidencyType_t newResidence ) { Assert ( ! " Unimpl " ) ; return true ; }
virtual void UpdateLodBias ( ) { }
virtual void SetErrorTexture ( bool isErrorTexture ) { }
protected :
# ifdef _DEBUG
char * m_pDebugName ;
# endif
CUtlSymbol m_Name ;
// What texture group this texture is in (winds up setting counters based on the group name,
// then the budget panel views the counters).
CUtlSymbol m_TextureGroupName ;
// The set of texture ids for each animation frame
ShaderAPITextureHandle_t m_hTexture ;
// Refcount
int m_nRefCount ;
public :
virtual void DeleteIfUnreferenced ( ) ;
void FixupTexture ( const void * pData , int nSize , LoaderError_t loaderError ) { NULL ; }
void SwapContents ( ITexture * pOther ) { NULL ; }
public :
void SetName ( char const * szName ) ;
void InitFromHandle (
const char * pTextureName ,
const char * pTextureGroupName ,
ShaderAPITextureHandle_t hTexture ) ;
} ;
CReferenceToHandleTexture : : CReferenceToHandleTexture ( ) :
m_hTexture ( INVALID_SHADERAPI_TEXTURE_HANDLE ) ,
# ifdef _DEBUG
m_pDebugName ( NULL ) ,
# endif
m_nRefCount ( 0 )
{
NULL ;
}
CReferenceToHandleTexture : : ~ CReferenceToHandleTexture ( )
{
# ifdef _DEBUG
if ( m_nRefCount ! = 0 )
{
Warning ( " Reference Count(%d) != 0 in ~CReferenceToHandleTexture for texture \" %s \" \n " , m_nRefCount , m_Name . String ( ) ) ;
}
if ( m_pDebugName )
{
delete [ ] m_pDebugName ;
}
# endif
}
void CReferenceToHandleTexture : : SetName ( char const * szName )
{
// normalize and convert to a symbol
char szCleanName [ MAX_PATH ] ;
m_Name = NormalizeTextureName ( szName , szCleanName , sizeof ( szCleanName ) ) ;
# ifdef _DEBUG
if ( m_pDebugName )
{
delete [ ] m_pDebugName ;
}
int nLen = V_strlen ( szCleanName ) + 1 ;
m_pDebugName = new char [ nLen ] ;
V_memcpy ( m_pDebugName , szCleanName , nLen ) ;
# endif
}
void CReferenceToHandleTexture : : InitFromHandle ( const char * pTextureName , const char * pTextureGroupName , ShaderAPITextureHandle_t hTexture )
{
SetName ( pTextureName ) ;
m_TextureGroupName = pTextureGroupName ;
m_hTexture = hTexture ;
}
void CReferenceToHandleTexture : : Bind ( Sampler_t sampler )
{
if ( g_pShaderDevice - > IsUsingGraphics ( ) )
{
g_pShaderAPI - > BindTexture ( sampler , m_hTexture ) ;
}
}
// TODO: make paired textures work with mat_texture_list
void CReferenceToHandleTexture : : Bind ( Sampler_t sampler1 , int nFrame , Sampler_t sampler2 /* = -1 */ )
{
if ( g_pShaderDevice - > IsUsingGraphics ( ) )
{
g_pShaderAPI - > BindTexture ( sampler1 , m_hTexture ) ;
}
}
void CReferenceToHandleTexture : : BindVertexTexture ( VertexTextureSampler_t sampler , int nFrame )
{
if ( g_pShaderDevice - > IsUsingGraphics ( ) )
{
g_pShaderAPI - > BindVertexTexture ( sampler , m_hTexture ) ;
}
}
void CReferenceToHandleTexture : : DeleteIfUnreferenced ( )
{
if ( m_nRefCount > 0 )
return ;
TextureManager ( ) - > RemoveTexture ( this ) ;
}
//-----------------------------------------------------------------------------
// Fixed-size allocator
//-----------------------------------------------------------------------------
//DEFINE_FIXEDSIZE_ALLOCATOR( CTexture, 1024, true );
//-----------------------------------------------------------------------------
// Static instance of VTF texture
//-----------------------------------------------------------------------------
# define MAX_RENDER_THREADS 4
// For safety's sake, we allow any of the threads that intersect with rendering
// to have their own state vars. In practice, we expect only the matqueue thread
// and the main thread to ever hit s_pVTFTexture.
static IVTFTexture * s_pVTFTexture [ MAX_RENDER_THREADS ] = { NULL } ;
// We only expect that the main thread or the matqueue thread to actually touch
// these, but we still need a NULL and size of 0 for the other threads.
static void * s_pOptimalReadBuffer [ MAX_RENDER_THREADS ] = { NULL } ;
static int s_nOptimalReadBufferSize [ MAX_RENDER_THREADS ] = { 0 } ;
//-----------------------------------------------------------------------------
// Class factory methods
//-----------------------------------------------------------------------------
ITextureInternal * ITextureInternal : : CreateFileTexture ( const char * pFileName , const char * pTextureGroupName )
{
CTexture * pTex = new CTexture ;
pTex - > InitFileTexture ( pFileName , pTextureGroupName ) ;
return pTex ;
}
ITextureInternal * ITextureInternal : : CreateReferenceTextureFromHandle (
const char * pTextureName ,
const char * pTextureGroupName ,
ShaderAPITextureHandle_t hTexture )
{
CReferenceToHandleTexture * pTex = new CReferenceToHandleTexture ;
pTex - > InitFromHandle ( pTextureName , pTextureGroupName , hTexture ) ;
return pTex ;
}
ITextureInternal * ITextureInternal : : CreateProceduralTexture (
const char * pTextureName ,
const char * pTextureGroupName ,
int w ,
int h ,
int d ,
ImageFormat fmt ,
int nFlags ,
ITextureRegenerator * generator )
{
CTexture * pTex = new CTexture ;
pTex - > InitProceduralTexture ( pTextureName , pTextureGroupName , w , h , d , fmt , nFlags , generator ) ;
pTex - > IncrementReferenceCount ( ) ;
return pTex ;
}
// GR - named RT
ITextureInternal * ITextureInternal : : CreateRenderTarget (
const char * pRTName ,
int w ,
int h ,
RenderTargetSizeMode_t sizeMode ,
ImageFormat fmt ,
RenderTargetType_t type ,
unsigned int textureFlags ,
unsigned int renderTargetFlags )
{
CTexture * pTex = new CTexture ;
pTex - > InitRenderTarget ( pRTName , w , h , sizeMode , fmt , type , textureFlags , renderTargetFlags ) ;
return pTex ;
}
//-----------------------------------------------------------------------------
// Rebuild and exisiting render target in place.
//-----------------------------------------------------------------------------
void ITextureInternal : : ChangeRenderTarget (
ITextureInternal * pTex ,
int w ,
int h ,
RenderTargetSizeMode_t sizeMode ,
ImageFormat fmt ,
RenderTargetType_t type ,
unsigned int textureFlags ,
unsigned int renderTargetFlags )
{
pTex - > ReleaseMemory ( ) ;
dynamic_cast < CTexture * > ( pTex ) - > InitRenderTarget ( pTex - > GetName ( ) , w , h , sizeMode , fmt , type , textureFlags , renderTargetFlags ) ;
}
void ITextureInternal : : Destroy ( ITextureInternal * pTex , bool bSkipTexMgrCheck )
{
# ifdef STAGING_ONLY
if ( ! bSkipTexMgrCheck & & TextureManager ( ) - > HasPendingTextureDestroys ( ) )
{
// Multithreading badness. This will cause a crash later! Grab JohnS or McJohn know!
DebuggerBreakIfDebugging_StagingOnly ( ) ;
}
# endif
int iIndex = g_pTextureRefList - > Find ( static_cast < ITexture * > ( pTex ) ) ;
if ( iIndex ! = g_pTextureRefList - > InvalidIndex ( ) )
{
if ( g_pTextureRefList - > Element ( iIndex ) ! = 0 )
{
int currentCount = g_pTextureRefList - > Element ( iIndex ) ;
Warning ( " Destroying a texture that is in the queue: %s (%p): %d! \n " , pTex - > GetName ( ) , pTex , currentCount ) ;
}
}
delete pTex ;
}
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CTexture : : CTexture ( ) : m_ImageFormat ( IMAGE_FORMAT_UNKNOWN )
{
m_dimsActual . m_nMipCount = 0 ;
m_dimsMapping . m_nWidth = 0 ;
m_dimsMapping . m_nHeight = 0 ;
m_dimsMapping . m_nDepth = 1 ;
m_dimsActual . m_nWidth = 0 ;
m_dimsActual . m_nHeight = 0 ;
m_dimsActual . m_nDepth = 1 ;
m_dimsAllocated . m_nWidth = 0 ;
m_dimsAllocated . m_nHeight = 0 ;
m_dimsAllocated . m_nDepth = 0 ;
m_dimsAllocated . m_nMipCount = 0 ;
m_nStreamingMips = 0 ;
m_nRefCount = 0 ;
m_nFlags = 0 ;
m_nInternalFlags = 0 ;
m_pTextureHandles = NULL ;
m_nFrameCount = 0 ;
VectorClear ( m_vecReflectivity ) ;
m_pTextureRegenerator = NULL ;
m_nOriginalRenderTargetType = NO_RENDER_TARGET ;
m_RenderTargetSizeMode = RT_SIZE_NO_CHANGE ;
m_nOriginalRTWidth = m_nOriginalRTHeight = 1 ;
m_LowResImageWidth = 0 ;
m_LowResImageHeight = 0 ;
m_pLowResImage = NULL ;
m_pStreamingJob = NULL ;
m_residenceTarget = RESIDENT_NONE ;
m_residenceCurrent = RESIDENT_NONE ;
m_lodClamp = 0 ;
m_lodBiasInitial = 0 ;
m_lodBiasCurrent = 0 ;
m_nDesiredDimensionLimit = 0 ;
m_nActualDimensionLimit = 0 ;
memset ( & m_cachedFileLodSettings , 0 , sizeof ( m_cachedFileLodSettings ) ) ;
# ifdef _DEBUG
m_pDebugName = NULL ;
# endif
m_pStreamingVTF = NULL ;
m_bStreamingFileReadFailed = false ;
}
CTexture : : ~ CTexture ( )
{
# ifdef _DEBUG
if ( m_nRefCount ! = 0 )
{
Warning ( " Reference Count(%d) != 0 in ~CTexture for texture \" %s \" \n " , ( int ) m_nRefCount , m_Name . String ( ) ) ;
}
# endif
Shutdown ( ) ;
# ifdef _DEBUG
if ( m_pDebugName )
{
// delete[] m_pDebugName;
}
# endif
// Deliberately stomp our VTable so that we can detect cases where code tries to access freed materials.
int * p = ( int * ) this ;
* p = 0xdeadbeef ;
}
//-----------------------------------------------------------------------------
// Initializes the texture
//-----------------------------------------------------------------------------
void CTexture : : Init ( int w , int h , int d , ImageFormat fmt , int iFlags , int iFrameCount )
{
Assert ( iFrameCount > 0 ) ;
// This is necessary to prevent blowing away the allocated state,
// which is necessary for the ReleaseTextureHandles call below to work.
SetErrorTexture ( false ) ;
// free and release previous data
// cannot change to new intialization parameters yet
FreeShaderAPITextures ( ) ;
ReleaseTextureHandles ( ) ;
// update to new initialization parameters
// these are the *desired* new values
m_dimsMapping . m_nWidth = w ;
m_dimsMapping . m_nHeight = h ;
m_dimsMapping . m_nDepth = d ;
m_ImageFormat = fmt ;
m_nFrameCount = iFrameCount ;
// We don't know the actual width and height until we get it ready to render
m_dimsActual . m_nWidth = m_dimsActual . m_nHeight = 0 ;
m_dimsActual . m_nDepth = 1 ;
m_dimsActual . m_nMipCount = 0 ;
m_dimsAllocated . m_nWidth = 0 ;
m_dimsAllocated . m_nHeight = 0 ;
m_dimsAllocated . m_nDepth = 0 ;
m_dimsAllocated . m_nMipCount = 0 ;
m_nStreamingMips = 0 ;
// Clear the m_nFlags bit. If we don't, then m_nFrameCount may end up being 1, and
// TEXTUREFLAGS_DEPTHRENDERTARGET could be set.
m_nFlags & = ~ TEXTUREFLAGS_DEPTHRENDERTARGET ;
m_nFlags | = iFlags ;
CancelStreamingJob ( false ) ;
m_residenceTarget = RESIDENT_NONE ;
m_residenceCurrent = RESIDENT_NONE ;
m_lodClamp = 0 ;
m_lodBiasInitial = 0 ;
m_lodBiasCurrent = 0 ;
AllocateTextureHandles ( ) ;
}
//-----------------------------------------------------------------------------
// Shuts down the texture
//-----------------------------------------------------------------------------
void CTexture : : Shutdown ( )
{
Assert ( m_pStreamingVTF = = NULL ) ;
// Clean up the low-res texture
delete [ ] m_pLowResImage ;
m_pLowResImage = 0 ;
// Clean up the resources data
for ( DataChunk const * pDataChunk = m_arrDataChunks . Base ( ) ,
* pDataChunkEnd = pDataChunk + m_arrDataChunks . Count ( ) ;
pDataChunk < pDataChunkEnd ; + + pDataChunk )
{
pDataChunk - > Deallocate ( ) ;
}
m_arrDataChunks . RemoveAll ( ) ;
// Frees the texture regen class
if ( m_pTextureRegenerator )
{
m_pTextureRegenerator - > Release ( ) ;
m_pTextureRegenerator = NULL ;
}
CancelStreamingJob ( false ) ;
m_residenceTarget = RESIDENT_NONE ;
m_residenceCurrent = RESIDENT_NONE ;
m_lodClamp = 0 ;
m_lodBiasInitial = 0 ;
m_lodBiasCurrent = 0 ;
// This deletes the textures
FreeShaderAPITextures ( ) ;
ReleaseTextureHandles ( ) ;
NotifyUnloadedFile ( ) ;
}
void CTexture : : ReleaseMemory ( )
{
FreeShaderAPITextures ( ) ;
NotifyUnloadedFile ( ) ;
}
IVTFTexture * CTexture : : GetScratchVTFTexture ( )
{
const bool cbThreadInMatQueue = ( MaterialSystem ( ) - > GetRenderThreadId ( ) = = ThreadGetCurrentId ( ) ) ; cbThreadInMatQueue ;
Assert ( cbThreadInMatQueue | | ThreadInMainThread ( ) ) ;
const int ti = GetThreadId ( ) ;
if ( ! s_pVTFTexture [ ti ] )
s_pVTFTexture [ ti ] = CreateVTFTexture ( ) ;
return s_pVTFTexture [ ti ] ;
}
void CTexture : : ReleaseScratchVTFTexture ( IVTFTexture * tex )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
const bool cbThreadInMatQueue = ( MaterialSystem ( ) - > GetRenderThreadId ( ) = = ThreadGetCurrentId ( ) ) ; cbThreadInMatQueue ;
Assert ( cbThreadInMatQueue | | ThreadInMainThread ( ) ) ;
Assert ( m_pStreamingVTF = = NULL | | ThreadInMainThread ( ) ) ; // Can only manipulate m_pStreamingVTF to release safely in main thread.
if ( m_pStreamingVTF )
{
Assert ( tex = = m_pStreamingVTF ) ;
TextureManager ( ) - > ReleaseAsyncScratchVTF ( m_pStreamingVTF ) ;
m_pStreamingVTF = NULL ;
return ;
}
// Normal scratch main-thread vtf doesn't need to do anything.
}
//-----------------------------------------------------------------------------
//
// Various initialization methods
//
//-----------------------------------------------------------------------------
void CTexture : : ApplyRenderTargetSizeMode ( int & width , int & height , ImageFormat fmt )
{
width = m_nOriginalRTWidth ;
height = m_nOriginalRTHeight ;
switch ( m_RenderTargetSizeMode )
{
case RT_SIZE_FULL_FRAME_BUFFER :
{
MaterialSystem ( ) - > GetRenderTargetFrameBufferDimensions ( width , height ) ;
if ( ! HardwareConfig ( ) - > SupportsNonPow2Textures ( ) )
{
width = FloorPow2 ( width + 1 ) ;
height = FloorPow2 ( height + 1 ) ;
}
}
break ;
case RT_SIZE_FULL_FRAME_BUFFER_ROUNDED_UP :
{
MaterialSystem ( ) - > GetRenderTargetFrameBufferDimensions ( width , height ) ;
if ( ! HardwareConfig ( ) - > SupportsNonPow2Textures ( ) )
{
width = CeilPow2 ( width ) ;
height = CeilPow2 ( height ) ;
}
}
break ;
case RT_SIZE_PICMIP :
{
int fbWidth , fbHeight ;
MaterialSystem ( ) - > GetRenderTargetFrameBufferDimensions ( fbWidth , fbHeight ) ;
int picmip = g_config . skipMipLevels ;
while ( picmip > 0 )
{
width > > = 1 ;
height > > = 1 ;
picmip - - ;
}
while ( width > fbWidth )
{
width > > = 1 ;
}
while ( height > fbHeight )
{
height > > = 1 ;
}
}
break ;
case RT_SIZE_DEFAULT :
{
// Assume that the input is pow2.
Assert ( ( width & ( width - 1 ) ) = = 0 ) ;
Assert ( ( height & ( height - 1 ) ) = = 0 ) ;
int fbWidth , fbHeight ;
MaterialSystem ( ) - > GetRenderTargetFrameBufferDimensions ( fbWidth , fbHeight ) ;
while ( width > fbWidth )
{
width > > = 1 ;
}
while ( height > fbHeight )
{
height > > = 1 ;
}
}
break ;
case RT_SIZE_HDR :
{
MaterialSystem ( ) - > GetRenderTargetFrameBufferDimensions ( width , height ) ;
width = width / 4 ;
height = height / 4 ;
}
break ;
case RT_SIZE_OFFSCREEN :
{
int fbWidth , fbHeight ;
MaterialSystem ( ) - > GetRenderTargetFrameBufferDimensions ( fbWidth , fbHeight ) ;
// Shrink the buffer if it's bigger than back buffer. Otherwise, don't mess with it.
while ( ( width > fbWidth ) | | ( height > fbHeight ) )
{
width > > = 1 ;
height > > = 1 ;
}
}
break ;
case RT_SIZE_LITERAL :
{
// Literal means literally don't mess with the dimensions. Unlike what OFFSCREEN does,
// which is totally to mess with the dimensions.
}
break ;
case RT_SIZE_LITERAL_PICMIP :
{
// Don't do anything here, like literal. Later, we will pay attention to picmip settings s.t.
// these render targets look like other textures wrt Mapping Dimensions vs Actual Dimensions.
}
break ;
case RT_SIZE_REPLAY_SCREENSHOT :
{
// Compute all possible resolutions if first time we're running this function
static bool bReplayInit = false ;
static int m_aScreenshotWidths [ 3 ] [ 2 ] ;
static ConVarRef replay_screenshotresolution ( " replay_screenshotresolution " ) ;
if ( ! bReplayInit )
{
bReplayInit = true ;
for ( int iAspect = 0 ; iAspect < 3 ; + + iAspect )
{
for ( int iRes = 0 ; iRes < 2 ; + + iRes )
{
int nWidth = ( int ) FastPow2 ( 9 + iRes ) ;
m_aScreenshotWidths [ iAspect ] [ iRes ] = nWidth ;
}
}
}
// Get dimensions for unpadded image
int nUnpaddedWidth , nUnpaddedHeight ;
// Figure out the proper screenshot size to use based on the aspect ratio
int nScreenWidth , nScreenHeight ;
MaterialSystem ( ) - > GetRenderTargetFrameBufferDimensions ( nScreenWidth , nScreenHeight ) ;
float flAspectRatio = ( float ) nScreenWidth / nScreenHeight ;
// Get the screenshot res
int iRes = clamp ( replay_screenshotresolution . GetInt ( ) , 0 , 1 ) ;
int iAspect ;
if ( flAspectRatio = = 16.0f / 9 )
{
iAspect = 0 ;
}
else if ( flAspectRatio = = 16.0f / 10 )
{
iAspect = 1 ;
}
else
{
iAspect = 2 ; // 4:3
}
static float s_flInvAspectRatios [ 3 ] = { 9.0f / 16.0f , 10.0f / 16 , 3.0f / 4 } ;
nUnpaddedWidth = min ( nScreenWidth , m_aScreenshotWidths [ iAspect ] [ iRes ] ) ;
nUnpaddedHeight = m_aScreenshotWidths [ iAspect ] [ iRes ] * s_flInvAspectRatios [ iAspect ] ;
// Get dimensions for padded image based on unpadded size - must be power of 2 for a material/texture
width = SmallestPowerOfTwoGreaterOrEqual ( nUnpaddedWidth ) ;
height = SmallestPowerOfTwoGreaterOrEqual ( nUnpaddedHeight ) ;
}
break ;
default :
{
if ( ! HushAsserts ( ) )
{
Assert ( m_RenderTargetSizeMode = = RT_SIZE_NO_CHANGE ) ;
Assert ( m_nOriginalRenderTargetType = = RENDER_TARGET_NO_DEPTH ) ; // Only can use NO_CHANGE if we don't have a depth buffer.
}
}
break ;
}
}
void CTexture : : CopyToStagingTexture ( ITexture * pDstTex )
{
Assert ( pDstTex ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// Need to flush any commands in flight on our side of things
materials - > Flush ( false ) ;
CTexture * pDstTexActual = assert_cast < CTexture * > ( pDstTex ) ;
// Then do the copy if everything is on the up and up.
if ( ( m_pTextureHandles = = NULL | | m_nFrameCount = = 0 ) | | ( pDstTexActual - > m_pTextureHandles = = NULL | | pDstTexActual - > m_nFrameCount = = 0 ) )
{
Assert ( ! " Can't copy to a non-existent texture, may need to generate or something. " ) ;
return ;
}
// Make sure we've actually got the right surface types.
Assert ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) ;
Assert ( pDstTex - > GetFlags ( ) & TEXTUREFLAGS_STAGING_MEMORY ) ;
g_pShaderAPI - > CopyRenderTargetToScratchTexture ( m_pTextureHandles [ 0 ] , pDstTexActual - > m_pTextureHandles [ 0 ] ) ;
}
void CTexture : : Map ( void * * pOutBits , int * pOutPitch )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// Must be a staging texture to avoid catastrophic perf fail.
Assert ( m_nFlags & TEXTUREFLAGS_STAGING_MEMORY ) ;
if ( m_pTextureHandles = = NULL | | m_nFrameCount = = 0 )
{
Assert ( ! " Can't map a non-existent texture, may need to generate or something. " ) ;
return ;
}
g_pShaderAPI - > LockRect ( pOutBits , pOutPitch , m_pTextureHandles [ 0 ] , 0 , 0 , 0 , GetActualWidth ( ) , GetActualHeight ( ) , false , true ) ;
}
void CTexture : : Unmap ( )
{
if ( m_pTextureHandles = = NULL | | m_nFrameCount = = 0 )
{
Assert ( ! " Can't unmap a non-existent texture, may need to generate or something. " ) ;
return ;
}
g_pShaderAPI - > UnlockRect ( m_pTextureHandles [ 0 ] , 0 ) ;
}
bool CTexture : : MakeResident ( ResidencyType_t newResidence )
{
Assert ( ( GetFlags ( ) & TEXTUREFLAGS_STREAMABLE ) ! = 0 ) ;
// If we already think we're supposed to go here, nothing to do and we should report success.
if ( m_residenceTarget = = newResidence )
return true ;
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
// What are we moving towards?
switch ( newResidence )
{
case RESIDENT_NONE :
MakeNonResident ( ) ;
return true ;
case RESIDENT_PARTIAL :
MakePartiallyResident ( ) ;
return true ;
case RESIDENT_FULL :
return MakeFullyResident ( ) ;
default :
Assert ( ! " Missing switch statement " ) ;
} ;
return false ;
}
void CTexture : : UpdateLodBias ( )
{
if ( m_lodBiasInitial = = 0.0f )
return ;
// Only perform adjustment once per frame.
if ( m_lastLodBiasAdjustFrame = = g_FrameNum )
return ;
bool bPopIn = mat_lodin_time . GetFloat ( ) = = 0 ;
if ( bPopIn & & m_lodBiasInitial = = 0.0f )
return ;
if ( ! bPopIn )
m_lodBiasCurrent = m_lodBiasInitial - ( Plat_FloatTime ( ) - m_lodBiasStartTime ) / mat_lodin_time . GetFloat ( ) * m_lodBiasInitial ;
else
m_lodBiasCurrent = m_lodBiasInitial = 0.0f ;
// If we're supposed to pop in when the object isn't visible and we have the opportunity...
if ( mat_lodin_hidden_pop . GetBool ( ) & & m_lastLodBiasAdjustFrame ! = g_FrameNum - 1 )
m_lodBiasCurrent = m_lodBiasInitial = 0.0f ;
if ( m_lodBiasCurrent < = 0.0f )
{
m_lodBiasCurrent = m_lodBiasInitial = 0.0f ;
m_lodBiasStartTime = 0 ;
}
m_lastLodBiasAdjustFrame = g_FrameNum ;
SetFilteringAndClampingMode ( true ) ;
}
void CTexture : : MakeNonResident ( )
{
if ( m_residenceCurrent ! = RESIDENT_NONE )
Shutdown ( ) ;
m_residenceCurrent = m_residenceTarget = RESIDENT_NONE ;
// Clear our the streamable fine flag to ensure we reload properly.
m_nFlags & = ~ TEXTUREFLAGS_STREAMABLE_FINE ;
}
void CTexture : : MakePartiallyResident ( )
{
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
ResidencyType_t oldCurrentResidence = m_residenceCurrent ;
ResidencyType_t oldTargetResidence = m_residenceTarget ;
m_residenceCurrent = m_residenceTarget = RESIDENT_PARTIAL ;
if ( oldCurrentResidence = = RESIDENT_PARTIAL )
{
Assert ( oldTargetResidence = = RESIDENT_FULL ) ; oldTargetResidence ;
// If we are already partially resident, then just cancel our job to stream in,
// cause we don't need that data anymore.
CancelStreamingJob ( ) ;
return ;
}
Assert ( oldCurrentResidence = = RESIDENT_FULL ) ;
// Clear the fine bit.
m_nFlags & = ~ TEXTUREFLAGS_STREAMABLE_FINE ;
if ( HardwareConfig ( ) - > CanStretchRectFromTextures ( ) )
{
m_lodClamp = 0 ;
m_lodBiasInitial = m_lodBiasCurrent = 0 ;
m_lastLodBiasAdjustFrame = g_FrameNum ;
DownloadTexture ( NULL , true ) ;
}
else
{
// Oops. We were overzealous above--restore the residency to what it was.
m_residenceCurrent = oldCurrentResidence ;
// Immediately display it as lower res (for consistency) but if we can't (efficiently)
// copy we just have to re-read everything from disk. Lame!
m_lodClamp = 3 ;
m_lodBiasInitial = m_lodBiasCurrent = 0 ;
m_lastLodBiasAdjustFrame = g_FrameNum ;
SetFilteringAndClampingMode ( true ) ;
SafeAssign ( & m_pStreamingJob , new CTextureStreamingJob ( this ) ) ;
MaterialSystem ( ) - > AsyncFindTexture ( GetName ( ) , GetTextureGroupName ( ) , m_pStreamingJob , ( void * ) RESIDENT_PARTIAL , false , TEXTUREFLAGS_STREAMABLE_COARSE ) ;
}
}
bool CTexture : : MakeFullyResident ( )
{
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
ResidencyType_t oldCurrentResidence = m_residenceCurrent ;
ResidencyType_t oldTargetResidence = m_residenceTarget ;
if ( oldCurrentResidence = = RESIDENT_FULL )
{
// This isn't a requirement, but right now it would be a mistake
Assert ( ! HardwareConfig ( ) - > CanStretchRectFromTextures ( ) ) ;
Assert ( oldTargetResidence = = RESIDENT_PARTIAL ) ; oldTargetResidence ;
m_residenceCurrent = m_residenceTarget = RESIDENT_FULL ;
m_lodClamp = 0 ;
m_lodBiasInitial = m_lodBiasCurrent = 0 ;
m_lastLodBiasAdjustFrame = g_FrameNum ;
SetFilteringAndClampingMode ( true ) ;
CancelStreamingJob ( ) ;
return true ;
}
Assert ( m_residenceTarget = = RESIDENT_PARTIAL & & m_residenceCurrent = = RESIDENT_PARTIAL ) ;
Assert ( m_pStreamingJob = = NULL ) ;
SafeAssign ( & m_pStreamingJob , new CTextureStreamingJob ( this ) ) ;
MaterialSystem ( ) - > AsyncFindTexture ( GetName ( ) , GetTextureGroupName ( ) , m_pStreamingJob , ( void * ) RESIDENT_FULL , false , TEXTUREFLAGS_STREAMABLE_FINE ) ;
m_residenceTarget = RESIDENT_FULL ;
return true ;
}
void CTexture : : CancelStreamingJob ( bool bJobMustExist )
{
bJobMustExist ; // Only used by asserts ensuring correctness, so reference it for release builds.
// Most callers should be aware of whether the job exists, but for cleanup we don't know and we
// should be safe in that case.
Assert ( ! bJobMustExist | | m_pStreamingJob ) ;
if ( ! m_pStreamingJob )
return ;
// The streaming job and this (this texture) have a circular reference count--each one holds one for the other.
// As a result, this means that having the streaming job forget about the texture may cause the texture to go
// away completely! So we need to ensure that after we call "ForgetOwner" that we don't touch any instance
// variables.
CTextureStreamingJob * pJob = m_pStreamingJob ;
m_pStreamingJob = NULL ;
pJob - > ForgetOwner ( this ) ;
SafeRelease ( & pJob ) ;
}
void CTexture : : OnStreamingJobComplete ( ResidencyType_t newResidenceCurrent )
{
Assert ( m_pStreamingJob ) ;
// It's probable that if this assert fires, we should just do nothing in here and return--but I'd
// like to see that happen to be sure.
Assert ( newResidenceCurrent = = m_residenceTarget ) ;
m_residenceCurrent = newResidenceCurrent ;
// Only do lod biasing for stream in. For stream out, just dump to lowest quality right away.
if ( m_residenceCurrent = = RESIDENT_FULL )
{
if ( mat_lodin_time . GetFloat ( ) > 0 )
{
m_lodBiasCurrent = m_lodBiasInitial = 1.0 * m_nStreamingMips ;
m_lodBiasStartTime = Plat_FloatTime ( ) ;
}
else
m_lodBiasCurrent = m_lodBiasInitial = 0.0f ;
m_lastLodBiasAdjustFrame = g_FrameNum ;
}
m_lodClamp = 0 ;
m_nStreamingMips = 0 ;
SetFilteringAndClampingMode ( true ) ;
// The job is complete, Cancel handles cleanup correctly.
CancelStreamingJob ( ) ;
}
void CTexture : : SetErrorTexture ( bool bIsErrorTexture )
{
if ( bIsErrorTexture )
m_nInternalFlags | = TEXTUREFLAGSINTERNAL_ERROR ;
else
m_nInternalFlags & = ( ~ TEXTUREFLAGSINTERNAL_ERROR ) ;
}
//-----------------------------------------------------------------------------
// Creates named render target texture
//-----------------------------------------------------------------------------
void CTexture : : InitRenderTarget (
const char * pRTName ,
int w ,
int h ,
RenderTargetSizeMode_t sizeMode ,
ImageFormat fmt ,
RenderTargetType_t type ,
unsigned int textureFlags ,
unsigned int renderTargetFlags )
{
if ( pRTName )
{
SetName ( pRTName ) ;
}
else
{
static int id = 0 ;
char pName [ 128 ] ;
Q_snprintf ( pName , sizeof ( pName ) , " __render_target_%d " , id ) ;
+ + id ;
SetName ( pName ) ;
}
if ( renderTargetFlags & CREATERENDERTARGETFLAGS_HDR )
{
if ( HardwareConfig ( ) - > GetHDRType ( ) = = HDR_TYPE_FLOAT )
{
// slam the format
fmt = IMAGE_FORMAT_RGBA16161616F ;
}
}
int nFrameCount = 1 ;
int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_RENDERTARGET ;
nFlags | = textureFlags ;
if ( type = = RENDER_TARGET_NO_DEPTH )
{
nFlags | = TEXTUREFLAGS_NODEPTHBUFFER ;
}
else if ( type = = RENDER_TARGET_WITH_DEPTH | | type = = RENDER_TARGET_ONLY_DEPTH | | g_pShaderAPI - > DoRenderTargetsNeedSeparateDepthBuffer ( ) )
{
nFlags | = TEXTUREFLAGS_DEPTHRENDERTARGET ;
+ + nFrameCount ;
}
if ( renderTargetFlags & CREATERENDERTARGETFLAGS_TEMP )
{
m_nInternalFlags | = TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET ;
}
m_nOriginalRenderTargetType = type ;
m_RenderTargetSizeMode = sizeMode ;
m_nOriginalRTWidth = w ;
m_nOriginalRTHeight = h ;
if ( ImageLoader : : ImageFormatInfo ( fmt ) . m_NumAlphaBits > 1 )
{
nFlags | = TEXTUREFLAGS_EIGHTBITALPHA ;
}
else if ( ImageLoader : : ImageFormatInfo ( fmt ) . m_NumAlphaBits = = 1 )
{
nFlags | = TEXTUREFLAGS_ONEBITALPHA ;
}
ApplyRenderTargetSizeMode ( w , h , fmt ) ;
Init ( w , h , 1 , fmt , nFlags , nFrameCount ) ;
m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET ;
}
void CTexture : : OnRestore ( )
{
// May have to change whether or not we have a depth buffer.
// Are we a render target?
if ( m_nFlags & TEXTUREFLAGS_RENDERTARGET )
{
// Did they not ask for a depth buffer?
if ( m_nOriginalRenderTargetType = = RENDER_TARGET )
{
// But, did we force them to have one, or should we force them to have one this time around?
bool bShouldForce = g_pShaderAPI - > DoRenderTargetsNeedSeparateDepthBuffer ( ) ;
bool bDidForce = ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) ! = 0 ) ;
if ( bShouldForce ! = bDidForce )
{
int nFlags = m_nFlags ;
int iFrameCount = m_nFrameCount ;
if ( bShouldForce )
{
Assert ( ! ( nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) ) ;
iFrameCount = 2 ;
nFlags | = TEXTUREFLAGS_DEPTHRENDERTARGET ;
}
else
{
Assert ( ( nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) ) ;
iFrameCount = 1 ;
nFlags & = ~ TEXTUREFLAGS_DEPTHRENDERTARGET ;
}
Shutdown ( ) ;
int newWidth , newHeight ;
ApplyRenderTargetSizeMode ( newWidth , newHeight , m_ImageFormat ) ;
Init ( newWidth , newHeight , 1 , m_ImageFormat , nFlags , iFrameCount ) ;
return ;
}
}
// If we didn't recreate it up above, then we may need to resize it anyway if the framebuffer
// got smaller than we are.
int newWidth , newHeight ;
ApplyRenderTargetSizeMode ( newWidth , newHeight , m_ImageFormat ) ;
if ( newWidth ! = m_dimsMapping . m_nWidth | | newHeight ! = m_dimsMapping . m_nHeight )
{
Shutdown ( ) ;
Init ( newWidth , newHeight , 1 , m_ImageFormat , m_nFlags , m_nFrameCount ) ;
return ;
}
}
else
{
if ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE )
{
MakeResident ( RESIDENT_NONE ) ;
}
}
}
//-----------------------------------------------------------------------------
// Creates a procedural texture
//-----------------------------------------------------------------------------
void CTexture : : InitProceduralTexture ( const char * pTextureName , const char * pTextureGroupName , int w , int h , int d , ImageFormat fmt , int nFlags , ITextureRegenerator * generator )
{
// We shouldn't be asking for render targets here
Assert ( ( nFlags & ( TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET ) ) = = 0 ) ;
SetName ( pTextureName ) ;
// Eliminate flags that are inappropriate...
nFlags & = ~ TEXTUREFLAGS_HINT_DXT5 | TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA |
TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET ;
// Insert required flags
nFlags | = TEXTUREFLAGS_PROCEDURAL ;
int nAlphaBits = ImageLoader : : ImageFormatInfo ( fmt ) . m_NumAlphaBits ;
if ( nAlphaBits > 1 )
{
nFlags | = TEXTUREFLAGS_EIGHTBITALPHA ;
}
else if ( nAlphaBits = = 1 )
{
nFlags | = TEXTUREFLAGS_ONEBITALPHA ;
}
// Procedural textures are always one frame only
Init ( w , h , d , fmt , nFlags , 1 ) ;
SetTextureRegenerator ( generator ) ;
m_TextureGroupName = pTextureGroupName ;
}
//-----------------------------------------------------------------------------
// Creates a file texture
//-----------------------------------------------------------------------------
void CTexture : : InitFileTexture ( const char * pTextureFile , const char * pTextureGroupName )
{
// For files, we only really know about the file name
// At any time, the file contents could change, and we could have
// a different size, number of frames, etc.
SetName ( pTextureFile ) ;
m_TextureGroupName = pTextureGroupName ;
}
//-----------------------------------------------------------------------------
// Assigns/releases texture IDs for our animation frames
//-----------------------------------------------------------------------------
void CTexture : : AllocateTextureHandles ( )
{
Assert ( ! m_pTextureHandles ) ;
Assert ( m_nFrameCount > 0 ) ;
# ifdef DBGFLAG_ASSERT
if ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
{
Assert ( m_nFrameCount > = 2 ) ;
}
# endif
m_pTextureHandles = new ShaderAPITextureHandle_t [ m_nFrameCount ] ;
for ( int i = 0 ; i ! = m_nFrameCount ; + + i )
m_pTextureHandles [ i ] = INVALID_SHADERAPI_TEXTURE_HANDLE ;
}
void CTexture : : ReleaseTextureHandles ( )
{
if ( m_pTextureHandles )
{
delete [ ] m_pTextureHandles ;
m_pTextureHandles = NULL ;
}
}
//-----------------------------------------------------------------------------
// Creates the texture
//-----------------------------------------------------------------------------
bool CTexture : : AllocateShaderAPITextures ( )
{
Assert ( ! HasBeenAllocated ( ) ) ;
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
int nCount = m_nFrameCount ;
int nCreateFlags = 0 ;
if ( ( m_nFlags & TEXTUREFLAGS_ENVMAP ) & & HardwareConfig ( ) - > SupportsCubeMaps ( ) )
{
nCreateFlags | = TEXTURE_CREATE_CUBEMAP ;
}
bool bIsFloat = ( m_ImageFormat = = IMAGE_FORMAT_RGBA16161616F ) | | ( m_ImageFormat = = IMAGE_FORMAT_R32F ) | |
( m_ImageFormat = = IMAGE_FORMAT_RGB323232F ) | | ( m_ImageFormat = = IMAGE_FORMAT_RGBA32323232F ) ;
// Don't do sRGB on floating point textures
if ( ( m_nFlags & TEXTUREFLAGS_SRGB ) & & ! bIsFloat )
{
nCreateFlags | = TEXTURE_CREATE_SRGB ; // for Posix/GL only
}
if ( m_nFlags & TEXTUREFLAGS_RENDERTARGET )
{
nCreateFlags | = TEXTURE_CREATE_RENDERTARGET ;
// This here is simply so we can use a different call to
// create the depth texture below
if ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) & & ( nCount = = 2 ) ) //nCount must be 2 on pc
{
- - nCount ;
}
}
else
{
// If it's not a render target, use the texture manager in dx
if ( m_nFlags & TEXTUREFLAGS_STAGING_MEMORY )
nCreateFlags | = TEXTURE_CREATE_SYSMEM ;
else
{
# if defined(IS_WINDOWS_PC)
static ConVarRef mat_dxlevel ( " mat_dxlevel " ) ;
if ( mat_dxlevel . GetInt ( ) < 90 | | mat_managedtextures . GetBool ( ) )
# endif
{
nCreateFlags | = TEXTURE_CREATE_MANAGED ;
}
}
}
if ( m_nFlags & TEXTUREFLAGS_POINTSAMPLE )
{
nCreateFlags | = TEXTURE_CREATE_UNFILTERABLE_OK ;
}
if ( m_nFlags & TEXTUREFLAGS_VERTEXTEXTURE )
{
nCreateFlags | = TEXTURE_CREATE_VERTEXTEXTURE ;
}
int nCopies = 1 ;
if ( IsProcedural ( ) )
{
// This is sort of hacky... should we store the # of copies in the VTF?
if ( ! ( m_nFlags & TEXTUREFLAGS_SINGLECOPY ) )
{
// FIXME: That 6 there is heuristically what I came up with what I
// need to get eyes not to stall on map alyx3. We need a better way
// of determining how many copies of the texture we should store.
nCopies = 6 ;
}
}
// For depth only render target: adjust texture width/height
// Currently we just leave it the same size, will update with further testing
int nShaderApiCreateTextureDepth = ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) & & ( m_nOriginalRenderTargetType = = RENDER_TARGET_ONLY_DEPTH ) ) ? 1 : m_dimsAllocated . m_nDepth ;
// Create all animated texture frames in a single call
g_pShaderAPI - > CreateTextures (
m_pTextureHandles , nCount ,
m_dimsAllocated . m_nWidth , m_dimsAllocated . m_nHeight , nShaderApiCreateTextureDepth , m_ImageFormat , m_dimsAllocated . m_nMipCount ,
nCopies , nCreateFlags , GetName ( ) , GetTextureGroupName ( ) ) ;
int accountingCount = nCount ;
// Create the depth render target buffer
if ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
{
MEM_ALLOC_CREDIT ( ) ;
Assert ( nCount = = 1 ) ;
char debugName [ 128 ] ;
Q_snprintf ( debugName , ARRAYSIZE ( debugName ) , " %s_ZBuffer " , GetName ( ) ) ;
Assert ( m_nFrameCount > = 2 ) ;
m_pTextureHandles [ 1 ] = g_pShaderAPI - > CreateDepthTexture (
m_ImageFormat ,
m_dimsAllocated . m_nWidth ,
m_dimsAllocated . m_nHeight ,
debugName ,
( m_nOriginalRenderTargetType = = RENDER_TARGET_ONLY_DEPTH ) ) ;
accountingCount + = 1 ;
}
STAGING_ONLY_EXEC ( g_currentTextures . InsertOrReplace ( this , TexInfo_t ( GetName ( ) , m_dimsAllocated . m_nWidth , m_dimsAllocated . m_nHeight , m_dimsAllocated . m_nDepth , m_dimsAllocated . m_nMipCount , accountingCount , nCopies , m_ImageFormat ) ) ) ;
m_nInternalFlags | = TEXTUREFLAGSINTERNAL_ALLOCATED ;
return true ;
}
//-----------------------------------------------------------------------------
// Releases the texture's hardware memory
//-----------------------------------------------------------------------------
void CTexture : : FreeShaderAPITextures ( )
{
if ( m_pTextureHandles & & HasBeenAllocated ( ) )
{
# ifdef STAGING_ONLY
// If this hits, there's a leak because we're not deallocating enough textures. Yikes!
Assert ( g_currentTextures [ g_currentTextures . Find ( this ) ] . m_nFrameCount = = m_nFrameCount ) ;
// Remove ourselves from the list.
g_currentTextures . Remove ( this ) ;
# endif
// Release the frames
for ( int i = m_nFrameCount ; - - i > = 0 ; )
{
if ( g_pShaderAPI - > IsTexture ( m_pTextureHandles [ i ] ) )
{
# ifdef WIN32
Assert ( _heapchk ( ) = = _HEAPOK ) ;
# endif
g_pShaderAPI - > DeleteTexture ( m_pTextureHandles [ i ] ) ;
m_pTextureHandles [ i ] = INVALID_SHADERAPI_TEXTURE_HANDLE ;
}
}
}
m_nInternalFlags & = ~ TEXTUREFLAGSINTERNAL_ALLOCATED ;
// Clear texture streaming stuff, too.
if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) ! = 0 )
{
m_nFlags & = ~ TEXTUREFLAGS_STREAMABLE_FINE ;
m_residenceCurrent = m_residenceTarget = RESIDENT_NONE ;
m_lodClamp = 0 ;
m_lodBiasCurrent = m_lodBiasInitial = 0 ;
m_lodBiasStartTime = 0 ;
}
}
void CTexture : : MigrateShaderAPITextures ( )
{
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
const int cBytes = m_nFrameCount * sizeof ( ShaderAPITextureHandle_t ) ;
ShaderAPITextureHandle_t * pTextureHandles = ( ShaderAPITextureHandle_t * ) stackalloc ( cBytes ) ;
Assert ( pTextureHandles ) ;
if ( ! pTextureHandles )
return ;
V_memcpy ( pTextureHandles , m_pTextureHandles , cBytes ) ;
// Pretend we haven't been allocated yet.
m_nInternalFlags & = ~ TEXTUREFLAGSINTERNAL_ALLOCATED ;
AllocateShaderAPITextures ( ) ;
for ( int i = 0 ; i < m_nFrameCount ; + + i )
{
Assert ( g_pShaderAPI - > IsTexture ( pTextureHandles [ i ] ) = = g_pShaderAPI - > IsTexture ( m_pTextureHandles [ i ] ) ) ;
if ( ! g_pShaderAPI - > IsTexture ( pTextureHandles [ i ] ) )
continue ;
g_pShaderAPI - > CopyTextureToTexture ( pTextureHandles [ i ] , m_pTextureHandles [ i ] ) ;
}
for ( int i = 0 ; i < m_nFrameCount ; + + i )
{
if ( ! g_pShaderAPI - > IsTexture ( pTextureHandles [ i ] ) )
continue ;
g_pShaderAPI - > DeleteTexture ( pTextureHandles [ i ] ) ;
}
}
//-----------------------------------------------------------------------------
// Computes the actual format of the texture
//-----------------------------------------------------------------------------
ImageFormat CTexture : : ComputeActualFormat ( ImageFormat srcFormat )
{
ImageFormat dstFormat ;
bool bIsCompressed = ImageLoader : : IsCompressed ( srcFormat ) ;
if ( g_config . bCompressedTextures & & HardwareConfig ( ) - > SupportsCompressedTextures ( ) & & bIsCompressed )
{
// for the runtime compressed formats the srcFormat won't equal the dstFormat, and we need to return srcFormat here
if ( ImageLoader : : IsRuntimeCompressed ( srcFormat ) )
{
return srcFormat ;
}
// don't do anything since we are already in a compressed format.
dstFormat = g_pShaderAPI - > GetNearestSupportedFormat ( srcFormat ) ;
Assert ( dstFormat = = srcFormat ) ;
return dstFormat ;
}
// NOTE: Below this piece of code is only called when compressed textures are
// turned off, or if the source texture is not compressed.
# ifdef DX_TO_GL_ABSTRACTION
if ( ( srcFormat = = IMAGE_FORMAT_UVWQ8888 ) | | ( srcFormat = = IMAGE_FORMAT_UV88 ) | | ( srcFormat = = IMAGE_FORMAT_UVLX8888 ) )
{
// Danger, this is going to blow up on the Mac. You better know what you're
// doing with these exotic formats...which were introduced in 1999
Assert ( 0 ) ;
}
# endif
// We use the TEXTUREFLAGS_EIGHTBITALPHA and TEXTUREFLAGS_ONEBITALPHA flags
// to decide how many bits of alpha we need; vtex checks the alpha channel
// for all white, etc.
if ( ( srcFormat = = IMAGE_FORMAT_UVWQ8888 ) | | ( srcFormat = = IMAGE_FORMAT_UV88 ) | |
( srcFormat = = IMAGE_FORMAT_UVLX8888 ) | | ( srcFormat = = IMAGE_FORMAT_RGBA16161616 ) | |
( srcFormat = = IMAGE_FORMAT_RGBA16161616F ) )
{
# ifdef DX_TO_GL_ABSTRACTION
dstFormat = g_pShaderAPI - > GetNearestSupportedFormat ( srcFormat , false ) ; // Stupid HACK!
# else
dstFormat = g_pShaderAPI - > GetNearestSupportedFormat ( srcFormat , true ) ; // Stupid HACK!
# endif
}
else if ( m_nFlags & ( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA ) )
{
dstFormat = g_pShaderAPI - > GetNearestSupportedFormat ( IMAGE_FORMAT_BGRA8888 ) ;
}
else if ( srcFormat = = IMAGE_FORMAT_I8 )
{
dstFormat = g_pShaderAPI - > GetNearestSupportedFormat ( IMAGE_FORMAT_I8 ) ;
}
else
{
dstFormat = g_pShaderAPI - > GetNearestSupportedFormat ( IMAGE_FORMAT_BGR888 ) ;
}
return dstFormat ;
}
//-----------------------------------------------------------------------------
// Calculates info about whether we can make the texture smaller and by how much
//-----------------------------------------------------------------------------
int CTexture : : ComputeActualSize ( bool bIgnorePicmip , IVTFTexture * pVTFTexture , bool bTextureMigration )
{
unsigned int stripFlags = 0 ;
return ComputeMipSkipCount ( GetName ( ) , m_dimsMapping , bIgnorePicmip , pVTFTexture , m_nFlags , m_nDesiredDimensionLimit , & m_nStreamingMips , & m_cachedFileLodSettings , & m_dimsActual , & m_dimsAllocated , & stripFlags ) ;
Assert ( stripFlags = = 0 ) ; // Not necessarily illegal, just needs investigating.
}
//-----------------------------------------------------------------------------
// Used to modify the texture bits (procedural textures only)
//-----------------------------------------------------------------------------
void CTexture : : SetTextureRegenerator ( ITextureRegenerator * pTextureRegen )
{
// NOTE: These can only be used by procedural textures
Assert ( IsProcedural ( ) ) ;
if ( m_pTextureRegenerator )
{
m_pTextureRegenerator - > Release ( ) ;
}
m_pTextureRegenerator = pTextureRegen ;
}
//-----------------------------------------------------------------------------
// Gets us modifying a particular frame of our texture
//-----------------------------------------------------------------------------
void CTexture : : Modify ( int iFrame )
{
Assert ( iFrame > = 0 & & iFrame < m_nFrameCount ) ;
Assert ( HasBeenAllocated ( ) ) ;
g_pShaderAPI - > ModifyTexture ( m_pTextureHandles [ iFrame ] ) ;
}
//-----------------------------------------------------------------------------
// Sets the texture clamping state on the currently modified frame
//-----------------------------------------------------------------------------
void CTexture : : SetWrapState ( )
{
// Border clamp applies to all texture coordinates
if ( m_nFlags & TEXTUREFLAGS_BORDER )
{
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_S , SHADER_TEXWRAPMODE_BORDER ) ;
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_T , SHADER_TEXWRAPMODE_BORDER ) ;
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_U , SHADER_TEXWRAPMODE_BORDER ) ;
return ;
}
// Clamp mode in S
if ( m_nFlags & TEXTUREFLAGS_CLAMPS )
{
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_S , SHADER_TEXWRAPMODE_CLAMP ) ;
}
else
{
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_S , SHADER_TEXWRAPMODE_REPEAT ) ;
}
// Clamp mode in T
if ( m_nFlags & TEXTUREFLAGS_CLAMPT )
{
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_T , SHADER_TEXWRAPMODE_CLAMP ) ;
}
else
{
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_T , SHADER_TEXWRAPMODE_REPEAT ) ;
}
// Clamp mode in U
if ( m_nFlags & TEXTUREFLAGS_CLAMPU )
{
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_U , SHADER_TEXWRAPMODE_CLAMP ) ;
}
else
{
g_pShaderAPI - > TexWrap ( SHADER_TEXCOORD_U , SHADER_TEXWRAPMODE_REPEAT ) ;
}
}
//-----------------------------------------------------------------------------
// Sets the texture filtering state on the currently modified frame
//-----------------------------------------------------------------------------
void CTexture : : SetFilterState ( )
{
// Turns off filtering when we're point sampling
if ( m_nFlags & TEXTUREFLAGS_POINTSAMPLE )
{
g_pShaderAPI - > TexMinFilter ( SHADER_TEXFILTERMODE_NEAREST ) ;
g_pShaderAPI - > TexMagFilter ( SHADER_TEXFILTERMODE_NEAREST ) ;
return ;
}
// NOTE: config.bMipMapTextures and config.bFilterTextures is handled in ShaderAPIDX8
bool bEnableMipmapping = ( m_nFlags & TEXTUREFLAGS_NOMIP ) ? false : true ;
if ( ! bEnableMipmapping )
{
g_pShaderAPI - > TexMinFilter ( SHADER_TEXFILTERMODE_LINEAR ) ;
g_pShaderAPI - > TexMagFilter ( SHADER_TEXFILTERMODE_LINEAR ) ;
return ;
}
// Determing the filtering mode
bool bIsAnisotropic = false , bIsTrilinear = false ;
if ( HardwareConfig ( ) - > GetDXSupportLevel ( ) > = 80 & & ( g_config . m_nForceAnisotropicLevel > 1 ) & &
( HardwareConfig ( ) - > MaximumAnisotropicLevel ( ) > 1 ) )
{
bIsAnisotropic = true ;
}
else if ( g_config . ForceTrilinear ( ) )
{
bIsAnisotropic = ( ( m_nFlags & TEXTUREFLAGS_ANISOTROPIC ) ! = 0 ) & & ( HardwareConfig ( ) - > MaximumAnisotropicLevel ( ) > 1 ) ;
bIsTrilinear = true ;
}
else
{
bIsAnisotropic = ( ( m_nFlags & TEXTUREFLAGS_ANISOTROPIC ) ! = 0 ) & & ( HardwareConfig ( ) - > MaximumAnisotropicLevel ( ) > 1 ) ;
bIsTrilinear = ( m_nFlags & TEXTUREFLAGS_TRILINEAR ) ! = 0 ;
}
if ( bIsAnisotropic )
{
g_pShaderAPI - > TexMinFilter ( SHADER_TEXFILTERMODE_ANISOTROPIC ) ;
g_pShaderAPI - > TexMagFilter ( SHADER_TEXFILTERMODE_ANISOTROPIC ) ;
}
else
{
// force trilinear if we are on a dx8 card or above
if ( bIsTrilinear )
{
g_pShaderAPI - > TexMinFilter ( SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR ) ;
g_pShaderAPI - > TexMagFilter ( SHADER_TEXFILTERMODE_LINEAR ) ;
}
else
{
g_pShaderAPI - > TexMinFilter ( SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST ) ;
g_pShaderAPI - > TexMagFilter ( SHADER_TEXFILTERMODE_LINEAR ) ;
}
}
SetLodState ( ) ;
}
//-----------------------------------------------------------------------------
// Sets the lod state on the currently modified frame
//-----------------------------------------------------------------------------
void CTexture : : SetLodState ( )
{
// Set the lod clamping value to ensure we don't see anything we're not supposed to.
g_pShaderAPI - > TexLodClamp ( m_lodClamp ) ;
g_pShaderAPI - > TexLodBias ( m_lodBiasCurrent ) ;
}
//-----------------------------------------------------------------------------
// Download bits main entry point!!
//-----------------------------------------------------------------------------
void CTexture : : DownloadTexture ( Rect_t * pRect , bool bCopyFromCurrent )
{
// No downloading necessary if there's no graphics
if ( ! g_pShaderDevice - > IsUsingGraphics ( ) )
return ;
// We don't know the actual size of the texture at this stage...
if ( ! pRect )
{
ReconstructTexture ( bCopyFromCurrent ) ;
}
else
{
// Not implemented yet.
Assert ( bCopyFromCurrent = = false ) ;
ReconstructPartialTexture ( pRect ) ;
}
// Iterate over all the frames and set the appropriate wrapping + filtering state
SetFilteringAndClampingMode ( ) ;
// texture bits have been updated, update the exclusion state
if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE )
{
m_nInternalFlags | = TEXTUREFLAGSINTERNAL_EXCLUDED ;
}
else
{
m_nInternalFlags & = ~ TEXTUREFLAGSINTERNAL_EXCLUDED ;
}
// texture bits have been picmipped, update the picmip state
m_nActualDimensionLimit = m_nDesiredDimensionLimit ;
}
void CTexture : : Download ( Rect_t * pRect , int nAdditionalCreationFlags /* = 0 */ )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// Only download the bits if we can...
if ( g_pShaderAPI - > CanDownloadTextures ( ) )
{
MaterialLock_t hLock = MaterialSystem ( ) - > Lock ( ) ;
m_nFlags | = nAdditionalCreationFlags ; // Path to let stdshaders drive settings like sRGB-ness at creation time
DownloadTexture ( pRect ) ;
MaterialSystem ( ) - > Unlock ( hLock ) ;
}
}
// Save texture to a file.
bool CTexture : : SaveToFile ( const char * fileName )
{
bool bRet = false ;
ITexture * pTexture = materials - > FindTexture ( " _rt_FullFrameFB1 " , TEXTURE_GROUP_RENDER_TARGET ) ;
if ( ! pTexture )
return bRet ;
const int width = GetActualWidth ( ) ;
const int height = GetActualHeight ( ) ;
if ( pTexture - > GetImageFormat ( ) = = IMAGE_FORMAT_RGBA8888 | |
pTexture - > GetImageFormat ( ) = = IMAGE_FORMAT_ABGR8888 | |
pTexture - > GetImageFormat ( ) = = IMAGE_FORMAT_ARGB8888 | |
pTexture - > GetImageFormat ( ) = = IMAGE_FORMAT_BGRA8888 | |
pTexture - > GetImageFormat ( ) = = IMAGE_FORMAT_BGRX8888 )
{
bool bCleanupTexture = false ;
// Need to allocate a temporarily renderable surface. Sadness.
if ( width > pTexture - > GetActualWidth ( ) | | height > pTexture - > GetActualHeight ( ) )
{
materials - > OverrideRenderTargetAllocation ( true ) ;
// This one bumps the ref automatically for us.
pTexture = materials - > CreateNamedRenderTargetTextureEx ( " _rt_savetofile " , width , height , RT_SIZE_LITERAL , IMAGE_FORMAT_BGRA8888 , MATERIAL_RT_DEPTH_NONE , TEXTUREFLAGS_IMMEDIATE_CLEANUP ) ;
materials - > OverrideRenderTargetAllocation ( false ) ;
if ( ! pTexture | | pTexture - > IsError ( ) )
{
SafeRelease ( & pTexture ) ;
Msg ( " SaveToFile: texture '_rt_FullFrameFB1' failed. Ptr:%p Format:%d \n " , pTexture , ( pTexture ? pTexture - > GetImageFormat ( ) : 0 ) ) ;
return false ;
}
bCleanupTexture = true ;
}
Rect_t SrcRect = { 0 , 0 , width , height } ;
Rect_t DstRect = SrcRect ;
if ( ( width > 0 ) & & ( height > 0 ) )
{
void * pixelValue = malloc ( width * height * 2 * sizeof ( BGRA8888_t ) ) ;
if ( pixelValue )
{
CMatRenderContextPtr pRenderContext ( MaterialSystem ( ) ) ;
// Set the clear color to opaque black
pRenderContext - > ClearColor4ub ( 0 , 0 , 0 , 0xFF ) ;
pRenderContext - > ClearBuffers ( true , true , true ) ;
pRenderContext - > PushRenderTargetAndViewport ( pTexture , 0 , 0 , width , height ) ;
pRenderContext - > CopyTextureToRenderTargetEx ( 0 , this , & SrcRect , & DstRect ) ;
pRenderContext - > ReadPixels ( 0 , 0 , width , height , ( unsigned char * ) pixelValue , pTexture - > GetImageFormat ( ) ) ;
// Slap the alpha channel at the bottom of the tga file so we don't have to deal with crappy tools that can't
// handle rgb + alpha well. This means we can just do a "mat_texture_save_fonts" concommand, and then use
// something like Beyond Compare to look at the fonts differences between various platforms, etc.
CPixelWriter pixelWriterSrc ;
CPixelWriter pixelWriterDst ;
pixelWriterSrc . SetPixelMemory ( pTexture - > GetImageFormat ( ) , pixelValue , width * sizeof ( BGRA8888_t ) ) ;
pixelWriterDst . SetPixelMemory ( pTexture - > GetImageFormat ( ) , pixelValue , width * sizeof ( BGRA8888_t ) ) ;
for ( int y = 0 ; y < height ; + + y )
{
pixelWriterSrc . Seek ( 0 , y ) ;
pixelWriterDst . Seek ( 0 , y + height ) ;
for ( int x = 0 ; x < width ; + + x )
{
int r , g , b , a ;
pixelWriterSrc . ReadPixelNoAdvance ( r , g , b , a ) ;
pixelWriterSrc . WritePixel ( a , a , a , 255 ) ;
pixelWriterDst . WritePixel ( r , g , b , 255 ) ;
}
}
if ( TGAWriter : : WriteTGAFile ( fileName , width , height * 2 , pTexture - > GetImageFormat ( ) , ( uint8 * ) pixelValue , width * sizeof ( BGRA8888_t ) ) )
{
bRet = true ;
}
// restore our previous state
pRenderContext - > PopRenderTargetAndViewport ( ) ;
free ( pixelValue ) ;
}
}
if ( bCleanupTexture )
SafeRelease ( & pTexture ) ;
}
else
{
Msg ( " SaveToFile: texture '_rt_FullFrameFB1' failed. Ptr:%p Format:%d \n " , pTexture , ( pTexture ? pTexture - > GetImageFormat ( ) : 0 ) ) ;
}
return bRet ;
}
bool CTexture : : AsyncReadTextureFromFile ( IVTFTexture * pVTFTexture , unsigned int nAdditionalCreationFlags )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
m_bStreamingFileReadFailed = false ; // Optimism!
char pCacheFileName [ MATERIAL_MAX_PATH ] ;
FileHandle_t fileHandle = FILESYSTEM_INVALID_HANDLE ;
GetCacheFilename ( pCacheFileName , MATERIAL_MAX_PATH ) ;
if ( ! GetFileHandle ( & fileHandle , pCacheFileName , NULL ) )
{
m_bStreamingFileReadFailed = true ;
return false ;
}
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - %s " , __FUNCTION__ , tmDynamicString ( TELEMETRY_LEVEL0 , pCacheFileName ) ) ;
2023-01-14 19:48:22 +08:00
2020-04-23 00:56:21 +08:00
// OSX hackery
int nPreserveFlags = nAdditionalCreationFlags ;
if ( m_nFlags & TEXTUREFLAGS_SRGB )
nPreserveFlags | = TEXTUREFLAGS_SRGB ;
uint16 dontCareStreamedMips = m_nStreamingMips ;
TextureLODControlSettings_t settings = m_cachedFileLodSettings ;
if ( ! SLoadTextureBitsFromFile ( & pVTFTexture , fileHandle , m_nFlags | nPreserveFlags , & settings , m_nDesiredDimensionLimit , & dontCareStreamedMips , GetName ( ) , pCacheFileName , & m_dimsMapping ) )
{
g_pFullFileSystem - > Close ( fileHandle ) ;
m_bStreamingFileReadFailed = true ;
return false ;
}
g_pFullFileSystem - > Close ( fileHandle ) ;
m_pStreamingVTF = pVTFTexture ;
return true ;
}
void CTexture : : AsyncCancelReadTexture ( )
{
Assert ( m_bStreamingFileReadFailed | | m_pStreamingVTF ! = NULL ) ;
if ( m_pStreamingVTF )
{
TextureManager ( ) - > ReleaseAsyncScratchVTF ( m_pStreamingVTF ) ;
m_pStreamingVTF = NULL ;
}
}
void CTexture : : Bind ( Sampler_t sampler )
{
Bind ( sampler , 0 ) ;
}
//-----------------------------------------------------------------------------
// Binds a particular texture (possibly paired)
//-----------------------------------------------------------------------------
void CTexture : : Bind ( Sampler_t sampler1 , int nFrame , Sampler_t sampler2 /* = -1 */ )
{
if ( g_pShaderDevice - > IsUsingGraphics ( ) )
{
TextureManager ( ) - > RequestAllMipmaps ( this ) ;
if ( nFrame < 0 | | nFrame > = m_nFrameCount )
{
// FIXME: Use the well-known 'error' id instead of frame 0
nFrame = 0 ;
// Assert(0);
}
// Make sure we've actually allocated the texture handle
if ( HasBeenAllocated ( ) )
{
g_pShaderAPI - > BindTexture ( sampler1 , m_pTextureHandles [ nFrame ] ) ;
}
else
{
ExecuteNTimes ( 20 , Warning ( " Trying to bind texture %s, but texture handles are not valid. Binding a white texture! \n " , GetName ( ) ) ) ;
g_pShaderAPI - > BindStandardTexture ( sampler1 , TEXTURE_WHITE ) ;
}
}
}
void CTexture : : BindVertexTexture ( VertexTextureSampler_t sampler , int nFrame )
{
if ( g_pShaderDevice - > IsUsingGraphics ( ) )
{
if ( nFrame < 0 | | nFrame > = m_nFrameCount )
{
// FIXME: Use the well-known 'error' id instead of frame 0
nFrame = 0 ;
// Assert(0);
}
// Make sure we've actually allocated the texture
Assert ( HasBeenAllocated ( ) ) ;
g_pShaderAPI - > BindVertexTexture ( sampler , m_pTextureHandles [ nFrame ] ) ;
}
}
//-----------------------------------------------------------------------------
// Set this texture as a render target
//-----------------------------------------------------------------------------
bool CTexture : : SetRenderTarget ( int nRenderTargetID )
{
return SetRenderTarget ( nRenderTargetID , NULL ) ;
}
//-----------------------------------------------------------------------------
// Set this texture as a render target
// Optionally bind pDepthTexture as depth buffer
//-----------------------------------------------------------------------------
bool CTexture : : SetRenderTarget ( int nRenderTargetID , ITexture * pDepthTexture )
{
if ( ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) = = 0 )
return false ;
// Make sure we've actually allocated the texture handles
Assert ( HasBeenAllocated ( ) ) ;
ShaderAPITextureHandle_t textureHandle = m_pTextureHandles [ 0 ] ;
2022-02-23 19:50:30 +08:00
ShaderAPITextureHandle_t depthTextureHandle = ( uintp ) SHADER_RENDERTARGET_DEPTHBUFFER ;
2020-04-23 00:56:21 +08:00
if ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
{
Assert ( m_nFrameCount > = 2 ) ;
depthTextureHandle = m_pTextureHandles [ 1 ] ;
}
else if ( m_nFlags & TEXTUREFLAGS_NODEPTHBUFFER )
{
// GR - render target without depth buffer
2022-02-23 19:50:30 +08:00
depthTextureHandle = ( uintp ) SHADER_RENDERTARGET_NONE ;
2020-04-23 00:56:21 +08:00
}
if ( pDepthTexture )
{
depthTextureHandle = static_cast < ITextureInternal * > ( pDepthTexture ) - > GetTextureHandle ( 0 ) ;
}
g_pShaderAPI - > SetRenderTargetEx ( nRenderTargetID , textureHandle , depthTextureHandle ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Reference counting
//-----------------------------------------------------------------------------
void CTexture : : IncrementReferenceCount ( void )
{
+ + m_nRefCount ;
}
void CTexture : : DecrementReferenceCount ( void )
{
if ( ( - - m_nRefCount < = 0 ) & & ( m_nFlags & TEXTUREFLAGS_IMMEDIATE_CLEANUP ) ! = 0 )
{
Assert ( m_nRefCount = = 0 ) ;
// Just inform the texture manager, it will decide to free us at a later date.
TextureManager ( ) - > MarkUnreferencedTextureForCleanup ( this ) ;
}
}
int CTexture : : GetReferenceCount ( )
{
return m_nRefCount ;
}
//-----------------------------------------------------------------------------
// Various accessor methods
//-----------------------------------------------------------------------------
const char * CTexture : : GetName ( ) const
{
return m_Name . String ( ) ;
}
const char * CTexture : : GetTextureGroupName ( ) const
{
return m_TextureGroupName . String ( ) ;
}
void CTexture : : SetName ( const char * pName )
{
// normalize and convert to a symbol
char szCleanName [ MAX_PATH ] ;
m_Name = NormalizeTextureName ( pName , szCleanName , sizeof ( szCleanName ) ) ;
# ifdef _DEBUG
if ( m_pDebugName )
{
delete [ ] m_pDebugName ;
}
int nLen = V_strlen ( szCleanName ) + 1 ;
m_pDebugName = new char [ nLen ] ;
V_memcpy ( m_pDebugName , szCleanName , nLen ) ;
# endif
}
ImageFormat CTexture : : GetImageFormat ( ) const
{
return m_ImageFormat ;
}
int CTexture : : GetMappingWidth ( ) const
{
return m_dimsMapping . m_nWidth ;
}
int CTexture : : GetMappingHeight ( ) const
{
return m_dimsMapping . m_nHeight ;
}
int CTexture : : GetMappingDepth ( ) const
{
return m_dimsMapping . m_nDepth ;
}
int CTexture : : GetActualWidth ( ) const
{
return m_dimsActual . m_nWidth ;
}
int CTexture : : GetActualHeight ( ) const
{
return m_dimsActual . m_nHeight ;
}
int CTexture : : GetActualDepth ( ) const
{
return m_dimsActual . m_nDepth ;
}
int CTexture : : GetNumAnimationFrames ( ) const
{
return m_nFrameCount ;
}
void CTexture : : GetReflectivity ( Vector & reflectivity )
{
Precache ( ) ;
VectorCopy ( m_vecReflectivity , reflectivity ) ;
}
//-----------------------------------------------------------------------------
// Little helper polling methods
//-----------------------------------------------------------------------------
bool CTexture : : IsTranslucent ( ) const
{
return ( m_nFlags & ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) ) ! = 0 ;
}
bool CTexture : : IsNormalMap ( void ) const
{
return ( ( m_nFlags & TEXTUREFLAGS_NORMAL ) ! = 0 ) ;
}
bool CTexture : : IsCubeMap ( void ) const
{
return ( ( m_nFlags & TEXTUREFLAGS_ENVMAP ) ! = 0 ) ;
}
bool CTexture : : IsRenderTarget ( void ) const
{
return ( ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) ! = 0 ) ;
}
bool CTexture : : IsTempRenderTarget ( void ) const
{
return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET ) ! = 0 ) ;
}
bool CTexture : : IsProcedural ( ) const
{
return ( ( m_nFlags & TEXTUREFLAGS_PROCEDURAL ) ! = 0 ) ;
}
bool CTexture : : IsMipmapped ( ) const
{
return ( ( m_nFlags & TEXTUREFLAGS_NOMIP ) = = 0 ) ;
}
unsigned int CTexture : : GetFlags ( ) const
{
return m_nFlags ;
}
void CTexture : : ForceLODOverride ( int iNumLodsOverrideUpOrDown )
{
TextureLodOverride : : OverrideInfo oi ( iNumLodsOverrideUpOrDown , iNumLodsOverrideUpOrDown ) ;
TextureLodOverride : : Add ( GetName ( ) , oi ) ;
Download ( NULL ) ;
}
bool CTexture : : IsError ( ) const
{
return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_ERROR ) ! = 0 ) ;
}
bool CTexture : : HasBeenAllocated ( ) const
{
return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_ALLOCATED ) ! = 0 ) ;
}
bool CTexture : : IsVolumeTexture ( ) const
{
return ( m_dimsMapping . m_nDepth > 1 ) ;
}
//-----------------------------------------------------------------------------
// Sets the filtering + clamping modes on the texture
//-----------------------------------------------------------------------------
void CTexture : : SetFilteringAndClampingMode ( bool bOnlyLodValues )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
if ( ! HasBeenAllocated ( ) )
return ;
for ( int iFrame = 0 ; iFrame < m_nFrameCount ; + + iFrame )
{
Modify ( iFrame ) ; // Indicate we're changing state with respect to a particular frame
if ( ! bOnlyLodValues )
{
SetWrapState ( ) ; // Send the appropriate wrap/clamping modes to the shaderapi.
SetFilterState ( ) ; // Set the filtering mode for the texture after downloading it.
// NOTE: Apparently, the filter state cannot be set until after download
}
else
SetLodState ( ) ;
}
}
//-----------------------------------------------------------------------------
// Loads up the non-fallback information about the texture
//-----------------------------------------------------------------------------
void CTexture : : Precache ( )
{
// We only have to do something in the case of a file texture
if ( IsRenderTarget ( ) | | IsProcedural ( ) )
return ;
if ( HasBeenAllocated ( ) )
return ;
// Blow off env_cubemap too...
if ( ! Q_strnicmp ( m_Name . String ( ) , " env_cubemap " , 12 ) )
return ;
int nAdditionalFlags = 0 ;
if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) ! = 0 )
{
// If we were previously streamed in, make sure we still do this time around.
nAdditionalFlags = TEXTUREFLAGS_STREAMABLE_COARSE ;
Assert ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE ) = = 0 ) ;
Assert ( m_residenceCurrent = = RESIDENT_NONE & & m_residenceTarget = = RESIDENT_NONE ) ;
Assert ( m_lodClamp = = 0 ) ;
Assert ( m_lodBiasCurrent = = 0 & & m_lodBiasInitial = = 0 ) ;
Assert ( m_lodBiasStartTime = = 0 ) ;
}
ScratchVTF scratch ( this ) ;
IVTFTexture * pVTFTexture = scratch . Get ( ) ;
// The texture name doubles as the relative file name
// It's assumed to have already been set by this point
// Compute the cache name
char pCacheFileName [ MATERIAL_MAX_PATH ] ;
Q_snprintf ( pCacheFileName , sizeof ( pCacheFileName ) , " materials/%s " TEXTURE_FNAME_EXTENSION , m_Name . String ( ) ) ;
int nHeaderSize = VTFFileHeaderSize ( VTF_MAJOR_VERSION ) ;
unsigned char * pMem = ( unsigned char * ) stackalloc ( nHeaderSize ) ;
CUtlBuffer buf ( pMem , nHeaderSize ) ;
if ( ! g_pFullFileSystem - > ReadFile ( pCacheFileName , NULL , buf , nHeaderSize ) )
{
goto precacheFailed ;
}
if ( ! pVTFTexture - > Unserialize ( buf , true ) )
{
Warning ( " Error reading material \" %s \" \n " , pCacheFileName ) ;
goto precacheFailed ;
}
// NOTE: Don't set the image format in case graphics are active
VectorCopy ( pVTFTexture - > Reflectivity ( ) , m_vecReflectivity ) ;
m_dimsMapping . m_nWidth = pVTFTexture - > Width ( ) ;
m_dimsMapping . m_nHeight = pVTFTexture - > Height ( ) ;
m_dimsMapping . m_nDepth = pVTFTexture - > Depth ( ) ;
m_nFlags = pVTFTexture - > Flags ( ) | nAdditionalFlags ;
m_nFrameCount = pVTFTexture - > FrameCount ( ) ;
if ( ! m_pTextureHandles )
{
// NOTE: m_nFrameCount and m_pTextureHandles are strongly associated
// whenever one is modified the other must also be modified
AllocateTextureHandles ( ) ;
}
return ;
precacheFailed :
m_vecReflectivity . Init ( 0 , 0 , 0 ) ;
m_dimsMapping . m_nWidth = 32 ;
m_dimsMapping . m_nHeight = 32 ;
m_dimsMapping . m_nDepth = 1 ;
m_nFlags = TEXTUREFLAGS_NOMIP ;
SetErrorTexture ( true ) ;
m_nFrameCount = 1 ;
if ( ! m_pTextureHandles )
{
// NOTE: m_nFrameCount and m_pTextureHandles are strongly associated
// whenever one is modified the other must also be modified
AllocateTextureHandles ( ) ;
}
}
//-----------------------------------------------------------------------------
// Loads the low-res image from the texture
//-----------------------------------------------------------------------------
void CTexture : : LoadLowResTexture ( IVTFTexture * pTexture )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
delete [ ] m_pLowResImage ;
m_pLowResImage = NULL ;
if ( pTexture - > LowResWidth ( ) = = 0 | | pTexture - > LowResHeight ( ) = = 0 )
{
m_LowResImageWidth = m_LowResImageHeight = 0 ;
return ;
}
m_LowResImageWidth = pTexture - > LowResWidth ( ) ;
m_LowResImageHeight = pTexture - > LowResHeight ( ) ;
m_pLowResImage = new unsigned char [ m_LowResImageWidth * m_LowResImageHeight * 3 ] ;
# ifdef DBGFLAG_ASSERT
bool retVal =
# endif
ImageLoader : : ConvertImageFormat ( pTexture - > LowResImageData ( ) , pTexture - > LowResFormat ( ) ,
m_pLowResImage , IMAGE_FORMAT_RGB888 , m_LowResImageWidth , m_LowResImageHeight ) ;
Assert ( retVal ) ;
}
void * CTexture : : GetResourceData ( uint32 eDataType , size_t * pnumBytes ) const
{
for ( DataChunk const * pDataChunk = m_arrDataChunks . Base ( ) ,
* pDataChunkEnd = pDataChunk + m_arrDataChunks . Count ( ) ;
pDataChunk < pDataChunkEnd ; + + pDataChunk )
{
if ( ( pDataChunk - > m_eType & ~ RSRCF_MASK ) = = eDataType )
{
if ( ( pDataChunk - > m_eType & RSRCF_HAS_NO_DATA_CHUNK ) = = 0 )
{
if ( pnumBytes )
* pnumBytes = pDataChunk - > m_numBytes ;
return pDataChunk - > m_pvData ;
}
else
{
if ( pnumBytes )
* pnumBytes = sizeof ( pDataChunk - > m_numBytes ) ;
return ( void * ) ( & pDataChunk - > m_numBytes ) ;
}
}
}
if ( pnumBytes )
pnumBytes = 0 ;
return NULL ;
}
# pragma pack(1)
struct DXTColBlock
{
unsigned short col0 ;
unsigned short col1 ;
// no bit fields - use bytes
unsigned char row [ 4 ] ;
} ;
struct DXTAlphaBlock3BitLinear
{
unsigned char alpha0 ;
unsigned char alpha1 ;
unsigned char stuff [ 6 ] ;
} ;
# pragma pack()
static void FillCompressedTextureWithSingleColor ( int red , int green , int blue , int alpha , unsigned char * pImageData ,
int width , int height , int depth , ImageFormat imageFormat )
{
Assert ( ( width < 4 ) | | ! ( width % 4 ) ) ;
Assert ( ( height < 4 ) | | ! ( height % 4 ) ) ;
Assert ( ( depth < 4 ) | | ! ( depth % 4 ) ) ;
if ( width < 4 & & width > 0 )
{
width = 4 ;
}
if ( height < 4 & & height > 0 )
{
height = 4 ;
}
if ( depth < 4 & & depth > 1 )
{
depth = 4 ;
}
int numBlocks = ( width * height ) > > 4 ;
numBlocks * = depth ;
DXTColBlock colorBlock ;
memset ( & colorBlock , 0 , sizeof ( colorBlock ) ) ;
( ( BGR565_t * ) & ( colorBlock . col0 ) ) - > Set ( red , green , blue ) ;
( ( BGR565_t * ) & ( colorBlock . col1 ) ) - > Set ( red , green , blue ) ;
switch ( imageFormat )
{
case IMAGE_FORMAT_DXT1 :
case IMAGE_FORMAT_ATI1N : // Invalid block data, but correct memory footprint
{
int i ;
for ( i = 0 ; i < numBlocks ; i + + )
{
memcpy ( pImageData + i * 8 , & colorBlock , sizeof ( colorBlock ) ) ;
}
}
break ;
case IMAGE_FORMAT_DXT5 :
case IMAGE_FORMAT_ATI2N :
{
int i ;
for ( i = 0 ; i < numBlocks ; i + + )
{
// memset( pImageData + i * 16, 0, 16 );
memcpy ( pImageData + i * 16 + 8 , & colorBlock , sizeof ( colorBlock ) ) ;
// memset( pImageData + i * 16 + 8, 0xffff, 8 ); // alpha block
}
}
break ;
default :
Assert ( 0 ) ;
break ;
}
}
// This table starts out like the programmatic logic that used to be here,
// but then has some other colors, so that we don't see repeats.
// Also, there is no black, which seems to be an error condition on OpenGL.
// There also aren't any zeros in this table, since these colors may get
// multiplied with, say, vertex colors which are tinted, resulting in black pixels.
int sg_nMipLevelColors [ 14 ] [ 3 ] = { { 64 , 255 , 64 } , // Green
{ 255 , 64 , 64 } , // Red
{ 255 , 255 , 64 } , // Yellow
{ 64 , 64 , 255 } , // Blue
{ 64 , 255 , 255 } , // Cyan
{ 255 , 64 , 255 } , // Magenta
{ 255 , 255 , 255 } , // White
{ 255 , 150 , 150 } , // Light Red
{ 255 , 255 , 150 } , // Light Yellow
{ 150 , 150 , 255 } , // Light Blue
{ 150 , 255 , 255 } , // Light Cyan
{ 255 , 150 , 255 } , // Light Magenta
{ 150 , 150 , 128 } , // Light Gray
{ 138 , 131 , 64 } } ; // Brown
//-----------------------------------------------------------------------------
// Generate a texture that shows the various mip levels
//-----------------------------------------------------------------------------
void CTexture : : GenerateShowMipLevelsTextures ( IVTFTexture * pTexture )
{
if ( pTexture - > FaceCount ( ) > 1 )
return ;
switch ( pTexture - > Format ( ) )
{
// These are formats that we don't bother with for generating mip level textures.
case IMAGE_FORMAT_RGBA16161616F :
case IMAGE_FORMAT_R32F :
case IMAGE_FORMAT_RGB323232F :
case IMAGE_FORMAT_RGBA32323232F :
case IMAGE_FORMAT_UV88 :
break ;
default :
for ( int iFrame = 0 ; iFrame < pTexture - > FrameCount ( ) ; + + iFrame )
{
for ( int iFace = 0 ; iFace < pTexture - > FaceCount ( ) ; + + iFace )
{
for ( int iMip = 0 ; iMip < pTexture - > MipCount ( ) ; + + iMip )
{
int red = sg_nMipLevelColors [ iMip ] [ 0 ] ; //( ( iMip + 1 ) & 2 ) ? 255 : 0;
int green = sg_nMipLevelColors [ iMip ] [ 1 ] ; //( ( iMip + 1 ) & 1 ) ? 255 : 0;
int blue = sg_nMipLevelColors [ iMip ] [ 2 ] ; //( ( iMip + 1 ) & 4 ) ? 255 : 0;
int nWidth , nHeight , nDepth ;
pTexture - > ComputeMipLevelDimensions ( iMip , & nWidth , & nHeight , & nDepth ) ;
if ( pTexture - > Format ( ) = = IMAGE_FORMAT_DXT1 | | pTexture - > Format ( ) = = IMAGE_FORMAT_DXT5 | |
pTexture - > Format ( ) = = IMAGE_FORMAT_ATI1N | | pTexture - > Format ( ) = = IMAGE_FORMAT_ATI2N )
{
unsigned char * pImageData = pTexture - > ImageData ( iFrame , iFace , iMip , 0 , 0 , 0 ) ;
int alpha = 255 ;
FillCompressedTextureWithSingleColor ( red , green , blue , alpha , pImageData , nWidth , nHeight , nDepth , pTexture - > Format ( ) ) ;
}
else
{
for ( int z = 0 ; z < nDepth ; + + z )
{
CPixelWriter pixelWriter ;
pixelWriter . SetPixelMemory ( pTexture - > Format ( ) ,
pTexture - > ImageData ( iFrame , iFace , iMip , 0 , 0 , z ) , pTexture - > RowSizeInBytes ( iMip ) ) ;
for ( int y = 0 ; y < nHeight ; + + y )
{
pixelWriter . Seek ( 0 , y ) ;
for ( int x = 0 ; x < nWidth ; + + x )
{
pixelWriter . WritePixel ( red , green , blue , 255 ) ;
}
}
}
}
}
}
}
break ;
}
}
//-----------------------------------------------------------------------------
// Generate a texture that shows the various mip levels
//-----------------------------------------------------------------------------
void CTexture : : CopyLowResImageToTexture ( IVTFTexture * pTexture )
{
int nFlags = pTexture - > Flags ( ) ;
nFlags | = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_POINTSAMPLE ;
nFlags & = ~ ( TEXTUREFLAGS_TRILINEAR | TEXTUREFLAGS_ANISOTROPIC | TEXTUREFLAGS_HINT_DXT5 ) ;
nFlags & = ~ ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_ENVMAP ) ;
nFlags & = ~ ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) ;
Assert ( pTexture - > FrameCount ( ) = = 1 ) ;
Init ( pTexture - > Width ( ) , pTexture - > Height ( ) , 1 , IMAGE_FORMAT_BGR888 , nFlags , 1 ) ;
pTexture - > Init ( m_LowResImageWidth , m_LowResImageHeight , 1 , IMAGE_FORMAT_BGR888 , nFlags , 1 ) ;
// Don't bother computing the actual size; it's actually equal to the low-res size
// With only one mip level
m_dimsActual . m_nWidth = m_LowResImageWidth ;
m_dimsActual . m_nHeight = m_LowResImageHeight ;
m_dimsActual . m_nDepth = 1 ;
m_dimsActual . m_nMipCount = 1 ;
// Copy the row-res image into the VTF Texture
CPixelWriter pixelWriter ;
pixelWriter . SetPixelMemory ( pTexture - > Format ( ) ,
pTexture - > ImageData ( 0 , 0 , 0 ) , pTexture - > RowSizeInBytes ( 0 ) ) ;
for ( int y = 0 ; y < m_LowResImageHeight ; + + y )
{
pixelWriter . Seek ( 0 , y ) ;
for ( int x = 0 ; x < m_LowResImageWidth ; + + x )
{
int red = m_pLowResImage [ 0 ] ;
int green = m_pLowResImage [ 1 ] ;
int blue = m_pLowResImage [ 2 ] ;
m_pLowResImage + = 3 ;
pixelWriter . WritePixel ( red , green , blue , 255 ) ;
}
}
}
//-----------------------------------------------------------------------------
// Sets up debugging texture bits, if appropriate
//-----------------------------------------------------------------------------
bool CTexture : : SetupDebuggingTextures ( IVTFTexture * pVTFTexture )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
if ( pVTFTexture - > Flags ( ) & TEXTUREFLAGS_NODEBUGOVERRIDE )
return false ;
// The all mips flag is typically used on detail textures, which can
// really mess up visualization if we apply the debug-colorized
// versions of them to debug-colorized base textures, so skip 'em
if ( g_config . nShowMipLevels & & ! ( pVTFTexture - > Flags ( ) & TEXTUREFLAGS_ALL_MIPS ) )
{
// mat_showmiplevels 1 means don't do normal maps
if ( ( g_config . nShowMipLevels = = 1 ) & & ( pVTFTexture - > Flags ( ) & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_SSBUMP ) ) )
return false ;
// mat_showmiplevels 2 means don't do base textures
if ( ( g_config . nShowMipLevels = = 2 ) & & ! ( pVTFTexture - > Flags ( ) & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_SSBUMP ) ) )
return false ;
// This mode shows the mip levels as different colors
GenerateShowMipLevelsTextures ( pVTFTexture ) ;
return true ;
}
else if ( g_config . bShowLowResImage & & pVTFTexture - > FrameCount ( ) = = 1 & &
pVTFTexture - > FaceCount ( ) = = 1 & & ( ( pVTFTexture - > Flags ( ) & TEXTUREFLAGS_NORMAL ) = = 0 ) & &
m_LowResImageWidth ! = 0 & & m_LowResImageHeight ! = 0 )
{
// This mode just uses the low res texture
CopyLowResImageToTexture ( pVTFTexture ) ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Converts the texture to the actual format
// Returns true if conversion applied, false otherwise
//-----------------------------------------------------------------------------
bool CTexture : : ConvertToActualFormat ( IVTFTexture * pVTFTexture )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
if ( ! g_pShaderDevice - > IsUsingGraphics ( ) )
return false ;
bool bConverted = false ;
ImageFormat fmt = m_ImageFormat ;
ImageFormat dstFormat = ComputeActualFormat ( pVTFTexture - > Format ( ) ) ;
if ( fmt ! = dstFormat )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - conversion from (%d to %d) " , __FUNCTION__ , fmt , dstFormat ) ;
pVTFTexture - > ConvertImageFormat ( dstFormat , false ) ;
m_ImageFormat = dstFormat ;
bConverted = true ;
}
else if ( HardwareConfig ( ) - > GetHDRType ( ) = = HDR_TYPE_INTEGER & &
fmt = = dstFormat & & dstFormat = = IMAGE_FORMAT_RGBA16161616F )
{
// This is to force at most the precision of int16 for fp16 texture when running the integer path.
pVTFTexture - > ConvertImageFormat ( IMAGE_FORMAT_RGBA16161616 , false ) ;
pVTFTexture - > ConvertImageFormat ( IMAGE_FORMAT_RGBA16161616F , false ) ;
bConverted = true ;
}
return bConverted ;
}
void CTexture : : GetFilename ( char * pOut , int maxLen ) const
{
const char * pName = m_Name . String ( ) ;
bool bIsUNCName = ( pName [ 0 ] = = ' / ' & & pName [ 1 ] = = ' / ' & & pName [ 2 ] ! = ' / ' ) ;
if ( ! bIsUNCName )
{
Q_snprintf ( pOut , maxLen ,
" materials/%s " TEXTURE_FNAME_EXTENSION , pName ) ;
}
else
{
Q_snprintf ( pOut , maxLen , " %s " TEXTURE_FNAME_EXTENSION , pName ) ;
}
}
void CTexture : : ReloadFilesInList ( IFileList * pFilesToReload )
{
if ( IsProcedural ( ) | | IsRenderTarget ( ) )
return ;
char filename [ MAX_PATH ] ;
GetFilename ( filename , sizeof ( filename ) ) ;
if ( pFilesToReload - > IsFileInList ( filename ) )
{
Download ( ) ;
}
}
//-----------------------------------------------------------------------------
// Loads the texture bits from a file.
//-----------------------------------------------------------------------------
IVTFTexture * CTexture : : LoadTextureBitsFromFile ( char * pCacheFileName , char * * ppResolvedFilename )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s %s " , __FUNCTION__ , tmDynamicString ( TELEMETRY_LEVEL0 , pCacheFileName ) ) ;
if ( m_bStreamingFileReadFailed )
{
Assert ( m_pStreamingVTF = = NULL ) ;
return HandleFileLoadFailedTexture ( GetScratchVTFTexture ( ) ) ;
}
// OSX hackery
int nPreserveFlags = 0 ;
if ( m_nFlags & TEXTUREFLAGS_SRGB )
nPreserveFlags | = TEXTUREFLAGS_SRGB ;
unsigned int stripFlags = 0 ;
IVTFTexture * pVTFTexture = m_pStreamingVTF ;
if ( ! pVTFTexture )
{
pVTFTexture = GetScratchVTFTexture ( ) ;
FileHandle_t fileHandle = FILESYSTEM_INVALID_HANDLE ;
if ( ! GetFileHandle ( & fileHandle , pCacheFileName , ppResolvedFilename ) )
return HandleFileLoadFailedTexture ( pVTFTexture ) ;
TextureLODControlSettings_t settings = m_cachedFileLodSettings ;
if ( ! SLoadTextureBitsFromFile ( & pVTFTexture , fileHandle , m_nFlags | nPreserveFlags , & settings , m_nDesiredDimensionLimit , & m_nStreamingMips , GetName ( ) , pCacheFileName , & m_dimsMapping , & m_dimsActual , & m_dimsAllocated , & stripFlags ) )
{
g_pFullFileSystem - > Close ( fileHandle ) ;
return HandleFileLoadFailedTexture ( pVTFTexture ) ;
}
g_pFullFileSystem - > Close ( fileHandle ) ;
}
// Don't reinitialize here if we're streaming in the fine levels, we already have been initialized with coarse.
if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE ) = = 0 )
{
// Initing resets these, but we're happy with the values now--so store and restore them around the Init call.
TexDimensions_t actual = m_dimsActual ,
allocated = m_dimsAllocated ;
// Initialize the texture class with vtf header data before operations
Init ( m_dimsMapping . m_nWidth ,
m_dimsMapping . m_nHeight ,
m_dimsMapping . m_nDepth ,
pVTFTexture - > Format ( ) ,
pVTFTexture - > Flags ( ) | nPreserveFlags ,
pVTFTexture - > FrameCount ( )
) ;
m_dimsActual = actual ;
m_dimsAllocated = allocated ;
m_nFlags & = ~ stripFlags ;
}
else
{
// Not illegal, just needs investigation.
Assert ( stripFlags = = 0 ) ;
}
if ( m_pStreamingVTF )
ComputeActualSize ( false , pVTFTexture , ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) ! = 0 ) ;
VectorCopy ( pVTFTexture - > Reflectivity ( ) , m_vecReflectivity ) ;
// If we've only streamed in coarse but haven't started on fine yet, go ahead and mark us as
// partially resident and set up our clamping values.
if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) = = TEXTUREFLAGS_STREAMABLE_COARSE )
{
pVTFTexture - > GetMipmapRange ( & m_lodClamp , NULL ) ;
m_residenceTarget = RESIDENT_PARTIAL ;
m_residenceCurrent = RESIDENT_PARTIAL ;
}
// Build the low-res texture
LoadLowResTexture ( pVTFTexture ) ;
// Load the resources
if ( unsigned int uiRsrcCount = pVTFTexture - > GetResourceTypes ( NULL , 0 ) )
{
uint32 * arrRsrcTypes = ( uint32 * ) _alloca ( uiRsrcCount * sizeof ( unsigned int ) ) ;
pVTFTexture - > GetResourceTypes ( arrRsrcTypes , uiRsrcCount ) ;
m_arrDataChunks . EnsureCapacity ( uiRsrcCount ) ;
for ( uint32 * arrRsrcTypesEnd = arrRsrcTypes + uiRsrcCount ;
arrRsrcTypes < arrRsrcTypesEnd ; + + arrRsrcTypes )
{
switch ( * arrRsrcTypes )
{
case VTF_LEGACY_RSRC_LOW_RES_IMAGE :
case VTF_LEGACY_RSRC_IMAGE :
// These stock types use specific load routines
continue ;
default :
{
DataChunk dc ;
dc . m_eType = * arrRsrcTypes ;
dc . m_eType & = ~ RSRCF_MASK ;
size_t numBytes ;
if ( void * pvData = pVTFTexture - > GetResourceData ( dc . m_eType , & numBytes ) )
{
Assert ( numBytes > = sizeof ( uint32 ) ) ;
if ( numBytes = = sizeof ( dc . m_numBytes ) )
{
dc . m_eType | = RSRCF_HAS_NO_DATA_CHUNK ;
dc . m_pvData = NULL ;
memcpy ( & dc . m_numBytes , pvData , numBytes ) ;
}
else
{
dc . Allocate ( numBytes ) ;
memcpy ( dc . m_pvData , pvData , numBytes ) ;
}
m_arrDataChunks . AddToTail ( dc ) ;
}
}
}
}
}
// Try to set up debugging textures, if we're in a debugging mode
if ( ! IsProcedural ( ) )
SetupDebuggingTextures ( pVTFTexture ) ;
if ( ConvertToActualFormat ( pVTFTexture ) )
pVTFTexture ; // STAGING_ONLY_EXEC ( Warning( "\"%s\" not in final format, this is causing stutters or load time bloat!\n", pCacheFileName ) );
return pVTFTexture ;
}
IVTFTexture * CTexture : : HandleFileLoadFailedTexture ( IVTFTexture * pVTFTexture )
{
// create the error texture
// This will make a checkerboard texture to indicate failure
pVTFTexture - > Init ( 32 , 32 , 1 , IMAGE_FORMAT_BGRA8888 , m_nFlags , 1 ) ;
Init ( pVTFTexture - > Width ( ) , pVTFTexture - > Height ( ) , pVTFTexture - > Depth ( ) , pVTFTexture - > Format ( ) ,
pVTFTexture - > Flags ( ) , pVTFTexture - > FrameCount ( ) ) ;
m_vecReflectivity . Init ( 0.5f , 0.5f , 0.5f ) ;
// NOTE: For mat_picmip to work, we must use the same size (32x32)
// Which should work since every card can handle textures of that size
m_dimsAllocated . m_nWidth = m_dimsActual . m_nWidth = pVTFTexture - > Width ( ) ;
m_dimsAllocated . m_nHeight = m_dimsActual . m_nHeight = pVTFTexture - > Height ( ) ;
m_dimsAllocated . m_nDepth = 1 ;
m_dimsAllocated . m_nMipCount = m_dimsActual . m_nMipCount = 1 ;
m_nStreamingMips = 0 ;
// generate the checkerboard
TextureManager ( ) - > GenerateErrorTexture ( this , pVTFTexture ) ;
ConvertToActualFormat ( pVTFTexture ) ;
// Deactivate procedural texture...
m_nFlags & = ~ TEXTUREFLAGS_PROCEDURAL ;
SetErrorTexture ( true ) ;
return pVTFTexture ;
}
//-----------------------------------------------------------------------------
// Computes subrect for a particular miplevel
//-----------------------------------------------------------------------------
void CTexture : : ComputeMipLevelSubRect ( const Rect_t * pSrcRect , int nMipLevel , Rect_t * pSubRect )
{
if ( nMipLevel = = 0 )
{
* pSubRect = * pSrcRect ;
return ;
}
float flInvShrink = 1.0f / ( float ) ( 1 < < nMipLevel ) ;
pSubRect - > x = pSrcRect - > x * flInvShrink ;
pSubRect - > y = pSrcRect - > y * flInvShrink ;
pSubRect - > width = ( int ) ceil ( ( pSrcRect - > x + pSrcRect - > width ) * flInvShrink ) - pSubRect - > x ;
pSubRect - > height = ( int ) ceil ( ( pSrcRect - > y + pSrcRect - > height ) * flInvShrink ) - pSubRect - > y ;
}
//-----------------------------------------------------------------------------
// Computes the face count + first face
//-----------------------------------------------------------------------------
void CTexture : : GetDownloadFaceCount ( int & nFirstFace , int & nFaceCount )
{
nFaceCount = 1 ;
nFirstFace = 0 ;
if ( IsCubeMap ( ) )
{
if ( HardwareConfig ( ) - > SupportsCubeMaps ( ) )
{
nFaceCount = CUBEMAP_FACE_COUNT - 1 ;
}
else
{
// This will cause us to use the spheremap instead of the cube faces
// in the case where we don't support cubemaps
nFirstFace = CUBEMAP_FACE_SPHEREMAP ;
}
}
}
//-----------------------------------------------------------------------------
// Fixup a queue loaded texture with the delayed hi-res data
//-----------------------------------------------------------------------------
void CTexture : : FixupTexture ( const void * pData , int nSize , LoaderError_t loaderError )
{
if ( loaderError ! = LOADERERROR_NONE )
{
// mark as invalid
nSize = 0 ;
}
m_nInternalFlags & = ~ TEXTUREFLAGSINTERNAL_QUEUEDLOAD ;
// Make sure we've actually allocated the texture handles
Assert ( HasBeenAllocated ( ) ) ;
}
static void QueuedLoaderCallback ( void * pContext , void * pContext2 , const void * pData , int nSize , LoaderError_t loaderError )
{
reinterpret_cast < CTexture * > ( pContext ) - > FixupTexture ( pData , nSize , loaderError ) ;
}
//-----------------------------------------------------------------------------
// Generates the procedural bits
//-----------------------------------------------------------------------------
IVTFTexture * CTexture : : ReconstructPartialProceduralBits ( const Rect_t * pRect , Rect_t * pActualRect )
{
// Figure out the actual size for this texture based on the current mode
bool bIgnorePicmip = ( m_nFlags & ( TEXTUREFLAGS_STAGING_MEMORY | TEXTUREFLAGS_IGNORE_PICMIP ) ) ! = 0 ;
ComputeActualSize ( bIgnorePicmip ) ;
// Figure out how many mip levels we're skipping...
int nSizeFactor = 1 ;
int nWidth = GetActualWidth ( ) ;
if ( nWidth ! = 0 )
{
nSizeFactor = GetMappingWidth ( ) / nWidth ;
}
int nMipSkipCount = 0 ;
while ( nSizeFactor > 1 )
{
nSizeFactor > > = 1 ;
+ + nMipSkipCount ;
}
// Determine a rectangle appropriate for the actual size...
// It must bound all partially-covered pixels..
ComputeMipLevelSubRect ( pRect , nMipSkipCount , pActualRect ) ;
// Create the texture
IVTFTexture * pVTFTexture = GetScratchVTFTexture ( ) ;
// Initialize the texture
pVTFTexture - > Init ( m_dimsActual . m_nWidth , m_dimsActual . m_nHeight , m_dimsActual . m_nDepth ,
ComputeActualFormat ( m_ImageFormat ) , m_nFlags , m_nFrameCount ) ;
// Generate the bits from the installed procedural regenerator
if ( m_pTextureRegenerator )
{
m_pTextureRegenerator - > RegenerateTextureBits ( this , pVTFTexture , pActualRect ) ;
}
else
{
// In this case, we don't have one, so just use a checkerboard...
TextureManager ( ) - > GenerateErrorTexture ( this , pVTFTexture ) ;
}
return pVTFTexture ;
}
//-----------------------------------------------------------------------------
// Regenerates the bits of a texture within a particular rectangle
//-----------------------------------------------------------------------------
void CTexture : : ReconstructPartialTexture ( const Rect_t * pRect )
{
// FIXME: for now, only procedural textures can handle sub-rect specification.
Assert ( IsProcedural ( ) ) ;
// Also, we need procedural textures that have only a single copy!!
// Otherwise this partial upload will not occur on all copies
Assert ( m_nFlags & TEXTUREFLAGS_SINGLECOPY ) ;
Rect_t vtfRect ;
IVTFTexture * pVTFTexture = ReconstructPartialProceduralBits ( pRect , & vtfRect ) ;
// FIXME: for now, depth textures do not work with this.
Assert ( pVTFTexture - > Depth ( ) = = 1 ) ;
// Make sure we've allocated the API textures
if ( ! HasBeenAllocated ( ) )
{
if ( ! AllocateShaderAPITextures ( ) )
return ;
}
int nFaceCount , nFirstFace ;
GetDownloadFaceCount ( nFirstFace , nFaceCount ) ;
// Blit down portions of the various VTF frames into the board memory
int nStride ;
Rect_t mipRect ;
for ( int iFrame = 0 ; iFrame < m_nFrameCount ; + + iFrame )
{
Modify ( iFrame ) ;
for ( int iFace = 0 ; iFace < nFaceCount ; + + iFace )
{
for ( int iMip = 0 ; iMip < m_dimsActual . m_nMipCount ; + + iMip )
{
pVTFTexture - > ComputeMipLevelSubRect ( & vtfRect , iMip , & mipRect ) ;
nStride = pVTFTexture - > RowSizeInBytes ( iMip ) ;
unsigned char * pBits = pVTFTexture - > ImageData ( iFrame , iFace + nFirstFace , iMip , mipRect . x , mipRect . y , 0 ) ;
g_pShaderAPI - > TexSubImage2D (
iMip ,
iFace ,
mipRect . x ,
mipRect . y ,
0 ,
mipRect . width ,
mipRect . height ,
pVTFTexture - > Format ( ) ,
nStride ,
false ,
pBits ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Generates the procedural bits
//-----------------------------------------------------------------------------
IVTFTexture * CTexture : : ReconstructProceduralBits ( )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// Figure out the actual size for this texture based on the current mode
bool bIgnorePicmip = ( m_nFlags & ( TEXTUREFLAGS_STAGING_MEMORY | TEXTUREFLAGS_IGNORE_PICMIP ) ) ! = 0 ;
ComputeActualSize ( bIgnorePicmip ) ;
// Create the texture
IVTFTexture * pVTFTexture = NULL ;
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - GetScratchVTFTexture " , __FUNCTION__ ) ;
pVTFTexture = GetScratchVTFTexture ( ) ;
}
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Init " , __FUNCTION__ ) ;
// Initialize the texture
pVTFTexture - > Init ( m_dimsActual . m_nWidth , m_dimsActual . m_nHeight , m_dimsActual . m_nDepth ,
ComputeActualFormat ( m_ImageFormat ) , m_nFlags , m_nFrameCount ) ;
}
// Generate the bits from the installed procedural regenerator
if ( m_pTextureRegenerator )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - RegenerateTextureBits " , __FUNCTION__ ) ;
Rect_t rect ;
rect . x = 0 ; rect . y = 0 ;
rect . width = m_dimsActual . m_nWidth ;
rect . height = m_dimsActual . m_nHeight ;
m_pTextureRegenerator - > RegenerateTextureBits ( this , pVTFTexture , & rect ) ;
}
else
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - GenerateErrorTexture " , __FUNCTION__ ) ;
// In this case, we don't have one, so just use a checkerboard...
TextureManager ( ) - > GenerateErrorTexture ( this , pVTFTexture ) ;
}
return pVTFTexture ;
}
void CTexture : : WriteDataToShaderAPITexture ( int nFrameCount , int nFaceCount , int nFirstFace , int nMipCount , IVTFTexture * pVTFTexture , ImageFormat fmt )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// If we're a staging texture, there's nothing to do.
if ( ( m_nFlags & TEXTUREFLAGS_STAGING_MEMORY ) ! = 0 )
return ;
for ( int iFrame = 0 ; iFrame < m_nFrameCount ; + + iFrame )
{
Modify ( iFrame ) ;
g_pShaderAPI - > TexImageFromVTF ( pVTFTexture , iFrame ) ;
}
}
bool CTexture : : IsDepthTextureFormat ( ImageFormat fmt )
{
return ( ( m_ImageFormat = = IMAGE_FORMAT_NV_DST16 ) | |
( m_ImageFormat = = IMAGE_FORMAT_NV_DST24 ) | |
( m_ImageFormat = = IMAGE_FORMAT_NV_INTZ ) | |
( m_ImageFormat = = IMAGE_FORMAT_NV_RAWZ ) | |
( m_ImageFormat = = IMAGE_FORMAT_ATI_DST16 ) | |
( m_ImageFormat = = IMAGE_FORMAT_ATI_DST24 ) ) ;
}
//-----------------------------------------------------------------------------
void CTexture : : NotifyUnloadedFile ( )
{
// Make sure we have a regular texture that was loaded from a file
if ( IsProcedural ( ) | | IsRenderTarget ( ) | | ! m_Name . IsValid ( ) )
return ;
const char * pName = m_Name . String ( ) ;
if ( * pName = = ' \0 ' )
return ;
bool bIsUNCName = ( pName [ 0 ] = = ' / ' & & pName [ 1 ] = = ' / ' & & pName [ 2 ] ! = ' / ' ) ;
if ( bIsUNCName )
return ;
// Generate the filename
char pCacheFileName [ MATERIAL_MAX_PATH ] ;
Q_snprintf ( pCacheFileName , sizeof ( pCacheFileName ) , " materials/%s " TEXTURE_FNAME_EXTENSION , pName ) ;
// Let filesystem know that the file is uncached, so it knows
// what to do with tracking info
g_pFullFileSystem - > NotifyFileUnloaded ( pCacheFileName , " GAME " ) ;
}
//-----------------------------------------------------------------------------
// Sets or updates the texture bits
//-----------------------------------------------------------------------------
void CTexture : : ReconstructTexture ( bool bCopyFromCurrent )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
Assert ( ! bCopyFromCurrent | | HardwareConfig ( ) - > CanStretchRectFromTextures ( ) ) ;
int oldWidth = m_dimsAllocated . m_nWidth ;
int oldHeight = m_dimsAllocated . m_nHeight ;
int oldDepth = m_dimsAllocated . m_nDepth ;
int oldMipCount = m_dimsAllocated . m_nMipCount ;
int oldFrameCount = m_nFrameCount ;
// FIXME: Should RenderTargets be a special case of Procedural?
char * pResolvedFilename = NULL ;
IVTFTexture * pVTFTexture = NULL ;
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Begin " , __FUNCTION__ ) ;
if ( IsProcedural ( ) )
{
// This will call the installed texture bit regeneration interface
pVTFTexture = ReconstructProceduralBits ( ) ;
}
else if ( IsRenderTarget ( ) )
{
// Compute the actual size + format based on the current mode
bool bIgnorePicmip = m_RenderTargetSizeMode ! = RT_SIZE_LITERAL_PICMIP ;
ComputeActualSize ( bIgnorePicmip ) ;
}
else if ( bCopyFromCurrent )
{
ComputeActualSize ( false , NULL , true ) ;
}
else
{
NotifyUnloadedFile ( ) ;
char pCacheFileName [ MATERIAL_MAX_PATH ] = { 0 } ;
GetCacheFilename ( pCacheFileName , ARRAYSIZE ( pCacheFileName ) ) ;
// Get the data from disk...
// NOTE: Reloading the texture bits can cause the texture size, frames, format, pretty much *anything* can change.
pVTFTexture = LoadTextureBitsFromFile ( pCacheFileName , & pResolvedFilename ) ;
}
}
if ( ! HasBeenAllocated ( ) | |
m_dimsAllocated . m_nWidth ! = oldWidth | |
m_dimsAllocated . m_nHeight ! = oldHeight | |
m_dimsAllocated . m_nDepth ! = oldDepth | |
m_dimsAllocated . m_nMipCount ! = oldMipCount | |
m_nFrameCount ! = oldFrameCount )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Allocation " , __FUNCTION__ ) ;
const bool cbCanStretchRectTextures = HardwareConfig ( ) - > CanStretchRectFromTextures ( ) ;
const bool cbShouldMigrateTextures = ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE ) ! = 0 ) & & m_nFrameCount = = oldFrameCount ;
// If we're just streaming in more data--or demoting ourselves, do a migration instead.
if ( bCopyFromCurrent | | ( cbCanStretchRectTextures & & cbShouldMigrateTextures ) )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Migration " , __FUNCTION__ ) ;
MigrateShaderAPITextures ( ) ;
// Ahh--I feel terrible about this, but we genuinely don't need anything else if we're streaming.
if ( bCopyFromCurrent )
return ;
}
else
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Deallocate / Allocate " , __FUNCTION__ ) ;
// If we're doing a wholesale copy, we need to restore these values that will be cleared by FreeShaderAPITextures.
// Record them here, restore them below.
unsigned int restoreStreamingFlag = ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) ;
ResidencyType_t restoreResidenceCurrent = m_residenceCurrent ;
ResidencyType_t restoreResidenceTarget = m_residenceTarget ;
if ( HasBeenAllocated ( ) )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Deallocate " , __FUNCTION__ ) ;
// This is necessary for the reload case, we may discover there
// are more frames of a texture animation, for example, which means
// we can't rely on having the same number of texture frames.
FreeShaderAPITextures ( ) ;
}
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Allocate " , __FUNCTION__ ) ;
// Create the shader api textures
if ( ! AllocateShaderAPITextures ( ) )
return ;
// Restored once we successfully allocate the shader api textures, but only if we're
//
if ( ! cbCanStretchRectTextures & & cbShouldMigrateTextures )
{
m_nFlags | = restoreStreamingFlag ;
m_residenceCurrent = restoreResidenceCurrent ;
m_residenceTarget = restoreResidenceTarget ;
}
}
}
else if ( bCopyFromCurrent )
{
Assert ( ! " We're about to crash, last chance to examine this texture. " ) ;
}
// Render Targets just need to be cleared, they have no upload
if ( IsRenderTarget ( ) )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - RT Stuff " , __FUNCTION__ ) ;
// Clear the render target to opaque black
// Only clear if we're not a depth-stencil texture
if ( ! IsDepthTextureFormat ( m_ImageFormat ) )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Clearing " , __FUNCTION__ ) ;
CMatRenderContextPtr pRenderContext ( MaterialSystem ( ) ) ;
ITexture * pThisTexture = GetEmbeddedTexture ( 0 ) ;
pRenderContext - > PushRenderTargetAndViewport ( pThisTexture ) ; // Push this texture on the stack
g_pShaderAPI - > ClearColor4ub ( 0 , 0 , 0 , 0xFF ) ; // Set the clear color to opaque black
g_pShaderAPI - > ClearBuffers ( true , false , false , m_dimsActual . m_nWidth , m_dimsActual . m_nHeight ) ; // Clear the target
pRenderContext - > PopRenderTargetAndViewport ( ) ; // Pop back to previous target
}
// no upload
return ;
}
// Blit down the texture faces, frames, and mips into the board memory
int nFirstFace , nFaceCount ;
GetDownloadFaceCount ( nFirstFace , nFaceCount ) ;
WriteDataToShaderAPITexture ( m_nFrameCount , nFaceCount , nFirstFace , m_dimsActual . m_nMipCount , pVTFTexture , m_ImageFormat ) ;
ReleaseScratchVTFTexture ( pVTFTexture ) ;
pVTFTexture = NULL ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - Final Cleanup " , __FUNCTION__ ) ;
// allocated by strdup
free ( pResolvedFilename ) ;
// the pc can afford to persist a large buffer
FreeOptimalReadBuffer ( 6 * 1024 * 1024 ) ;
}
void CTexture : : GetCacheFilename ( char * pOutBuffer , int nBufferSize ) const
{
Assert ( pOutBuffer ) ;
if ( IsProcedural ( ) | | IsRenderTarget ( ) )
{
pOutBuffer [ 0 ] = 0 ;
return ;
}
else
{
const char * pName ;
if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE )
{
pName = " dev/dev_exclude_error " ;
}
else
{
pName = m_Name . String ( ) ;
}
bool bIsUNCName = ( pName [ 0 ] = = ' / ' & & pName [ 1 ] = = ' / ' & & pName [ 2 ] ! = ' / ' ) ;
if ( ! bIsUNCName )
{
Q_snprintf ( pOutBuffer , nBufferSize , " materials/%s " TEXTURE_FNAME_EXTENSION , pName ) ;
}
else
{
Q_snprintf ( pOutBuffer , nBufferSize , " %s " TEXTURE_FNAME_EXTENSION , pName ) ;
}
}
}
bool CTexture : : GetFileHandle ( FileHandle_t * pOutFileHandle , char * pCacheFileName , char * * ppResolvedFilename ) const
{
Assert ( pOutFileHandle ) ;
FileHandle_t & fileHandle = * pOutFileHandle ;
fileHandle = FILESYSTEM_INVALID_HANDLE ;
while ( fileHandle = = FILESYSTEM_INVALID_HANDLE ) // run until found a file or out of rules
{
fileHandle = g_pFullFileSystem - > OpenEx ( pCacheFileName , " rb " , 0 , MaterialSystem ( ) - > GetForcedTextureLoadPathID ( ) , ppResolvedFilename ) ;
if ( fileHandle = = FILESYSTEM_INVALID_HANDLE )
{
// try any fallbacks.
char * pHdrExt = Q_stristr ( pCacheFileName , " .hdr " TEXTURE_FNAME_EXTENSION ) ;
if ( pHdrExt )
{
DevWarning ( " A custom HDR cubemap \" %s \" : cannot be found on disk. \n "
" This really should have a HDR version, trying a fall back to a non-HDR version. \n " , pCacheFileName ) ;
strcpy ( pHdrExt , TEXTURE_FNAME_EXTENSION ) ;
}
else
{
// no more fallbacks
break ;
}
}
}
if ( fileHandle = = FILESYSTEM_INVALID_HANDLE )
{
if ( Q_strnicmp ( m_Name . String ( ) , " env_cubemap " , 12 ) )
{
if ( IsPosix ( ) )
{
Msg ( " \n ##### CTexture::LoadTextureBitsFromFile couldn't find %s \n " , pCacheFileName ) ;
}
DevWarning ( " \" %s \" : can't be found on disk \n " , pCacheFileName ) ;
}
return false ;
}
return true ;
}
// Get the shaderapi texture handle associated w/ a particular frame
ShaderAPITextureHandle_t CTexture : : GetTextureHandle ( int nFrame , int nTextureChannel )
{
if ( nFrame < 0 )
{
nFrame = 0 ;
Warning ( " CTexture::GetTextureHandle(): nFrame is < 0! \n " ) ;
}
if ( nFrame > = m_nFrameCount )
{
// NOTE: This can happen during alt-tab. If you alt-tab while loading a level then the first local cubemap bind will do this, for example.
Assert ( nFrame < m_nFrameCount ) ;
return INVALID_SHADERAPI_TEXTURE_HANDLE ;
}
Assert ( nTextureChannel < 2 ) ;
// Make sure we've actually allocated the texture handles
Assert ( m_pTextureHandles ) ;
Assert ( HasBeenAllocated ( ) ) ;
if ( m_pTextureHandles = = NULL | | ! HasBeenAllocated ( ) )
{
return INVALID_SHADERAPI_TEXTURE_HANDLE ;
}
// Don't get paired handle here...callers of this function don't know about paired textures
return m_pTextureHandles [ nFrame ] ;
}
void CTexture : : GetLowResColorSample ( float s , float t , float * color ) const
{
if ( m_LowResImageWidth < = 0 | | m_LowResImageHeight < = 0 )
{
// Warning( "Programming error: GetLowResColorSample \"%s\": %dx%d\n", m_pName, ( int )m_LowResImageWidth, ( int )m_LowResImageHeight );
return ;
}
// force s and t into [0,1)
if ( s < 0.0f )
{
s = ( 1.0f - ( float ) ( int ) s ) + s ;
}
if ( t < 0.0f )
{
t = ( 1.0f - ( float ) ( int ) t ) + t ;
}
s = s - ( float ) ( int ) s ;
t = t - ( float ) ( int ) t ;
s * = m_LowResImageWidth ;
t * = m_LowResImageHeight ;
int wholeS , wholeT ;
wholeS = ( int ) s ;
wholeT = ( int ) t ;
float fracS , fracT ;
fracS = s - ( float ) ( int ) s ;
fracT = t - ( float ) ( int ) t ;
// filter twice in the s dimension.
float sColor [ 2 ] [ 3 ] ;
int wholeSPlusOne = ( wholeS + 1 ) % m_LowResImageWidth ;
int wholeTPlusOne = ( wholeT + 1 ) % m_LowResImageHeight ;
sColor [ 0 ] [ 0 ] = ( 1.0f - fracS ) * ( m_pLowResImage [ ( wholeS + wholeT * m_LowResImageWidth ) * 3 + 0 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 0 ] [ 1 ] = ( 1.0f - fracS ) * ( m_pLowResImage [ ( wholeS + wholeT * m_LowResImageWidth ) * 3 + 1 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 0 ] [ 2 ] = ( 1.0f - fracS ) * ( m_pLowResImage [ ( wholeS + wholeT * m_LowResImageWidth ) * 3 + 2 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 0 ] [ 0 ] + = fracS * ( m_pLowResImage [ ( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 0 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 0 ] [ 1 ] + = fracS * ( m_pLowResImage [ ( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 1 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 0 ] [ 2 ] + = fracS * ( m_pLowResImage [ ( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 2 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 1 ] [ 0 ] = ( 1.0f - fracS ) * ( m_pLowResImage [ ( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 0 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 1 ] [ 1 ] = ( 1.0f - fracS ) * ( m_pLowResImage [ ( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 1 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 1 ] [ 2 ] = ( 1.0f - fracS ) * ( m_pLowResImage [ ( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 2 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 1 ] [ 0 ] + = fracS * ( m_pLowResImage [ ( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 0 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 1 ] [ 1 ] + = fracS * ( m_pLowResImage [ ( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 1 ] * ( 1.0f / 255.0f ) ) ;
sColor [ 1 ] [ 2 ] + = fracS * ( m_pLowResImage [ ( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 2 ] * ( 1.0f / 255.0f ) ) ;
color [ 0 ] = sColor [ 0 ] [ 0 ] * ( 1.0f - fracT ) + sColor [ 1 ] [ 0 ] * fracT ;
color [ 1 ] = sColor [ 0 ] [ 1 ] * ( 1.0f - fracT ) + sColor [ 1 ] [ 1 ] * fracT ;
color [ 2 ] = sColor [ 0 ] [ 2 ] * ( 1.0f - fracT ) + sColor [ 1 ] [ 2 ] * fracT ;
}
int CTexture : : GetApproximateVidMemBytes ( void ) const
{
ImageFormat format = GetImageFormat ( ) ;
int width = GetActualWidth ( ) ;
int height = GetActualHeight ( ) ;
int depth = GetActualDepth ( ) ;
int numFrames = GetNumAnimationFrames ( ) ;
bool isMipmapped = IsMipmapped ( ) ;
return numFrames * ImageLoader : : GetMemRequired ( width , height , depth , format , isMipmapped ) ;
}
void CTexture : : CopyFrameBufferToMe ( int nRenderTargetID , Rect_t * pSrcRect , Rect_t * pDstRect )
{
Assert ( m_pTextureHandles & & m_nFrameCount > = 1 ) ;
if ( m_pTextureHandles & & m_nFrameCount > = 1 )
{
g_pShaderAPI - > CopyRenderTargetToTextureEx ( m_pTextureHandles [ 0 ] , nRenderTargetID , pSrcRect , pDstRect ) ;
}
}
void CTexture : : CopyMeToFrameBuffer ( int nRenderTargetID , Rect_t * pSrcRect , Rect_t * pDstRect )
{
Assert ( m_pTextureHandles & & m_nFrameCount > = 1 ) ;
if ( m_pTextureHandles & & m_nFrameCount > = 1 )
{
g_pShaderAPI - > CopyTextureToRenderTargetEx ( nRenderTargetID , m_pTextureHandles [ 0 ] , pSrcRect , pDstRect ) ;
}
}
ITexture * CTexture : : GetEmbeddedTexture ( int nIndex )
{
return ( nIndex = = 0 ) ? this : NULL ;
}
void CTexture : : DeleteIfUnreferenced ( )
{
if ( m_nRefCount > 0 )
return ;
if ( ThreadInMainThread ( ) )
{
// Render thread better not be active or bad things can happen.
2022-06-05 06:44:42 +08:00
Assert ( MaterialSystem ( ) - > GetRenderThreadId ( ) = = ( uintp ) - 1 ) ;
2020-04-23 00:56:21 +08:00
TextureManager ( ) - > RemoveTexture ( this ) ;
return ;
}
// Can't actually clean up from render thread--just safely mark this texture as
// one we should check for cleanup next EndFrame when it's safe.
TextureManager ( ) - > MarkUnreferencedTextureForCleanup ( this ) ;
}
//Swap everything about a texture except the name. Created to support Portal mod's need for swapping out water render targets in recursive stencil views
void CTexture : : SwapContents ( ITexture * pOther )
{
if ( ( pOther = = NULL ) | | ( pOther = = this ) )
return ;
ICallQueue * pCallQueue = materials - > GetRenderContext ( ) - > GetCallQueue ( ) ;
if ( pCallQueue )
{
pCallQueue - > QueueCall ( this , & CTexture : : SwapContents , pOther ) ;
return ;
}
AssertMsg ( dynamic_cast < CTexture * > ( pOther ) ! = NULL , " Texture swapping broken " ) ;
CTexture * pOtherAsCTexture = ( CTexture * ) pOther ;
CTexture * pTemp = ( CTexture * ) stackalloc ( sizeof ( CTexture ) ) ;
//swap everything. Note that this copies the entire object including the
// vtable pointer, thus ruining polymorphism. Use with care.
// The unnecessary casts to (void*) hint to clang that we know what we
// are doing.
memcpy ( ( void * ) pTemp , ( const void * ) this , sizeof ( CTexture ) ) ;
memcpy ( ( void * ) this , ( const void * ) pOtherAsCTexture , sizeof ( CTexture ) ) ;
memcpy ( ( void * ) pOtherAsCTexture , ( const void * ) pTemp , sizeof ( CTexture ) ) ;
//we have the other's name, give it back
memcpy ( & pOtherAsCTexture - > m_Name , & m_Name , sizeof ( m_Name ) ) ;
//pTemp still has our name
memcpy ( & m_Name , & pTemp - > m_Name , sizeof ( m_Name ) ) ;
}
void CTexture : : MarkAsPreloaded ( bool bSet )
{
if ( bSet )
{
m_nInternalFlags | = TEXTUREFLAGSINTERNAL_PRELOADED ;
}
else
{
m_nInternalFlags & = ~ TEXTUREFLAGSINTERNAL_PRELOADED ;
}
}
bool CTexture : : IsPreloaded ( ) const
{
return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_PRELOADED ) ! = 0 ) ;
}
void CTexture : : MarkAsExcluded ( bool bSet , int nDimensionsLimit )
{
if ( bSet )
{
// exclusion trumps picmipping
m_nInternalFlags | = TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE ;
m_nDesiredDimensionLimit = 0 ;
}
else
{
// not excluding, but can optionally picmip
m_nInternalFlags & = ~ TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE ;
m_nDesiredDimensionLimit = nDimensionsLimit ;
}
}
bool CTexture : : UpdateExcludedState ( void )
{
bool bDesired = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE ) ! = 0 ;
bool bActual = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_EXCLUDED ) ! = 0 ;
if ( ( bDesired = = bActual ) & & ( m_nDesiredDimensionLimit = = m_nActualDimensionLimit ) )
{
return false ;
}
if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD )
{
// already scheduled
return true ;
}
// force the texture to re-download, causes the texture bits to match its desired exclusion state
Download ( ) ;
return true ;
}
void CTextureStreamingJob : : OnAsyncFindComplete ( ITexture * pTex , void * pExtraArgs )
{
2022-02-23 19:50:30 +08:00
const intp cArgsAsInt = ( intp ) pExtraArgs ;
2020-04-23 00:56:21 +08:00
Assert ( m_pOwner = = NULL | | m_pOwner = = pTex ) ;
if ( m_pOwner )
m_pOwner - > OnStreamingJobComplete ( static_cast < ResidencyType_t > ( cArgsAsInt ) ) ;
// OnStreamingJobComplete should've cleaned us up
Assert ( m_pOwner = = NULL ) ;
}
// ------------------------------------------------------------------------------------------------
int GetThreadId ( )
{
TM_ZONE_DEFAULT ( TELEMETRY_LEVEL0 ) ;
// Turns the current thread into a 0-based index for use in accessing statics in this file.
int retVal = INT_MAX ;
if ( ThreadInMainThread ( ) )
retVal = 0 ;
else if ( MaterialSystem ( ) - > GetRenderThreadId ( ) = = ThreadGetCurrentId ( ) )
retVal = 1 ;
else if ( TextureManager ( ) - > ThreadInAsyncLoadThread ( ) )
retVal = 2 ;
else if ( TextureManager ( ) - > ThreadInAsyncReadThread ( ) )
retVal = 3 ;
else
{
STAGING_ONLY_EXEC ( AssertAlways ( ! " Unexpected thread in GetThreadId, need to debug this--crash is next. Tell McJohn. " ) ) ;
DebuggerBreakIfDebugging_StagingOnly ( ) ;
}
Assert ( retVal < MAX_RENDER_THREADS ) ;
return retVal ;
}
// ------------------------------------------------------------------------------------------------
bool SLoadTextureBitsFromFile ( IVTFTexture * * ppOutVtfTexture , FileHandle_t hFile , unsigned int nFlags ,
TextureLODControlSettings_t * pInOutCachedFileLodSettings ,
int nDesiredDimensionLimit , unsigned short * pOutStreamedMips ,
const char * pName , const char * pCacheFileName ,
TexDimensions_t * pOptOutDimsMapping ,
TexDimensions_t * pOptOutDimsActual ,
TexDimensions_t * pOptOutDimsAllocated ,
unsigned int * pOptOutStripFlags )
{
// NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
// NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
// NOTE! NOTE! NOTE! or by the streaming texture code!
Assert ( ppOutVtfTexture ! = NULL & & * ppOutVtfTexture ! = NULL ) ;
CUtlBuffer buf ;
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - ReadHeaderFromFile " , __FUNCTION__ ) ;
int nHeaderSize = VTFFileHeaderSize ( VTF_MAJOR_VERSION ) ;
// restrict read to the header only!
// header provides info to avoid reading the entire file
int nBytesOptimalRead = GetOptimalReadBuffer ( & buf , hFile , nHeaderSize ) ;
int nBytesRead = g_pFullFileSystem - > ReadEx ( buf . Base ( ) , nBytesOptimalRead , Min ( nHeaderSize , ( int ) g_pFullFileSystem - > Size ( hFile ) ) , hFile ) ; // only read as much as the file has
buf . SeekPut ( CUtlBuffer : : SEEK_HEAD , nBytesRead ) ;
nBytesRead = nHeaderSize = ( ( VTFFileBaseHeader_t * ) buf . Base ( ) ) - > headerSize ;
g_pFullFileSystem - > Seek ( hFile , nHeaderSize , FILESYSTEM_SEEK_HEAD ) ;
}
// Unserialize the header only
// need the header first to determine remainder of data
if ( ! ( * ppOutVtfTexture ) - > Unserialize ( buf , true ) )
{
Warning ( " Error reading texture header \" %s \" \n " , pCacheFileName ) ;
return false ;
}
// Need to record this now, before we ask for the trimmed down data to potentially be loaded.
TexDimensions_t dimsMappingCurrent ( ( * ppOutVtfTexture ) - > Width ( ) , ( * ppOutVtfTexture ) - > Height ( ) , ( * ppOutVtfTexture ) - > MipCount ( ) , ( * ppOutVtfTexture ) - > Depth ( ) ) ;
if ( pOptOutDimsMapping )
( * pOptOutDimsMapping ) = dimsMappingCurrent ;
int nFullFlags = ( * ppOutVtfTexture ) - > Flags ( )
| nFlags ;
// Seek the reading back to the front of the buffer
buf . SeekGet ( CUtlBuffer : : SEEK_HEAD , 0 ) ;
// Compute the actual texture dimensions
int nMipSkipCount = ComputeMipSkipCount ( pName , dimsMappingCurrent , false , * ppOutVtfTexture , nFullFlags , nDesiredDimensionLimit , pOutStreamedMips , pInOutCachedFileLodSettings , pOptOutDimsActual , pOptOutDimsAllocated , pOptOutStripFlags ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s - ReadDataFromFile " , __FUNCTION__ ) ;
// Determine how much of the file to read in
int nFileSize = ( * ppOutVtfTexture ) - > FileSize ( nMipSkipCount ) ;
int nActualFileSize = ( int ) g_pFullFileSystem - > Size ( hFile ) ;
if ( nActualFileSize < nFileSize )
{
if ( mat_spew_on_texture_size . GetInt ( ) )
DevMsg ( " Bad VTF data for %s, expected file size:%d actual file size:%d \n " , pCacheFileName , nFileSize , nActualFileSize ) ;
nFileSize = nActualFileSize ;
}
// Read only the portion of the file that we care about
g_pFullFileSystem - > Seek ( hFile , 0 , FILESYSTEM_SEEK_HEAD ) ;
int nBytesOptimalRead = GetOptimalReadBuffer ( & buf , hFile , nFileSize ) ;
int nBytesRead = g_pFullFileSystem - > ReadEx ( buf . Base ( ) , nBytesOptimalRead , nFileSize , hFile ) ;
buf . SeekPut ( CUtlBuffer : : SEEK_HEAD , nBytesRead ) ;
// Some hardware doesn't support copying textures to other textures. For them, we need to reread the
// whole file, so if they are doing the final read (the fine levels) then reread everything by stripping
// off the flags we are trying to pass in.
unsigned int nForceFlags = nFullFlags & TEXTUREFLAGS_STREAMABLE ;
if ( ! HardwareConfig ( ) - > CanStretchRectFromTextures ( ) & & ( nForceFlags & TEXTUREFLAGS_STREAMABLE_FINE ) )
nForceFlags = 0 ;
// NOTE: Skipping mip levels here will cause the size to be changed
bool bRetVal = ( * ppOutVtfTexture ) - > UnserializeEx ( buf , false , nForceFlags , nMipSkipCount ) ;
FreeOptimalReadBuffer ( 6 * 1024 * 1024 ) ;
if ( ! bRetVal )
{
Warning ( " Error reading texture data \" %s \" \n " , pCacheFileName ) ;
}
return bRetVal ;
}
//-----------------------------------------------------------------------------
// Compute the actual mip count based on the actual size
//-----------------------------------------------------------------------------
int ComputeActualMipCount ( const TexDimensions_t & actualDims , unsigned int nFlags )
{
if ( nFlags & TEXTUREFLAGS_ENVMAP )
{
if ( ! HardwareConfig ( ) - > SupportsMipmappedCubemaps ( ) )
{
return 1 ;
}
}
if ( nFlags & TEXTUREFLAGS_NOMIP )
{
return 1 ;
}
// Unless ALLMIPS is set, we stop mips at 32x32
const int nMaxMipSize = 32 ;
// Clamp border textures on Posix to fix L4D2 flashlight cookie issue
# ifdef DX_TO_GL_ABSTRACTION
if ( ( false & & ! g_bForceTextureAllMips & & ! ( nFlags & TEXTUREFLAGS_ALL_MIPS ) ) | | ( true & & ( nFlags & TEXTUREFLAGS_BORDER ) ) )
# else
if ( ( true & & ! g_bForceTextureAllMips & & ! ( nFlags & TEXTUREFLAGS_ALL_MIPS ) ) | | ( false & & ( nFlags & TEXTUREFLAGS_BORDER ) ) )
# endif
{
int nNumMipLevels = 1 ;
int h = actualDims . m_nWidth ;
int w = actualDims . m_nHeight ;
while ( MIN ( w , h ) > nMaxMipSize )
{
+ + nNumMipLevels ;
w > > = 1 ;
h > > = 1 ;
}
return nNumMipLevels ;
}
return ImageLoader : : GetNumMipMapLevels ( actualDims . m_nWidth , actualDims . m_nHeight , actualDims . m_nDepth ) ;
}
// ------------------------------------------------------------------------------------------------
int ComputeMipSkipCount ( const char * pName , const TexDimensions_t & mappingDims , bool bIgnorePicmip , IVTFTexture * pOptVTFTexture , unsigned int nFlags , int nDesiredDimensionLimit , unsigned short * pOutStreamedMips , TextureLODControlSettings_t * pInOutCachedFileLodSettings , TexDimensions_t * pOptOutActualDims , TexDimensions_t * pOptOutAllocatedDims , unsigned int * pOptOutStripFlags )
{
// NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
// NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
// NOTE! NOTE! NOTE! or by the streaming texture code!
Assert ( pName ! = NULL ) ;
Assert ( pOutStreamedMips ! = NULL ) ;
Assert ( pInOutCachedFileLodSettings ! = NULL ) ;
TexDimensions_t actualDims = mappingDims ,
allocatedDims ;
const bool bTextureMigration = ( nFlags & TEXTUREFLAGS_STREAMABLE ) ! = 0 ;
unsigned int stripFlags = 0 ;
int nClampX = actualDims . m_nWidth ; // no clamping (clamp to texture dimensions)
int nClampY = actualDims . m_nHeight ;
int nClampZ = actualDims . m_nDepth ;
// Fetch LOD settings from the VTF if available
TextureLODControlSettings_t lcs ;
memset ( & lcs , 0 , sizeof ( lcs ) ) ;
TextureLODControlSettings_t const * pLODInfo = NULL ;
if ( pOptVTFTexture )
{
pLODInfo = reinterpret_cast < TextureLODControlSettings_t const * > (
pOptVTFTexture - > GetResourceData ( VTF_RSRC_TEXTURE_LOD_SETTINGS , NULL ) ) ;
// Texture streaming means there are times we call this where we don't have a VTFTexture, even though
// we're a file. So we need to store off the LOD settings whenever we get in here with a file that has them
// so that we can use the correct values for when we don't. Otherwise, the texture will be confused about
// what size to use and everything will die a horrible, horrible death.
if ( pLODInfo )
( * pInOutCachedFileLodSettings ) = ( * pLODInfo ) ;
}
else if ( bTextureMigration )
{
pLODInfo = pInOutCachedFileLodSettings ;
}
if ( pLODInfo )
lcs = * pLODInfo ;
// Prepare the default LOD settings (that essentially result in no clamping)
TextureLODControlSettings_t default_lod_settings ;
memset ( & default_lod_settings , 0 , sizeof ( default_lod_settings ) ) ;
{
for ( int w = actualDims . m_nWidth ; w > 1 ; w > > = 1 )
+ + default_lod_settings . m_ResolutionClampX ;
for ( int h = actualDims . m_nHeight ; h > 1 ; h > > = 1 )
+ + default_lod_settings . m_ResolutionClampY ;
}
// Check for LOD control override
{
TextureLodOverride : : OverrideInfo oi = TextureLodOverride : : Get ( pName ) ;
if ( oi . x & & oi . y & & ! pLODInfo ) // If overriding texture that doesn't have lod info yet, then use default
lcs = default_lod_settings ;
lcs . m_ResolutionClampX + = oi . x ;
lcs . m_ResolutionClampY + = oi . y ;
if ( int8 ( lcs . m_ResolutionClampX ) < 0 )
lcs . m_ResolutionClampX = 0 ;
if ( int8 ( lcs . m_ResolutionClampY ) < 0 )
lcs . m_ResolutionClampY = 0 ;
}
// Compute the requested mip0 dimensions
if ( lcs . m_ResolutionClampX & & lcs . m_ResolutionClampY )
{
nClampX = ( 1 < < lcs . m_ResolutionClampX ) ;
nClampY = ( 1 < < lcs . m_ResolutionClampY ) ;
}
// In case clamp values exceed texture dimensions, then fix up
// the clamping values
nClampX = min ( nClampX , ( int ) actualDims . m_nWidth ) ;
nClampY = min ( nClampY , ( int ) actualDims . m_nHeight ) ;
//
// Honor dimension limit restrictions
//
if ( nDesiredDimensionLimit > 0 )
{
while ( nClampX > nDesiredDimensionLimit | |
nClampY > nDesiredDimensionLimit )
{
nClampX > > = 1 ;
nClampY > > = 1 ;
}
}
//
// Unless ignoring picmip, reflect the global picmip level in clamp dimensions
//
if ( ! bIgnorePicmip )
{
// If picmip requests texture degradation, then honor it
// for loddable textures only
if ( ! ( nFlags & TEXTUREFLAGS_NOLOD ) & &
( g_config . skipMipLevels > 0 ) )
{
for ( int iDegrade = 0 ; iDegrade < g_config . skipMipLevels ; + + iDegrade )
{
// don't go lower than 4, or dxt textures won't work properly
if ( nClampX > 4 & &
nClampY > 4 )
{
nClampX > > = 1 ;
nClampY > > = 1 ;
}
}
}
// If picmip requests quality upgrade, then always honor it
if ( g_config . skipMipLevels < 0 )
{
for ( int iUpgrade = 0 ; iUpgrade < - g_config . skipMipLevels ; + + iUpgrade )
{
if ( nClampX < actualDims . m_nWidth & &
nClampY < actualDims . m_nHeight )
{
nClampX < < = 1 ;
nClampY < < = 1 ;
}
else
break ;
}
}
}
//
// Now use hardware settings to clamp our "clamping dimensions"
//
int iHwWidth = HardwareConfig ( ) - > MaxTextureWidth ( ) ;
int iHwHeight = HardwareConfig ( ) - > MaxTextureHeight ( ) ;
int iHwDepth = HardwareConfig ( ) - > MaxTextureDepth ( ) ;
nClampX = min ( nClampX , max ( iHwWidth , 4 ) ) ;
nClampY = min ( nClampY , max ( iHwHeight , 4 ) ) ;
nClampZ = min ( nClampZ , max ( iHwDepth , 1 ) ) ;
// In case clamp values exceed texture dimensions, then fix up
// the clamping values.
nClampX = min ( nClampX , ( int ) actualDims . m_nWidth ) ;
nClampY = min ( nClampY , ( int ) actualDims . m_nHeight ) ;
nClampZ = min ( nClampZ , ( int ) actualDims . m_nDepth ) ;
//
// Clamp to the determined dimensions
//
int numMipsSkipped = 0 ; // will compute now when clamping how many mips we drop
while ( ( actualDims . m_nWidth > nClampX ) | |
( actualDims . m_nHeight > nClampY ) | |
( actualDims . m_nDepth > nClampZ ) )
{
actualDims . m_nWidth > > = 1 ;
actualDims . m_nHeight > > = 1 ;
actualDims . m_nDepth = Max ( 1 , actualDims . m_nDepth > > 1 ) ;
+ + numMipsSkipped ;
}
Assert ( actualDims . m_nWidth > 0 & & actualDims . m_nHeight > 0 & & actualDims . m_nDepth > 0 ) ;
// Now that we've got the actual size, we can figure out the mip count
actualDims . m_nMipCount = ComputeActualMipCount ( actualDims , nFlags ) ;
// If we're streaming, cut down what we're loading.
// We can only stream things that have a mipmap pyramid (not just a single mipmap).
bool bHasSetAllocation = false ;
if ( ( nFlags & TEXTUREFLAGS_STREAMABLE ) = = TEXTUREFLAGS_STREAMABLE_COARSE )
{
if ( actualDims . m_nMipCount > 1 )
{
allocatedDims . m_nWidth = actualDims . m_nWidth ;
allocatedDims . m_nHeight = actualDims . m_nHeight ;
allocatedDims . m_nDepth = actualDims . m_nDepth ;
allocatedDims . m_nMipCount = actualDims . m_nMipCount ;
for ( int i = 0 ; i < STREAMING_START_MIPMAP ; + + i )
{
// Stop when width or height is at 4 pixels (or less). We could do better,
// but some textures really can't function if they're less than 4 pixels (compressed textures, for example).
if ( allocatedDims . m_nWidth < = 4 | | allocatedDims . m_nHeight < = 4 )
break ;
allocatedDims . m_nWidth > > = 1 ;
allocatedDims . m_nHeight > > = 1 ;
allocatedDims . m_nDepth = Max ( 1 , allocatedDims . m_nDepth > > 1 ) ;
allocatedDims . m_nMipCount = Max ( 1 , allocatedDims . m_nMipCount - 1 ) ;
+ + numMipsSkipped ;
+ + ( * pOutStreamedMips ) ;
}
bHasSetAllocation = true ;
}
else
{
// Clear out that we're streaming, this isn't a texture we can stream.
stripFlags | = TEXTUREFLAGS_STREAMABLE_COARSE ;
}
}
if ( ! bHasSetAllocation )
{
allocatedDims . m_nWidth = actualDims . m_nWidth ;
allocatedDims . m_nHeight = actualDims . m_nHeight ;
allocatedDims . m_nDepth = actualDims . m_nDepth ;
allocatedDims . m_nMipCount = actualDims . m_nMipCount ;
}
if ( pOptOutActualDims )
* pOptOutActualDims = actualDims ;
if ( pOptOutAllocatedDims )
* pOptOutAllocatedDims = allocatedDims ;
if ( pOptOutStripFlags )
( * pOptOutStripFlags ) = stripFlags ;
// Returns the number we skipped
return numMipsSkipped ;
}
//-----------------------------------------------------------------------------
// Get an optimal read buffer, persists and avoids excessive allocations
//-----------------------------------------------------------------------------
int GetOptimalReadBuffer ( CUtlBuffer * pOutOptimalBuffer , FileHandle_t hFile , int nSize )
{
// NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
// NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
// NOTE! NOTE! NOTE! or by the streaming texture code!
Assert ( GetThreadId ( ) < MAX_RENDER_THREADS ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s (%d bytes) " , __FUNCTION__ , nSize ) ;
Assert ( pOutOptimalBuffer ! = NULL ) ;
// get an optimal read buffer, only resize if necessary
const int minSize = 2 * 1024 * 1024 ; // Uses 2MB min to avoid fragmentation
nSize = max ( nSize , minSize ) ;
int nBytesOptimalRead = g_pFullFileSystem - > GetOptimalReadSize ( hFile , nSize ) ;
const int ti = GetThreadId ( ) ;
if ( nBytesOptimalRead > s_nOptimalReadBufferSize [ ti ] )
{
FreeOptimalReadBuffer ( 0 ) ;
s_nOptimalReadBufferSize [ ti ] = nBytesOptimalRead ;
s_pOptimalReadBuffer [ ti ] = g_pFullFileSystem - > AllocOptimalReadBuffer ( hFile , nSize ) ;
if ( mat_spewalloc . GetBool ( ) )
{
Msg ( " Allocated optimal read buffer of %d bytes @ 0x%p for thread %d \n " , s_nOptimalReadBufferSize [ ti ] , s_pOptimalReadBuffer [ ti ] , ti ) ;
}
}
// set external buffer and reset to empty
( * pOutOptimalBuffer ) . SetExternalBuffer ( s_pOptimalReadBuffer [ ti ] , s_nOptimalReadBufferSize [ ti ] , 0 ) ;
// return the optimal read size
return nBytesOptimalRead ;
}
//-----------------------------------------------------------------------------
// Free the optimal read buffer if it grows too large
//-----------------------------------------------------------------------------
void FreeOptimalReadBuffer ( int nMaxSize )
{
// NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
// NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
// NOTE! NOTE! NOTE! or by the streaming texture code!
Assert ( GetThreadId ( ) < MAX_RENDER_THREADS ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
const int ti = GetThreadId ( ) ;
if ( s_pOptimalReadBuffer [ ti ] & & s_nOptimalReadBufferSize [ ti ] > = nMaxSize )
{
if ( mat_spewalloc . GetBool ( ) )
{
Msg ( " Freeing optimal read buffer of %d bytes @ 0x%p for thread %d \n " , s_nOptimalReadBufferSize [ ti ] , s_pOptimalReadBuffer [ ti ] , ti ) ;
}
g_pFullFileSystem - > FreeOptimalReadBuffer ( s_pOptimalReadBuffer [ ti ] ) ;
s_pOptimalReadBuffer [ ti ] = NULL ;
s_nOptimalReadBufferSize [ ti ] = 0 ;
}
}
# if defined( STAGING_ONLY )
CON_COMMAND ( dumptexallocs , " List currently allocated textures and properties about them " )
{
Msg ( " Texture Memory Statistics follow: \n " ) ;
uint64 totalTexMemAllocated = 0 ;
FOR_EACH_MAP_FAST ( g_currentTextures , i )
{
const TexInfo_t & tex = g_currentTextures [ i ] ;
uint64 thisTexMem = tex . ComputeTexSize ( ) ;
totalTexMemAllocated + = thisTexMem ;
Msg ( " %s: %llu bytes \n " , ( const char * ) tex . m_Name , thisTexMem ) ;
}
Msg ( " Total Memory Allocated: %llu bytes \n " , totalTexMemAllocated ) ;
}
# endif
//////////////////////////////////////////////////////////////////////////
//
// Saving all the texture LOD modifications to content
//
//////////////////////////////////////////////////////////////////////////
# ifdef IS_WINDOWS_PC
static bool SetBufferValue ( char * chTxtFileBuffer , char const * szLookupKey , char const * szNewValue )
{
bool bResult = false ;
size_t lenTmp = strlen ( szNewValue ) ;
size_t nTxtFileBufferLen = strlen ( chTxtFileBuffer ) ;
for ( char * pch = chTxtFileBuffer ;
( NULL ! = ( pch = strstr ( pch , szLookupKey ) ) ) ;
+ + pch )
{
char * val = pch + strlen ( szLookupKey ) ;
if ( ! V_isspace ( * val ) )
continue ;
else
+ + val ;
char * pValStart = val ;
// Okay, here comes the value
while ( * val & & V_isspace ( * val ) )
+ + val ;
while ( * val & & ! V_isspace ( * val ) )
+ + val ;
char * pValEnd = val ; // Okay, here ends the value
memmove ( pValStart + lenTmp , pValEnd , chTxtFileBuffer + nTxtFileBufferLen + 1 - pValEnd ) ;
memcpy ( pValStart , szNewValue , lenTmp ) ;
nTxtFileBufferLen + = ( lenTmp - ( pValEnd - pValStart ) ) ;
bResult = true ;
}
if ( ! bResult )
{
char * pchAdd = chTxtFileBuffer + nTxtFileBufferLen ;
strcpy ( pchAdd + strlen ( pchAdd ) , " \n " ) ;
strcpy ( pchAdd + strlen ( pchAdd ) , szLookupKey ) ;
strcpy ( pchAdd + strlen ( pchAdd ) , " " ) ;
strcpy ( pchAdd + strlen ( pchAdd ) , szNewValue ) ;
strcpy ( pchAdd + strlen ( pchAdd ) , " \n " ) ;
bResult = true ;
}
return bResult ;
}
// Replaces the first occurrence of "szFindData" with "szNewData"
// Returns the remaining buffer past the replaced data or NULL if
// no replacement occurred.
static char * BufferReplace ( char * buf , char const * szFindData , char const * szNewData )
{
size_t len = strlen ( buf ) , lFind = strlen ( szFindData ) , lNew = strlen ( szNewData ) ;
if ( char * pBegin = strstr ( buf , szFindData ) )
{
memmove ( pBegin + lNew , pBegin + lFind , buf + len - ( pBegin + lFind ) ) ;
memmove ( pBegin , szNewData , lNew ) ;
return pBegin + lNew ;
}
return NULL ;
}
class CP4Requirement
{
public :
CP4Requirement ( ) ;
~ CP4Requirement ( ) ;
protected :
bool m_bLoadedModule ;
CSysModule * m_pP4Module ;
} ;
CP4Requirement : : CP4Requirement ( ) :
m_bLoadedModule ( false ) ,
m_pP4Module ( NULL )
{
# ifdef STAGING_ONLY
if ( p4 )
return ;
// load the p4 lib
m_pP4Module = Sys_LoadModule ( " p4lib " ) ;
m_bLoadedModule = true ;
if ( m_pP4Module )
{
CreateInterfaceFn factory = Sys_GetFactory ( m_pP4Module ) ;
if ( factory )
{
p4 = ( IP4 * ) factory ( P4_INTERFACE_VERSION , NULL ) ;
if ( p4 )
{
extern CreateInterfaceFn g_fnMatSystemConnectCreateInterface ;
p4 - > Connect ( g_fnMatSystemConnectCreateInterface ) ;
p4 - > Init ( ) ;
}
}
}
# endif // STAGING_ONLY
if ( ! p4 )
{
Warning ( " Can't load p4lib.dll \n " ) ;
}
}
CP4Requirement : : ~ CP4Requirement ( )
{
if ( m_bLoadedModule & & m_pP4Module )
{
if ( p4 )
{
p4 - > Shutdown ( ) ;
p4 - > Disconnect ( ) ;
}
Sys_UnloadModule ( m_pP4Module ) ;
m_pP4Module = NULL ;
p4 = NULL ;
}
}
static ConVar mat_texture_list_content_path ( " mat_texture_list_content_path " , " " , FCVAR_ARCHIVE , " The content path to the materialsrc directory. If left unset, it'll assume your content directory is next to the currently running game dir. " ) ;
CON_COMMAND_F ( mat_texture_list_txlod_sync , " 'reset' - resets all run-time changes to LOD overrides, 'save' - saves all changes to material content files " , FCVAR_DONTRECORD )
{
using namespace TextureLodOverride ;
if ( args . ArgC ( ) ! = 2 )
goto usage ;
char const * szCmd = args . Arg ( 1 ) ;
Msg ( " mat_texture_list_txlod_sync %s... \n " , szCmd ) ;
if ( ! stricmp ( szCmd , " reset " ) )
{
for ( int k = 0 ; k < s_OverrideMap . GetNumStrings ( ) ; + + k )
{
char const * szTx = s_OverrideMap . String ( k ) ;
s_OverrideMap [ k ] = OverrideInfo ( ) ; // Reset the override info
// Force the texture LOD override to get re-processed
if ( ITexture * pTx = materials - > FindTexture ( szTx , " " ) )
pTx - > ForceLODOverride ( 0 ) ;
else
Warning ( " mat_texture_list_txlod_sync reset - texture '%s' no longer found. \n " , szTx ) ;
}
s_OverrideMap . Purge ( ) ;
Msg ( " mat_texture_list_txlod_sync reset : completed. \n " ) ;
return ;
}
else if ( ! stricmp ( szCmd , " save " ) )
{
CP4Requirement p4req ;
if ( ! p4 )
g_p4factory - > SetDummyMode ( true ) ;
for ( int k = 0 ; k < s_OverrideMap . GetNumStrings ( ) ; + + k )
{
char const * szTx = s_OverrideMap . String ( k ) ;
OverrideInfo oi = s_OverrideMap [ k ] ;
ITexture * pTx = materials - > FindTexture ( szTx , " " ) ;
if ( ! oi . x | | ! oi . y )
continue ;
if ( ! pTx )
{
Warning ( " mat_texture_list_txlod_sync save - texture '%s' no longer found. \n " , szTx ) ;
continue ;
}
int iMaxWidth = pTx - > GetActualWidth ( ) , iMaxHeight = pTx - > GetActualHeight ( ) ;
// Save maxwidth and maxheight
char chMaxWidth [ 20 ] , chMaxHeight [ 20 ] ;
sprintf ( chMaxWidth , " %d " , iMaxWidth ) , sprintf ( chMaxHeight , " %d " , iMaxHeight ) ;
// We have the texture and path to its content
char chResolveName [ MAX_PATH ] = { 0 } , chResolveNameArg [ MAX_PATH ] = { 0 } ;
Q_snprintf ( chResolveNameArg , sizeof ( chResolveNameArg ) - 1 , " materials/%s " TEXTURE_FNAME_EXTENSION , szTx ) ;
char * szTextureContentPath ;
if ( ! mat_texture_list_content_path . GetString ( ) [ 0 ] )
{
szTextureContentPath = const_cast < char * > ( g_pFullFileSystem - > RelativePathToFullPath ( chResolveNameArg , " game " , chResolveName , sizeof ( chResolveName ) - 1 ) ) ;
if ( ! szTextureContentPath )
{
Warning ( " mat_texture_list_txlod_sync save - texture '%s' is not loaded from file system. \n " , szTx ) ;
continue ;
}
if ( ! BufferReplace ( szTextureContentPath , " \\ game \\ " , " \\ content \\ " ) | |
! BufferReplace ( szTextureContentPath , " \\ materials \\ " , " \\ materialsrc \\ " ) )
{
Warning ( " mat_texture_list_txlod_sync save - texture '%s' cannot be mapped to content directory. \n " , szTx ) ;
continue ;
}
}
else
{
V_strncpy ( chResolveName , mat_texture_list_content_path . GetString ( ) , MAX_PATH ) ;
V_strncat ( chResolveName , " / " , MAX_PATH ) ;
V_strncat ( chResolveName , szTx , MAX_PATH ) ;
V_strncat ( chResolveName , TEXTURE_FNAME_EXTENSION , MAX_PATH ) ;
szTextureContentPath = chResolveName ;
}
// Figure out what kind of source content is there:
// 1. look for TGA - if found, get the txt file (if txt file missing, create one)
// 2. otherwise look for PSD - affecting psdinfo
// 3. else error
char * pExtPut = szTextureContentPath + strlen ( szTextureContentPath ) - strlen ( TEXTURE_FNAME_EXTENSION ) ; // compensating the TEXTURE_FNAME_EXTENSION(.vtf) extension
// 1.tga
sprintf ( pExtPut , " .tga " ) ;
if ( g_pFullFileSystem - > FileExists ( szTextureContentPath ) )
{
// Have tga - pump in the txt file
sprintf ( pExtPut , " .txt " ) ;
CUtlBuffer bufTxtFileBuffer ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
g_pFullFileSystem - > ReadFile ( szTextureContentPath , 0 , bufTxtFileBuffer ) ;
for ( int kCh = 0 ; kCh < 1024 ; + + kCh ) bufTxtFileBuffer . PutChar ( 0 ) ;
// Now fix maxwidth/maxheight settings
SetBufferValue ( ( char * ) bufTxtFileBuffer . Base ( ) , " maxwidth " , chMaxWidth ) ;
SetBufferValue ( ( char * ) bufTxtFileBuffer . Base ( ) , " maxheight " , chMaxHeight ) ;
bufTxtFileBuffer . SeekPut ( CUtlBuffer : : SEEK_HEAD , strlen ( ( char * ) bufTxtFileBuffer . Base ( ) ) ) ;
// Check out or add the file
g_p4factory - > SetOpenFileChangeList ( " Texture LOD Autocheckout " ) ;
CP4AutoEditFile autop4_edit ( szTextureContentPath ) ;
// Save the file contents
if ( g_pFullFileSystem - > WriteFile ( szTextureContentPath , 0 , bufTxtFileBuffer ) )
{
Msg ( " '%s' : saved. \n " , szTextureContentPath ) ;
CP4AutoAddFile autop4_add ( szTextureContentPath ) ;
}
else
{
Warning ( " '%s' : failed to save - set \" maxwidth %d maxheight %d \" manually. \n " ,
szTextureContentPath , iMaxWidth , iMaxHeight ) ;
}
continue ;
}
// 2.psd
sprintf ( pExtPut , " .psd " ) ;
if ( g_pFullFileSystem - > FileExists ( szTextureContentPath ) )
{
char chCommand [ MAX_PATH ] ;
char szTxtFileName [ MAX_PATH ] = { 0 } ;
GetModSubdirectory ( " tmp_lod_psdinfo.txt " , szTxtFileName , sizeof ( szTxtFileName ) ) ;
sprintf ( chCommand , " /C psdinfo \" %s \" > \" %s \" " , szTextureContentPath , szTxtFileName ) ;
ShellExecute ( NULL , NULL , " cmd.exe " , chCommand , NULL , SW_HIDE ) ;
Sleep ( 200 ) ;
CUtlBuffer bufTxtFileBuffer ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
g_pFullFileSystem - > ReadFile ( szTxtFileName , 0 , bufTxtFileBuffer ) ;
for ( int kCh = 0 ; kCh < 1024 ; + + kCh ) bufTxtFileBuffer . PutChar ( 0 ) ;
// Now fix maxwidth/maxheight settings
SetBufferValue ( ( char * ) bufTxtFileBuffer . Base ( ) , " maxwidth " , chMaxWidth ) ;
SetBufferValue ( ( char * ) bufTxtFileBuffer . Base ( ) , " maxheight " , chMaxHeight ) ;
bufTxtFileBuffer . SeekPut ( CUtlBuffer : : SEEK_HEAD , strlen ( ( char * ) bufTxtFileBuffer . Base ( ) ) ) ;
// Check out or add the file
// Save the file contents
if ( g_pFullFileSystem - > WriteFile ( szTxtFileName , 0 , bufTxtFileBuffer ) )
{
g_p4factory - > SetOpenFileChangeList ( " Texture LOD Autocheckout " ) ;
CP4AutoEditFile autop4_edit ( szTextureContentPath ) ;
sprintf ( chCommand , " /C psdinfo -write \" %s \" < \" %s \" " , szTextureContentPath , szTxtFileName ) ;
Sleep ( 200 ) ;
ShellExecute ( NULL , NULL , " cmd.exe " , chCommand , NULL , SW_HIDE ) ;
Sleep ( 200 ) ;
Msg ( " '%s' : saved. \n " , szTextureContentPath ) ;
CP4AutoAddFile autop4_add ( szTextureContentPath ) ;
}
else
{
Warning ( " '%s' : failed to save - set \" maxwidth %d maxheight %d \" manually. \n " ,
szTextureContentPath , iMaxWidth , iMaxHeight ) ;
}
continue ;
}
// 3. - error
sprintf ( pExtPut , " " ) ;
{
Warning ( " '%s' : doesn't specify a valid TGA or PSD file! \n " , szTextureContentPath ) ;
continue ;
}
}
Msg ( " mat_texture_list_txlod_sync save : completed. \n " ) ;
return ;
}
else
goto usage ;
return ;
usage :
Warning (
" Usage: \n "
" mat_texture_list_txlod_sync reset - resets all run-time changes to LOD overrides; \n "
" mat_texture_list_txlod_sync save - saves all changes to material content files. \n "
) ;
}
# endif