summaryrefslogtreecommitdiff
path: root/materialsystem/ctexture.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'materialsystem/ctexture.cpp')
-rw-r--r--materialsystem/ctexture.cpp4959
1 files changed, 4959 insertions, 0 deletions
diff --git a/materialsystem/ctexture.cpp b/materialsystem/ctexture.cpp
new file mode 100644
index 0000000..dbfac95
--- /dev/null
+++ b/materialsystem/ctexture.cpp
@@ -0,0 +1,4959 @@
+//========= 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 <malloc.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;
+ }
+
+ if ( V_strstr( GetName(), "c_sniperrifle_scope" ) )
+ {
+ int i = 0;
+ i = 3;
+ }
+
+
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - %s", __FUNCTION__, tmDynamicString( TELEMETRY_LEVEL0, pCacheFileName ) );
+
+ // 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];
+
+ ShaderAPITextureHandle_t depthTextureHandle = (unsigned int)SHADER_RENDERTARGET_DEPTHBUFFER;
+
+ 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
+ depthTextureHandle = (unsigned int)SHADER_RENDERTARGET_NONE;
+ }
+
+ 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.
+ Assert( MaterialSystem()->GetRenderThreadId() == 0xFFFFFFFF );
+ 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 )
+{
+ const int cArgsAsInt = ( int ) pExtraArgs;
+
+ 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 );
+
+ if ( V_strstr( pName, "c_rocketlauncher/c_rocketlauncher" ) )
+ {
+ int i = 0;
+ i = 3;
+ }
+
+ 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