Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4959 lines
154 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #ifdef PROTECTED_THINGS_ENABLE
  7. #undef PROTECTED_THINGS_ENABLE
  8. #endif
  9. #include "platform.h"
  10. // HACK: Need ShellExecute for PSD updates
  11. #ifdef IS_WINDOWS_PC
  12. #include <windows.h>
  13. #include <shellapi.h>
  14. #pragma comment ( lib, "shell32" )
  15. #endif
  16. #include "materialsystem_global.h"
  17. #include "shaderapi/ishaderapi.h"
  18. #include "itextureinternal.h"
  19. #include "utlsymbol.h"
  20. #include "time.h"
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include "bitmap/imageformat.h"
  24. #include "bitmap/tgaloader.h"
  25. #include "bitmap/tgawriter.h"
  26. #ifdef _WIN32
  27. #include "direct.h"
  28. #endif
  29. #include "colorspace.h"
  30. #include "string.h"
  31. #include <malloc.h>
  32. #include <stdlib.h>
  33. #include "utlmemory.h"
  34. #include "IHardwareConfigInternal.h"
  35. #include "filesystem.h"
  36. #include "tier1/strtools.h"
  37. #include "vtf/vtf.h"
  38. #include "materialsystem/materialsystem_config.h"
  39. #include "mempool.h"
  40. #include "texturemanager.h"
  41. #include "utlbuffer.h"
  42. #include "pixelwriter.h"
  43. #include "tier1/callqueue.h"
  44. #include "tier1/UtlStringMap.h"
  45. #include "filesystem/IQueuedLoader.h"
  46. #include "tier2/fileutils.h"
  47. #include "filesystem.h"
  48. #include "tier2/p4helpers.h"
  49. #include "tier2/tier2.h"
  50. #include "p4lib/ip4.h"
  51. #include "ctype.h"
  52. #include "ifilelist.h"
  53. #include "tier0/icommandline.h"
  54. #include "tier0/vprof.h"
  55. // NOTE: This must be the last file included!!!
  56. #include "tier0/memdbgon.h"
  57. // this allows the command line to force the "all mips" flag to on for all textures
  58. bool g_bForceTextureAllMips = false;
  59. #if defined(IS_WINDOWS_PC)
  60. static void ConVarChanged_mat_managedtextures( IConVar *var, const char *pOldValue, float flOldValue );
  61. 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 );
  62. static void ConVarChanged_mat_managedtextures( IConVar *var, const char *pOldValue, float flOldValue )
  63. {
  64. if ( mat_managedtextures.GetBool() != (flOldValue != 0) )
  65. {
  66. materials->ReleaseResources();
  67. materials->ReacquireResources();
  68. }
  69. }
  70. #endif
  71. 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" );
  72. static ConVar mat_lodin_time( "mat_lodin_time", "5.0", FCVAR_DEVELOPMENTONLY );
  73. static ConVar mat_lodin_hidden_pop( "mat_lodin_hidden_pop", "1", FCVAR_DEVELOPMENTONLY );
  74. #define TEXTURE_FNAME_EXTENSION ".vtf"
  75. #define TEXTURE_FNAME_EXTENSION_LEN 4
  76. #define TEXTURE_FNAME_EXTENSION_NORMAL "_normal.vtf"
  77. #ifdef STAGING_ONLY
  78. ConVar mat_spewalloc( "mat_spewalloc", "0" );
  79. #else
  80. ConVar mat_spewalloc( "mat_spewalloc", "0", FCVAR_ARCHIVE | FCVAR_DEVELOPMENTONLY );
  81. #endif
  82. struct TexDimensions_t
  83. {
  84. uint16 m_nWidth;
  85. uint16 m_nHeight;
  86. uint16 m_nMipCount;
  87. uint16 m_nDepth;
  88. TexDimensions_t( uint16 nWidth = 0, uint nHeight = 0, uint nMipCount = 0, uint16 nDepth = 1 )
  89. : m_nWidth( nWidth )
  90. , m_nHeight( nHeight )
  91. , m_nMipCount( nMipCount )
  92. , m_nDepth( nDepth )
  93. { }
  94. };
  95. #ifdef STAGING_ONLY
  96. struct TexInfo_t
  97. {
  98. CUtlString m_Name;
  99. unsigned short m_nWidth;
  100. unsigned short m_nHeight;
  101. unsigned short m_nDepth;
  102. unsigned short m_nMipCount;
  103. unsigned short m_nFrameCount;
  104. unsigned short m_nCopies;
  105. ImageFormat m_Format;
  106. uint64 ComputeTexSize() const
  107. {
  108. uint64 total = 0;
  109. unsigned short width = m_nWidth;
  110. unsigned short height = m_nHeight;
  111. unsigned short depth = m_nDepth;
  112. for ( int mip = 0; mip < m_nMipCount; ++mip )
  113. {
  114. // Make sure that mip count lines up with the count
  115. Assert( width > 1 || height > 1 || depth > 1 || ( mip == ( m_nMipCount - 1 ) ) );
  116. total += ImageLoader::GetMemRequired( width, height, depth, m_Format, false );
  117. width = Max( 1, width >> 1 );
  118. height = Max( 1, height >> 1 );
  119. depth = Max( 1, depth >> 1 );
  120. }
  121. return total * Min( (unsigned short) 1, m_nFrameCount ) * Min( (unsigned short) 1, m_nCopies );
  122. }
  123. 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 )
  124. : m_nWidth( w )
  125. , m_nHeight( h )
  126. , m_nDepth( d )
  127. , m_nMipCount( mips )
  128. , m_nFrameCount( frames )
  129. , m_nCopies( copies )
  130. , m_Format( fmt )
  131. {
  132. if ( name && name[0] )
  133. m_Name = name;
  134. else
  135. m_Name = "<unnamed>";
  136. }
  137. };
  138. CUtlMap< ITexture*, TexInfo_t > g_currentTextures( DefLessFunc( ITexture* ) );
  139. #endif
  140. //-----------------------------------------------------------------------------
  141. // Internal texture flags
  142. //-----------------------------------------------------------------------------
  143. enum InternalTextureFlags
  144. {
  145. TEXTUREFLAGSINTERNAL_ERROR = 0x00000001,
  146. TEXTUREFLAGSINTERNAL_ALLOCATED = 0x00000002,
  147. TEXTUREFLAGSINTERNAL_PRELOADED = 0x00000004, // 360: textures that went through the preload process
  148. TEXTUREFLAGSINTERNAL_QUEUEDLOAD = 0x00000008, // 360: load using the queued loader
  149. TEXTUREFLAGSINTERNAL_EXCLUDED = 0x00000020, // actual exclusion state
  150. TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE = 0x00000040, // desired exclusion state
  151. TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET = 0x00000080, // 360: should only allocate texture bits upon first resolve, destroy at level end
  152. };
  153. static int GetThreadId();
  154. 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 );
  155. static int ComputeActualMipCount( const TexDimensions_t& actualDims, unsigned int nFlags );
  156. 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 );
  157. static int GetOptimalReadBuffer( CUtlBuffer *pOutOptimalBuffer, FileHandle_t hFile, int nFileSize );
  158. static void FreeOptimalReadBuffer( int nMaxSize );
  159. //-----------------------------------------------------------------------------
  160. // Use Warning to show texture flags.
  161. //-----------------------------------------------------------------------------
  162. static void PrintFlags( unsigned int flags )
  163. {
  164. if ( flags & TEXTUREFLAGS_NOMIP )
  165. {
  166. Warning( "TEXTUREFLAGS_NOMIP|" );
  167. }
  168. if ( flags & TEXTUREFLAGS_NOLOD )
  169. {
  170. Warning( "TEXTUREFLAGS_NOLOD|" );
  171. }
  172. if ( flags & TEXTUREFLAGS_SRGB )
  173. {
  174. Warning( "TEXTUREFLAGS_SRGB|" );
  175. }
  176. if ( flags & TEXTUREFLAGS_POINTSAMPLE )
  177. {
  178. Warning( "TEXTUREFLAGS_POINTSAMPLE|" );
  179. }
  180. if ( flags & TEXTUREFLAGS_TRILINEAR )
  181. {
  182. Warning( "TEXTUREFLAGS_TRILINEAR|" );
  183. }
  184. if ( flags & TEXTUREFLAGS_CLAMPS )
  185. {
  186. Warning( "TEXTUREFLAGS_CLAMPS|" );
  187. }
  188. if ( flags & TEXTUREFLAGS_CLAMPT )
  189. {
  190. Warning( "TEXTUREFLAGS_CLAMPT|" );
  191. }
  192. if ( flags & TEXTUREFLAGS_HINT_DXT5 )
  193. {
  194. Warning( "TEXTUREFLAGS_HINT_DXT5|" );
  195. }
  196. if ( flags & TEXTUREFLAGS_ANISOTROPIC )
  197. {
  198. Warning( "TEXTUREFLAGS_ANISOTROPIC|" );
  199. }
  200. if ( flags & TEXTUREFLAGS_PROCEDURAL )
  201. {
  202. Warning( "TEXTUREFLAGS_PROCEDURAL|" );
  203. }
  204. if ( flags & TEXTUREFLAGS_ALL_MIPS )
  205. {
  206. Warning( "TEXTUREFLAGS_ALL_MIPS|" );
  207. }
  208. if ( flags & TEXTUREFLAGS_SINGLECOPY )
  209. {
  210. Warning( "TEXTUREFLAGS_SINGLECOPY|" );
  211. }
  212. if ( flags & TEXTUREFLAGS_STAGING_MEMORY )
  213. {
  214. Warning( "TEXTUREFLAGS_STAGING_MEMORY|" );
  215. }
  216. if ( flags & TEXTUREFLAGS_IGNORE_PICMIP )
  217. {
  218. Warning( "TEXTUREFLAGS_IGNORE_PICMIP|" );
  219. }
  220. if ( flags & TEXTUREFLAGS_IMMEDIATE_CLEANUP )
  221. {
  222. Warning( "TEXTUREFLAGS_IMMEDIATE_CLEANUP|" );
  223. }
  224. }
  225. namespace TextureLodOverride
  226. {
  227. struct OverrideInfo
  228. {
  229. OverrideInfo() : x( 0 ), y( 0 ) {}
  230. OverrideInfo( int8 x_, int8 y_ ) : x( x_ ), y( y_ ) {}
  231. int8 x, y;
  232. };
  233. // Override map
  234. typedef CUtlStringMap< OverrideInfo > OverrideMap_t;
  235. OverrideMap_t s_OverrideMap;
  236. // Retrieves the override info adjustments
  237. OverrideInfo Get( char const *szName )
  238. {
  239. UtlSymId_t idx = s_OverrideMap.Find( szName );
  240. if ( idx != s_OverrideMap.InvalidIndex() )
  241. return s_OverrideMap[ idx ];
  242. else
  243. return OverrideInfo();
  244. }
  245. // Combines the existing override info adjustments with the given one
  246. void Add( char const *szName, OverrideInfo oi )
  247. {
  248. OverrideInfo oiex = Get( szName );
  249. oiex.x += oi.x;
  250. oiex.y += oi.y;
  251. s_OverrideMap[ szName ] = oiex;
  252. }
  253. }; // end namespace TextureLodOverride
  254. class CTextureStreamingJob;
  255. //-----------------------------------------------------------------------------
  256. // Base texture class
  257. //-----------------------------------------------------------------------------
  258. class CTexture : public ITextureInternal
  259. {
  260. public:
  261. CTexture();
  262. virtual ~CTexture();
  263. virtual const char *GetName( void ) const;
  264. const char *GetTextureGroupName( void ) const;
  265. // Stats about the texture itself
  266. virtual ImageFormat GetImageFormat() const;
  267. NormalDecodeMode_t GetNormalDecodeMode() const { return NORMAL_DECODE_NONE; }
  268. virtual int GetMappingWidth() const;
  269. virtual int GetMappingHeight() const;
  270. virtual int GetActualWidth() const;
  271. virtual int GetActualHeight() const;
  272. virtual int GetNumAnimationFrames() const;
  273. virtual bool IsTranslucent() const;
  274. virtual void GetReflectivity( Vector& reflectivity );
  275. // Reference counting
  276. virtual void IncrementReferenceCount( );
  277. virtual void DecrementReferenceCount( );
  278. virtual int GetReferenceCount( );
  279. // Used to modify the texture bits (procedural textures only)
  280. virtual void SetTextureRegenerator( ITextureRegenerator *pTextureRegen );
  281. // Little helper polling methods
  282. virtual bool IsNormalMap( ) const;
  283. virtual bool IsCubeMap( void ) const;
  284. virtual bool IsRenderTarget( ) const;
  285. virtual bool IsTempRenderTarget( void ) const;
  286. virtual bool IsProcedural() const;
  287. virtual bool IsMipmapped() const;
  288. virtual bool IsError() const;
  289. // For volume textures
  290. virtual bool IsVolumeTexture() const;
  291. virtual int GetMappingDepth() const;
  292. virtual int GetActualDepth() const;
  293. // Various ways of initializing the texture
  294. void InitFileTexture( const char *pTextureFile, const char *pTextureGroupName );
  295. void InitProceduralTexture( const char *pTextureName, const char *pTextureGroupName, int w, int h, int d, ImageFormat fmt, int nFlags, ITextureRegenerator* generator = NULL );
  296. // Releases the texture's hw memory
  297. void ReleaseMemory();
  298. virtual void OnRestore();
  299. // Sets the filtering modes on the texture we're modifying
  300. void SetFilteringAndClampingMode( bool bOnlyLodValues = false );
  301. void Download( Rect_t *pRect = NULL, int nAdditionalCreationFlags = 0 );
  302. // Loads up information about the texture
  303. virtual void Precache();
  304. // FIXME: Bogus methods... can we please delete these?
  305. virtual void GetLowResColorSample( float s, float t, float *color ) const;
  306. // Gets texture resource data of the specified type.
  307. // Params:
  308. // eDataType type of resource to retrieve.
  309. // pnumBytes on return is the number of bytes available in the read-only data buffer or is undefined
  310. // Returns:
  311. // pointer to the resource data, or NULL. Note that the data from this pointer can disappear when
  312. // the texture goes away - you want to copy this data!
  313. virtual void *GetResourceData( uint32 eDataType, size_t *pNumBytes ) const;
  314. virtual int GetApproximateVidMemBytes( void ) const;
  315. // Stretch blit the framebuffer into this texture.
  316. virtual void CopyFrameBufferToMe( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL );
  317. virtual void CopyMeToFrameBuffer( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL );
  318. virtual ITexture *GetEmbeddedTexture( int nIndex );
  319. // Get the shaderapi texture handle associated w/ a particular frame
  320. virtual ShaderAPITextureHandle_t GetTextureHandle( int nFrame, int nChannel = 0 );
  321. // Sets the texture as the render target
  322. virtual void Bind( Sampler_t sampler );
  323. virtual void Bind( Sampler_t sampler1, int nFrame, Sampler_t sampler2 = (Sampler_t) -1 );
  324. virtual void BindVertexTexture( VertexTextureSampler_t stage, int nFrame );
  325. // Set this texture as a render target
  326. bool SetRenderTarget( int nRenderTargetID );
  327. // Set this texture as a render target (optionally set depth texture as depth buffer as well)
  328. bool SetRenderTarget( int nRenderTargetID, ITexture *pDepthTexture);
  329. virtual void MarkAsPreloaded( bool bSet );
  330. virtual bool IsPreloaded() const;
  331. virtual void MarkAsExcluded( bool bSet, int nDimensionsLimit );
  332. virtual bool UpdateExcludedState( void );
  333. // Retrieve the vtf flags mask
  334. virtual unsigned int GetFlags( void ) const;
  335. virtual void ForceLODOverride( int iNumLodsOverrideUpOrDown );
  336. void GetFilename( char *pOut, int maxLen ) const;
  337. virtual void ReloadFilesInList( IFileList *pFilesToReload );
  338. // Save texture to a file.
  339. virtual bool SaveToFile( const char *fileName );
  340. // Load the texture from a file.
  341. bool AsyncReadTextureFromFile( IVTFTexture* pVTFTexture, unsigned int nAdditionalCreationFlags );
  342. void AsyncCancelReadTexture( );
  343. virtual void Map( void** pOutBits, int* pOutPitch );
  344. virtual void Unmap();
  345. virtual ResidencyType_t GetCurrentResidence() const { return m_residenceCurrent; }
  346. virtual ResidencyType_t GetTargetResidence() const { return m_residenceTarget; }
  347. virtual bool MakeResident( ResidencyType_t newResidence );
  348. virtual void UpdateLodBias();
  349. protected:
  350. bool IsDepthTextureFormat( ImageFormat fmt );
  351. void ReconstructTexture( bool bCopyFromCurrent );
  352. void GetCacheFilename( char* pOutBuffer, int bufferSize ) const;
  353. bool GetFileHandle( FileHandle_t *pOutFileHandle, char *pCacheFilename, char **ppResolvedFilename ) const;
  354. void ReconstructPartialTexture( const Rect_t *pRect );
  355. bool HasBeenAllocated() const;
  356. void WriteDataToShaderAPITexture( int nFrameCount, int nFaceCount, int nFirstFace, int nMipCount, IVTFTexture *pVTFTexture, ImageFormat fmt );
  357. // Initializes/shuts down the texture
  358. void Init( int w, int h, int d, ImageFormat fmt, int iFlags, int iFrameCount );
  359. void Shutdown();
  360. // Sets the texture name
  361. void SetName( const char* pName );
  362. // Assigns/releases texture IDs for our animation frames
  363. void AllocateTextureHandles( );
  364. void ReleaseTextureHandles( );
  365. // Calculates info about whether we can make the texture smaller and by how much
  366. // Returns the number of skipped mip levels
  367. int ComputeActualSize( bool bIgnorePicmip = false, IVTFTexture *pVTFTexture = NULL, bool bTextureMigration = false );
  368. // Computes the actual format of the texture given a desired src format
  369. ImageFormat ComputeActualFormat( ImageFormat srcFormat );
  370. // Creates/releases the shader api texture
  371. bool AllocateShaderAPITextures();
  372. void FreeShaderAPITextures();
  373. void MigrateShaderAPITextures();
  374. void NotifyUnloadedFile();
  375. // Download bits
  376. void DownloadTexture( Rect_t *pRect, bool bCopyFromCurrent = false );
  377. void ReconstructTextureBits(Rect_t *pRect);
  378. // Gets us modifying a particular frame of our texture
  379. void Modify( int iFrame );
  380. // Sets the texture clamping state on the currently modified frame
  381. void SetWrapState( );
  382. // Sets the texture filtering state on the currently modified frame
  383. void SetFilterState();
  384. // Sets the lod state on the currently modified frame
  385. void SetLodState();
  386. // Loads the texture bits from a file. Optionally provides absolute path
  387. IVTFTexture *LoadTextureBitsFromFile( char *pCacheFileName, char **pResolvedFilename );
  388. IVTFTexture *HandleFileLoadFailedTexture( IVTFTexture *pVTFTexture );
  389. // Generates the procedural bits
  390. IVTFTexture *ReconstructProceduralBits( );
  391. IVTFTexture *ReconstructPartialProceduralBits( const Rect_t *pRect, Rect_t *pActualRect );
  392. // Sets up debugging texture bits, if appropriate
  393. bool SetupDebuggingTextures( IVTFTexture *pTexture );
  394. // Generate a texture that shows the various mip levels
  395. void GenerateShowMipLevelsTextures( IVTFTexture *pTexture );
  396. void Cleanup( void );
  397. // Converts a source image read from disk into its actual format
  398. bool ConvertToActualFormat( IVTFTexture *pTexture );
  399. // Builds the low-res image from the texture
  400. void LoadLowResTexture( IVTFTexture *pTexture );
  401. void CopyLowResImageToTexture( IVTFTexture *pTexture );
  402. void GetDownloadFaceCount( int &nFirstFace, int &nFaceCount );
  403. void ComputeMipLevelSubRect( const Rect_t* pSrcRect, int nMipLevel, Rect_t *pSubRect );
  404. IVTFTexture *GetScratchVTFTexture( );
  405. void ReleaseScratchVTFTexture( IVTFTexture* tex );
  406. void ApplyRenderTargetSizeMode( int &width, int &height, ImageFormat fmt );
  407. virtual void CopyToStagingTexture( ITexture* pDstTex );
  408. virtual void SetErrorTexture( bool _isErrorTexture );
  409. // Texture streaming
  410. void MakeNonResident();
  411. void MakePartiallyResident();
  412. bool MakeFullyResident();
  413. void CancelStreamingJob( bool bJobMustExist = true );
  414. void OnStreamingJobComplete( ResidencyType_t newResidenceCurrent );
  415. protected:
  416. #ifdef _DEBUG
  417. char *m_pDebugName;
  418. #endif
  419. // Reflectivity vector
  420. Vector m_vecReflectivity;
  421. CUtlSymbol m_Name;
  422. // What texture group this texture is in (winds up setting counters based on the group name,
  423. // then the budget panel views the counters).
  424. CUtlSymbol m_TextureGroupName;
  425. unsigned int m_nFlags;
  426. unsigned int m_nInternalFlags;
  427. CInterlockedInt m_nRefCount;
  428. // This is the *desired* image format, which may or may not represent reality
  429. ImageFormat m_ImageFormat;
  430. // mapping dimensions and actual dimensions can/will vary due to user settings, hardware support, etc.
  431. // Allocated is what is physically allocated on the hardware at this instant, and considers texture streaming.
  432. TexDimensions_t m_dimsMapping;
  433. TexDimensions_t m_dimsActual;
  434. TexDimensions_t m_dimsAllocated;
  435. // This is the iWidth/iHeight for whatever is downloaded to the card, ignoring current streaming settings
  436. // Some callers want to know how big the texture is if all data was present, and that's this.
  437. // TODO: Rename this before check in.
  438. unsigned short m_nFrameCount;
  439. // These are the values for what is truly allocated on the card, including streaming settings.
  440. unsigned short m_nStreamingMips;
  441. unsigned short m_nOriginalRTWidth; // The values they initially specified. We generated a different width
  442. unsigned short m_nOriginalRTHeight; // and height based on screen size and the flags they specify.
  443. unsigned char m_LowResImageWidth;
  444. unsigned char m_LowResImageHeight;
  445. unsigned short m_nDesiredDimensionLimit; // part of texture exclusion
  446. unsigned short m_nActualDimensionLimit; // value not necessarily accurate, but mismatch denotes dirty state
  447. // m_pStreamingJob is refcounted, but it is not safe to call SafeRelease directly on it--you must call
  448. // CancelStreamingJob to ensure that releasing it doesn't cause a crash.
  449. CTextureStreamingJob* m_pStreamingJob;
  450. IVTFTexture* m_pStreamingVTF;
  451. ResidencyType_t m_residenceTarget;
  452. ResidencyType_t m_residenceCurrent;
  453. int m_lodClamp;
  454. int m_lastLodBiasAdjustFrame;
  455. float m_lodBiasInitial;
  456. float m_lodBiasCurrent;
  457. double m_lodBiasStartTime;
  458. // If the read failed, this will be true. We can't just return from the function because the call may
  459. // happen in the async thread.
  460. bool m_bStreamingFileReadFailed;
  461. // The set of texture ids for each animation frame
  462. ShaderAPITextureHandle_t *m_pTextureHandles;
  463. TextureLODControlSettings_t m_cachedFileLodSettings;
  464. // lowresimage info - used for getting color data from a texture
  465. // without having a huge system mem overhead.
  466. // FIXME: We should keep this in compressed form. .is currently decompressed at load time.
  467. unsigned char *m_pLowResImage;
  468. ITextureRegenerator *m_pTextureRegenerator;
  469. // Used to help decide whether or not to recreate the render target if AA changes.
  470. RenderTargetType_t m_nOriginalRenderTargetType;
  471. RenderTargetSizeMode_t m_RenderTargetSizeMode;
  472. // Fixed-size allocator
  473. // DECLARE_FIXEDSIZE_ALLOCATOR( CTexture );
  474. public:
  475. void InitRenderTarget( const char *pRTName, int w, int h, RenderTargetSizeMode_t sizeMode,
  476. ImageFormat fmt, RenderTargetType_t type, unsigned int textureFlags,
  477. unsigned int renderTargetFlags );
  478. virtual void DeleteIfUnreferenced();
  479. void FixupTexture( const void *pData, int nSize, LoaderError_t loaderError );
  480. void SwapContents( ITexture *pOther );
  481. protected:
  482. // private data, generally from VTF resource extensions
  483. struct DataChunk
  484. {
  485. void Allocate( unsigned int numBytes )
  486. {
  487. m_pvData = new unsigned char[ numBytes ];
  488. m_numBytes = numBytes;
  489. }
  490. void Deallocate() const { delete [] m_pvData; }
  491. unsigned int m_eType;
  492. unsigned int m_numBytes;
  493. unsigned char *m_pvData;
  494. };
  495. CUtlVector< DataChunk > m_arrDataChunks;
  496. struct ScratchVTF
  497. {
  498. ScratchVTF( CTexture* _tex ) : m_pParent( _tex ), m_pScratchVTF( _tex->GetScratchVTFTexture( ) ) { }
  499. ~ScratchVTF( )
  500. {
  501. if ( m_pScratchVTF )
  502. m_pParent->ReleaseScratchVTFTexture( m_pScratchVTF );
  503. m_pScratchVTF = NULL;
  504. }
  505. IVTFTexture* Get() const { return m_pScratchVTF; }
  506. void TakeOwnership() { m_pScratchVTF = NULL; }
  507. CTexture* m_pParent;
  508. IVTFTexture* m_pScratchVTF;
  509. };
  510. friend class CTextureStreamingJob;
  511. };
  512. class CTextureStreamingJob : public IAsyncTextureOperationReceiver
  513. {
  514. public:
  515. CTextureStreamingJob( CTexture* pTex ) : m_referenceCount( 0 ), m_pOwner( pTex ) { Assert( m_pOwner != NULL ); m_pOwner->AddRef(); }
  516. virtual ~CTextureStreamingJob() { SafeRelease( &m_pOwner ); }
  517. virtual int AddRef() OVERRIDE { return ++m_referenceCount; }
  518. virtual int Release() OVERRIDE { int retVal = --m_referenceCount; Assert( retVal >= 0 ); if ( retVal == 0 ) { delete this; } return retVal; }
  519. virtual int GetRefCount() const OVERRIDE { return m_referenceCount; }
  520. virtual void OnAsyncCreateComplete( ITexture* pTex, void* pExtraArgs ) OVERRIDE { Assert( !"unimpl" ); }
  521. virtual void OnAsyncFindComplete( ITexture* pTex, void* pExtraArgs ) OVERRIDE;
  522. virtual void OnAsyncMapComplete( ITexture* pTex, void* pExtraArgs, void* pMemory, int nPitch ) { Assert( !"unimpl" ); }
  523. virtual void OnAsyncReadbackBegin( ITexture* pDst, ITexture* pSrc, void* pExtraArgs ) OVERRIDE { Assert( !"unimpl" ); }
  524. void ForgetOwner( ITextureInternal* pTex ) { Assert( pTex == m_pOwner ); SafeRelease( &m_pOwner ); }
  525. private:
  526. CInterlockedInt m_referenceCount;
  527. CTexture* m_pOwner;
  528. };
  529. //////////////////////////////////////////////////////////////////////////
  530. //
  531. // CReferenceToHandleTexture is a special implementation of ITexture
  532. // to be used solely for binding the texture handle when rendering.
  533. // It is used when a D3D texture handle is available, but should be used
  534. // at a higher level of abstraction requiring an ITexture or ITextureInternal.
  535. //
  536. //////////////////////////////////////////////////////////////////////////
  537. class CReferenceToHandleTexture : public ITextureInternal
  538. {
  539. public:
  540. CReferenceToHandleTexture();
  541. virtual ~CReferenceToHandleTexture();
  542. virtual const char *GetName( void ) const { return m_Name.String(); }
  543. const char *GetTextureGroupName( void ) const { return m_TextureGroupName.String(); }
  544. // Stats about the texture itself
  545. virtual ImageFormat GetImageFormat() const { return IMAGE_FORMAT_UNKNOWN; }
  546. virtual NormalDecodeMode_t GetNormalDecodeMode() const { return NORMAL_DECODE_NONE; }
  547. virtual int GetMappingWidth() const { return 1; }
  548. virtual int GetMappingHeight() const { return 1; }
  549. virtual int GetActualWidth() const { return 1; }
  550. virtual int GetActualHeight() const { return 1; }
  551. virtual int GetNumAnimationFrames() const { return 1; }
  552. virtual bool IsTranslucent() const { return false; }
  553. virtual void GetReflectivity( Vector& reflectivity ) { reflectivity.Zero(); }
  554. // Reference counting
  555. virtual void IncrementReferenceCount( ) { ++ m_nRefCount; }
  556. virtual void DecrementReferenceCount( ) { -- m_nRefCount; }
  557. virtual int GetReferenceCount( ) { return m_nRefCount; }
  558. // Used to modify the texture bits (procedural textures only)
  559. virtual void SetTextureRegenerator( ITextureRegenerator *pTextureRegen ) { NULL; }
  560. // Little helper polling methods
  561. virtual bool IsNormalMap( ) const { return false; }
  562. virtual bool IsCubeMap( void ) const { return false; }
  563. virtual bool IsRenderTarget( ) const { return false; }
  564. virtual bool IsTempRenderTarget( void ) const { return false; }
  565. virtual bool IsProcedural() const { return true; }
  566. virtual bool IsMipmapped() const { return false; }
  567. virtual bool IsError() const { return false; }
  568. // For volume textures
  569. virtual bool IsVolumeTexture() const { return false; }
  570. virtual int GetMappingDepth() const { return 1; }
  571. virtual int GetActualDepth() const { return 1; }
  572. // Releases the texture's hw memory
  573. void ReleaseMemory() { NULL; }
  574. virtual void OnRestore() { NULL; }
  575. // Sets the filtering modes on the texture we're modifying
  576. void SetFilteringAndClampingMode( bool bOnlyLodValues = false ) { NULL; }
  577. void Download( Rect_t *pRect = NULL, int nAdditionalCreationFlags = 0 ) { NULL; }
  578. // Loads up information about the texture
  579. virtual void Precache() { NULL; }
  580. // FIXME: Bogus methods... can we please delete these?
  581. virtual void GetLowResColorSample( float s, float t, float *color ) const { NULL; }
  582. // Gets texture resource data of the specified type.
  583. // Params:
  584. // eDataType type of resource to retrieve.
  585. // pnumBytes on return is the number of bytes available in the read-only data buffer or is undefined
  586. // Returns:
  587. // pointer to the resource data, or NULL. Note that the data from this pointer can disappear when
  588. // the texture goes away - you want to copy this data!
  589. virtual void *GetResourceData( uint32 eDataType, size_t *pNumBytes ) const { return NULL; }
  590. virtual int GetApproximateVidMemBytes( void ) const { return 32; }
  591. // Stretch blit the framebuffer into this texture.
  592. virtual void CopyFrameBufferToMe( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ) { NULL; }
  593. virtual void CopyMeToFrameBuffer( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ) { NULL; }
  594. virtual ITexture *GetEmbeddedTexture( int nIndex ) { return ( nIndex == 0 ) ? this : NULL; }
  595. // Get the shaderapi texture handle associated w/ a particular frame
  596. virtual ShaderAPITextureHandle_t GetTextureHandle( int nFrame, int nTextureChannel = 0 ) { return m_hTexture; }
  597. // Bind the texture
  598. virtual void Bind( Sampler_t sampler );
  599. virtual void Bind( Sampler_t sampler1, int nFrame, Sampler_t sampler2 = (Sampler_t) -1 );
  600. virtual void BindVertexTexture( VertexTextureSampler_t stage, int nFrame );
  601. // Set this texture as a render target
  602. bool SetRenderTarget( int nRenderTargetID ) { return SetRenderTarget( nRenderTargetID, NULL ); }
  603. // Set this texture as a render target (optionally set depth texture as depth buffer as well)
  604. bool SetRenderTarget( int nRenderTargetID, ITexture *pDepthTexture) { return false; }
  605. virtual void MarkAsPreloaded( bool bSet ) { NULL; }
  606. virtual bool IsPreloaded() const { return true; }
  607. virtual void MarkAsExcluded( bool bSet, int nDimensionsLimit ) { NULL; }
  608. virtual bool UpdateExcludedState( void ) { return true; }
  609. // Retrieve the vtf flags mask
  610. virtual unsigned int GetFlags( void ) const { return 0; }
  611. virtual void ForceLODOverride( int iNumLodsOverrideUpOrDown ) { NULL; }
  612. virtual void ReloadFilesInList( IFileList *pFilesToReload ) {}
  613. // Save texture to a file.
  614. virtual bool SaveToFile( const char *fileName ) { return false; }
  615. virtual bool AsyncReadTextureFromFile( IVTFTexture* pVTFTexture, unsigned int nAdditionalCreationFlags ) { Assert( !"Should never get here." ); return false; }
  616. virtual void AsyncCancelReadTexture() { Assert( !"Should never get here." ); }
  617. virtual void CopyToStagingTexture( ITexture* pDstTex ) { Assert( !"Should never get here." ); };
  618. // Map and unmap. These can fail. And can cause a very significant perf penalty. Be very careful with them.
  619. virtual void Map( void** pOutBits, int* pOutPitch ) { }
  620. virtual void Unmap() { }
  621. virtual ResidencyType_t GetCurrentResidence() const { return RESIDENT_FULL; }
  622. virtual ResidencyType_t GetTargetResidence() const { return RESIDENT_FULL; }
  623. virtual bool MakeResident( ResidencyType_t newResidence ) { Assert( !"Unimpl" ); return true; }
  624. virtual void UpdateLodBias() {}
  625. virtual void SetErrorTexture( bool isErrorTexture ) { }
  626. protected:
  627. #ifdef _DEBUG
  628. char *m_pDebugName;
  629. #endif
  630. CUtlSymbol m_Name;
  631. // What texture group this texture is in (winds up setting counters based on the group name,
  632. // then the budget panel views the counters).
  633. CUtlSymbol m_TextureGroupName;
  634. // The set of texture ids for each animation frame
  635. ShaderAPITextureHandle_t m_hTexture;
  636. // Refcount
  637. int m_nRefCount;
  638. public:
  639. virtual void DeleteIfUnreferenced();
  640. void FixupTexture( const void *pData, int nSize, LoaderError_t loaderError ) { NULL; }
  641. void SwapContents( ITexture *pOther ) { NULL; }
  642. public:
  643. void SetName( char const *szName );
  644. void InitFromHandle(
  645. const char *pTextureName,
  646. const char *pTextureGroupName,
  647. ShaderAPITextureHandle_t hTexture );
  648. };
  649. CReferenceToHandleTexture::CReferenceToHandleTexture() :
  650. m_hTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ),
  651. #ifdef _DEBUG
  652. m_pDebugName( NULL ),
  653. #endif
  654. m_nRefCount( 0 )
  655. {
  656. NULL;
  657. }
  658. CReferenceToHandleTexture::~CReferenceToHandleTexture()
  659. {
  660. #ifdef _DEBUG
  661. if ( m_nRefCount != 0 )
  662. {
  663. Warning( "Reference Count(%d) != 0 in ~CReferenceToHandleTexture for texture \"%s\"\n", m_nRefCount, m_Name.String() );
  664. }
  665. if ( m_pDebugName )
  666. {
  667. delete [] m_pDebugName;
  668. }
  669. #endif
  670. }
  671. void CReferenceToHandleTexture::SetName( char const *szName )
  672. {
  673. // normalize and convert to a symbol
  674. char szCleanName[MAX_PATH];
  675. m_Name = NormalizeTextureName( szName, szCleanName, sizeof( szCleanName ) );
  676. #ifdef _DEBUG
  677. if ( m_pDebugName )
  678. {
  679. delete [] m_pDebugName;
  680. }
  681. int nLen = V_strlen( szCleanName ) + 1;
  682. m_pDebugName = new char[nLen];
  683. V_memcpy( m_pDebugName, szCleanName, nLen );
  684. #endif
  685. }
  686. void CReferenceToHandleTexture::InitFromHandle( const char *pTextureName, const char *pTextureGroupName, ShaderAPITextureHandle_t hTexture )
  687. {
  688. SetName( pTextureName );
  689. m_TextureGroupName = pTextureGroupName;
  690. m_hTexture = hTexture;
  691. }
  692. void CReferenceToHandleTexture::Bind( Sampler_t sampler )
  693. {
  694. if ( g_pShaderDevice->IsUsingGraphics() )
  695. {
  696. g_pShaderAPI->BindTexture( sampler, m_hTexture );
  697. }
  698. }
  699. // TODO: make paired textures work with mat_texture_list
  700. void CReferenceToHandleTexture::Bind( Sampler_t sampler1, int nFrame, Sampler_t sampler2 /* = -1 */ )
  701. {
  702. if ( g_pShaderDevice->IsUsingGraphics() )
  703. {
  704. g_pShaderAPI->BindTexture( sampler1, m_hTexture );
  705. }
  706. }
  707. void CReferenceToHandleTexture::BindVertexTexture( VertexTextureSampler_t sampler, int nFrame )
  708. {
  709. if ( g_pShaderDevice->IsUsingGraphics() )
  710. {
  711. g_pShaderAPI->BindVertexTexture( sampler, m_hTexture );
  712. }
  713. }
  714. void CReferenceToHandleTexture::DeleteIfUnreferenced()
  715. {
  716. if ( m_nRefCount > 0 )
  717. return;
  718. TextureManager()->RemoveTexture( this );
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Fixed-size allocator
  722. //-----------------------------------------------------------------------------
  723. //DEFINE_FIXEDSIZE_ALLOCATOR( CTexture, 1024, true );
  724. //-----------------------------------------------------------------------------
  725. // Static instance of VTF texture
  726. //-----------------------------------------------------------------------------
  727. #define MAX_RENDER_THREADS 4
  728. // For safety's sake, we allow any of the threads that intersect with rendering
  729. // to have their own state vars. In practice, we expect only the matqueue thread
  730. // and the main thread to ever hit s_pVTFTexture.
  731. static IVTFTexture *s_pVTFTexture[ MAX_RENDER_THREADS ] = { NULL };
  732. // We only expect that the main thread or the matqueue thread to actually touch
  733. // these, but we still need a NULL and size of 0 for the other threads.
  734. static void *s_pOptimalReadBuffer[ MAX_RENDER_THREADS ] = { NULL };
  735. static int s_nOptimalReadBufferSize[ MAX_RENDER_THREADS ] = { 0 };
  736. //-----------------------------------------------------------------------------
  737. // Class factory methods
  738. //-----------------------------------------------------------------------------
  739. ITextureInternal *ITextureInternal::CreateFileTexture( const char *pFileName, const char *pTextureGroupName )
  740. {
  741. CTexture *pTex = new CTexture;
  742. pTex->InitFileTexture( pFileName, pTextureGroupName );
  743. return pTex;
  744. }
  745. ITextureInternal *ITextureInternal::CreateReferenceTextureFromHandle(
  746. const char *pTextureName,
  747. const char *pTextureGroupName,
  748. ShaderAPITextureHandle_t hTexture )
  749. {
  750. CReferenceToHandleTexture *pTex = new CReferenceToHandleTexture;
  751. pTex->InitFromHandle( pTextureName, pTextureGroupName, hTexture );
  752. return pTex;
  753. }
  754. ITextureInternal *ITextureInternal::CreateProceduralTexture(
  755. const char *pTextureName,
  756. const char *pTextureGroupName,
  757. int w,
  758. int h,
  759. int d,
  760. ImageFormat fmt,
  761. int nFlags,
  762. ITextureRegenerator *generator)
  763. {
  764. CTexture *pTex = new CTexture;
  765. pTex->InitProceduralTexture( pTextureName, pTextureGroupName, w, h, d, fmt, nFlags, generator );
  766. pTex->IncrementReferenceCount();
  767. return pTex;
  768. }
  769. // GR - named RT
  770. ITextureInternal *ITextureInternal::CreateRenderTarget(
  771. const char *pRTName,
  772. int w,
  773. int h,
  774. RenderTargetSizeMode_t sizeMode,
  775. ImageFormat fmt,
  776. RenderTargetType_t type,
  777. unsigned int textureFlags,
  778. unsigned int renderTargetFlags )
  779. {
  780. CTexture *pTex = new CTexture;
  781. pTex->InitRenderTarget( pRTName, w, h, sizeMode, fmt, type, textureFlags, renderTargetFlags );
  782. return pTex;
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Rebuild and exisiting render target in place.
  786. //-----------------------------------------------------------------------------
  787. void ITextureInternal::ChangeRenderTarget(
  788. ITextureInternal *pTex,
  789. int w,
  790. int h,
  791. RenderTargetSizeMode_t sizeMode,
  792. ImageFormat fmt,
  793. RenderTargetType_t type,
  794. unsigned int textureFlags,
  795. unsigned int renderTargetFlags )
  796. {
  797. pTex->ReleaseMemory();
  798. dynamic_cast< CTexture * >(pTex)->InitRenderTarget( pTex->GetName(), w, h, sizeMode, fmt, type, textureFlags, renderTargetFlags );
  799. }
  800. void ITextureInternal::Destroy( ITextureInternal *pTex, bool bSkipTexMgrCheck )
  801. {
  802. #ifdef STAGING_ONLY
  803. if ( !bSkipTexMgrCheck && TextureManager()->HasPendingTextureDestroys() )
  804. {
  805. // Multithreading badness. This will cause a crash later! Grab JohnS or McJohn know!
  806. DebuggerBreakIfDebugging_StagingOnly();
  807. }
  808. #endif
  809. int iIndex = g_pTextureRefList->Find( static_cast<ITexture*>( pTex ) );
  810. if ( iIndex != g_pTextureRefList->InvalidIndex () )
  811. {
  812. if ( g_pTextureRefList->Element(iIndex) != 0 )
  813. {
  814. int currentCount = g_pTextureRefList->Element( iIndex );
  815. Warning( "Destroying a texture that is in the queue: %s (%p): %d!\n", pTex->GetName(), pTex, currentCount );
  816. }
  817. }
  818. delete pTex;
  819. }
  820. //-----------------------------------------------------------------------------
  821. // Constructor, destructor
  822. //-----------------------------------------------------------------------------
  823. CTexture::CTexture() : m_ImageFormat( IMAGE_FORMAT_UNKNOWN )
  824. {
  825. m_dimsActual.m_nMipCount = 0;
  826. m_dimsMapping.m_nWidth = 0;
  827. m_dimsMapping.m_nHeight = 0;
  828. m_dimsMapping.m_nDepth = 1;
  829. m_dimsActual.m_nWidth = 0;
  830. m_dimsActual.m_nHeight = 0;
  831. m_dimsActual.m_nDepth = 1;
  832. m_dimsAllocated.m_nWidth = 0;
  833. m_dimsAllocated.m_nHeight = 0;
  834. m_dimsAllocated.m_nDepth = 0;
  835. m_dimsAllocated.m_nMipCount = 0;
  836. m_nStreamingMips = 0;
  837. m_nRefCount = 0;
  838. m_nFlags = 0;
  839. m_nInternalFlags = 0;
  840. m_pTextureHandles = NULL;
  841. m_nFrameCount = 0;
  842. VectorClear( m_vecReflectivity );
  843. m_pTextureRegenerator = NULL;
  844. m_nOriginalRenderTargetType = NO_RENDER_TARGET;
  845. m_RenderTargetSizeMode = RT_SIZE_NO_CHANGE;
  846. m_nOriginalRTWidth = m_nOriginalRTHeight = 1;
  847. m_LowResImageWidth = 0;
  848. m_LowResImageHeight = 0;
  849. m_pLowResImage = NULL;
  850. m_pStreamingJob = NULL;
  851. m_residenceTarget = RESIDENT_NONE;
  852. m_residenceCurrent = RESIDENT_NONE;
  853. m_lodClamp = 0;
  854. m_lodBiasInitial = 0;
  855. m_lodBiasCurrent = 0;
  856. m_nDesiredDimensionLimit = 0;
  857. m_nActualDimensionLimit = 0;
  858. memset( &m_cachedFileLodSettings, 0, sizeof( m_cachedFileLodSettings ) );
  859. #ifdef _DEBUG
  860. m_pDebugName = NULL;
  861. #endif
  862. m_pStreamingVTF = NULL;
  863. m_bStreamingFileReadFailed = false;
  864. }
  865. CTexture::~CTexture()
  866. {
  867. #ifdef _DEBUG
  868. if ( m_nRefCount != 0 )
  869. {
  870. Warning( "Reference Count(%d) != 0 in ~CTexture for texture \"%s\"\n", (int)m_nRefCount, m_Name.String() );
  871. }
  872. #endif
  873. Shutdown();
  874. #ifdef _DEBUG
  875. if ( m_pDebugName )
  876. {
  877. // delete[] m_pDebugName;
  878. }
  879. #endif
  880. // Deliberately stomp our VTable so that we can detect cases where code tries to access freed materials.
  881. int *p = (int *)this;
  882. *p = 0xdeadbeef;
  883. }
  884. //-----------------------------------------------------------------------------
  885. // Initializes the texture
  886. //-----------------------------------------------------------------------------
  887. void CTexture::Init( int w, int h, int d, ImageFormat fmt, int iFlags, int iFrameCount )
  888. {
  889. Assert( iFrameCount > 0 );
  890. // This is necessary to prevent blowing away the allocated state,
  891. // which is necessary for the ReleaseTextureHandles call below to work.
  892. SetErrorTexture( false );
  893. // free and release previous data
  894. // cannot change to new intialization parameters yet
  895. FreeShaderAPITextures();
  896. ReleaseTextureHandles();
  897. // update to new initialization parameters
  898. // these are the *desired* new values
  899. m_dimsMapping.m_nWidth = w;
  900. m_dimsMapping.m_nHeight = h;
  901. m_dimsMapping.m_nDepth = d;
  902. m_ImageFormat = fmt;
  903. m_nFrameCount = iFrameCount;
  904. // We don't know the actual width and height until we get it ready to render
  905. m_dimsActual.m_nWidth = m_dimsActual.m_nHeight = 0;
  906. m_dimsActual.m_nDepth = 1;
  907. m_dimsActual.m_nMipCount = 0;
  908. m_dimsAllocated.m_nWidth = 0;
  909. m_dimsAllocated.m_nHeight = 0;
  910. m_dimsAllocated.m_nDepth = 0;
  911. m_dimsAllocated.m_nMipCount = 0;
  912. m_nStreamingMips = 0;
  913. // Clear the m_nFlags bit. If we don't, then m_nFrameCount may end up being 1, and
  914. // TEXTUREFLAGS_DEPTHRENDERTARGET could be set.
  915. m_nFlags &= ~TEXTUREFLAGS_DEPTHRENDERTARGET;
  916. m_nFlags |= iFlags;
  917. CancelStreamingJob( false );
  918. m_residenceTarget = RESIDENT_NONE;
  919. m_residenceCurrent = RESIDENT_NONE;
  920. m_lodClamp = 0;
  921. m_lodBiasInitial = 0;
  922. m_lodBiasCurrent = 0;
  923. AllocateTextureHandles();
  924. }
  925. //-----------------------------------------------------------------------------
  926. // Shuts down the texture
  927. //-----------------------------------------------------------------------------
  928. void CTexture::Shutdown()
  929. {
  930. Assert( m_pStreamingVTF == NULL );
  931. // Clean up the low-res texture
  932. delete[] m_pLowResImage;
  933. m_pLowResImage = 0;
  934. // Clean up the resources data
  935. for ( DataChunk const *pDataChunk = m_arrDataChunks.Base(),
  936. *pDataChunkEnd = pDataChunk + m_arrDataChunks.Count();
  937. pDataChunk < pDataChunkEnd; ++pDataChunk )
  938. {
  939. pDataChunk->Deallocate();
  940. }
  941. m_arrDataChunks.RemoveAll();
  942. // Frees the texture regen class
  943. if ( m_pTextureRegenerator )
  944. {
  945. m_pTextureRegenerator->Release();
  946. m_pTextureRegenerator = NULL;
  947. }
  948. CancelStreamingJob( false );
  949. m_residenceTarget = RESIDENT_NONE;
  950. m_residenceCurrent = RESIDENT_NONE;
  951. m_lodClamp = 0;
  952. m_lodBiasInitial = 0;
  953. m_lodBiasCurrent = 0;
  954. // This deletes the textures
  955. FreeShaderAPITextures();
  956. ReleaseTextureHandles();
  957. NotifyUnloadedFile();
  958. }
  959. void CTexture::ReleaseMemory()
  960. {
  961. FreeShaderAPITextures();
  962. NotifyUnloadedFile();
  963. }
  964. IVTFTexture *CTexture::GetScratchVTFTexture( )
  965. {
  966. const bool cbThreadInMatQueue = ( MaterialSystem()->GetRenderThreadId() == ThreadGetCurrentId() ); cbThreadInMatQueue;
  967. Assert( cbThreadInMatQueue || ThreadInMainThread() );
  968. const int ti = GetThreadId();
  969. if ( !s_pVTFTexture[ ti ] )
  970. s_pVTFTexture[ ti ] = CreateVTFTexture();
  971. return s_pVTFTexture[ ti ];
  972. }
  973. void CTexture::ReleaseScratchVTFTexture( IVTFTexture* tex )
  974. {
  975. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  976. const bool cbThreadInMatQueue = ( MaterialSystem()->GetRenderThreadId() == ThreadGetCurrentId() ); cbThreadInMatQueue;
  977. Assert( cbThreadInMatQueue || ThreadInMainThread() );
  978. Assert( m_pStreamingVTF == NULL || ThreadInMainThread() ); // Can only manipulate m_pStreamingVTF to release safely in main thread.
  979. if ( m_pStreamingVTF )
  980. {
  981. Assert( tex == m_pStreamingVTF );
  982. TextureManager()->ReleaseAsyncScratchVTF( m_pStreamingVTF );
  983. m_pStreamingVTF = NULL;
  984. return;
  985. }
  986. // Normal scratch main-thread vtf doesn't need to do anything.
  987. }
  988. //-----------------------------------------------------------------------------
  989. //
  990. // Various initialization methods
  991. //
  992. //-----------------------------------------------------------------------------
  993. void CTexture::ApplyRenderTargetSizeMode( int &width, int &height, ImageFormat fmt )
  994. {
  995. width = m_nOriginalRTWidth;
  996. height = m_nOriginalRTHeight;
  997. switch ( m_RenderTargetSizeMode )
  998. {
  999. case RT_SIZE_FULL_FRAME_BUFFER:
  1000. {
  1001. MaterialSystem()->GetRenderTargetFrameBufferDimensions( width, height );
  1002. if( !HardwareConfig()->SupportsNonPow2Textures() )
  1003. {
  1004. width = FloorPow2( width + 1 );
  1005. height = FloorPow2( height + 1 );
  1006. }
  1007. }
  1008. break;
  1009. case RT_SIZE_FULL_FRAME_BUFFER_ROUNDED_UP:
  1010. {
  1011. MaterialSystem()->GetRenderTargetFrameBufferDimensions( width, height );
  1012. if( !HardwareConfig()->SupportsNonPow2Textures() )
  1013. {
  1014. width = CeilPow2( width );
  1015. height = CeilPow2( height );
  1016. }
  1017. }
  1018. break;
  1019. case RT_SIZE_PICMIP:
  1020. {
  1021. int fbWidth, fbHeight;
  1022. MaterialSystem()->GetRenderTargetFrameBufferDimensions( fbWidth, fbHeight );
  1023. int picmip = g_config.skipMipLevels;
  1024. while( picmip > 0 )
  1025. {
  1026. width >>= 1;
  1027. height >>= 1;
  1028. picmip--;
  1029. }
  1030. while( width > fbWidth )
  1031. {
  1032. width >>= 1;
  1033. }
  1034. while( height > fbHeight )
  1035. {
  1036. height >>= 1;
  1037. }
  1038. }
  1039. break;
  1040. case RT_SIZE_DEFAULT:
  1041. {
  1042. // Assume that the input is pow2.
  1043. Assert( ( width & ( width - 1 ) ) == 0 );
  1044. Assert( ( height & ( height - 1 ) ) == 0 );
  1045. int fbWidth, fbHeight;
  1046. MaterialSystem()->GetRenderTargetFrameBufferDimensions( fbWidth, fbHeight );
  1047. while( width > fbWidth )
  1048. {
  1049. width >>= 1;
  1050. }
  1051. while( height > fbHeight )
  1052. {
  1053. height >>= 1;
  1054. }
  1055. }
  1056. break;
  1057. case RT_SIZE_HDR:
  1058. {
  1059. MaterialSystem()->GetRenderTargetFrameBufferDimensions( width, height );
  1060. width = width / 4;
  1061. height = height / 4;
  1062. }
  1063. break;
  1064. case RT_SIZE_OFFSCREEN:
  1065. {
  1066. int fbWidth, fbHeight;
  1067. MaterialSystem()->GetRenderTargetFrameBufferDimensions( fbWidth, fbHeight );
  1068. // Shrink the buffer if it's bigger than back buffer. Otherwise, don't mess with it.
  1069. while( (width > fbWidth) || (height > fbHeight) )
  1070. {
  1071. width >>= 1;
  1072. height >>= 1;
  1073. }
  1074. }
  1075. break;
  1076. case RT_SIZE_LITERAL:
  1077. {
  1078. // Literal means literally don't mess with the dimensions. Unlike what OFFSCREEN does,
  1079. // which is totally to mess with the dimensions.
  1080. }
  1081. break;
  1082. case RT_SIZE_LITERAL_PICMIP:
  1083. {
  1084. // Don't do anything here, like literal. Later, we will pay attention to picmip settings s.t.
  1085. // these render targets look like other textures wrt Mapping Dimensions vs Actual Dimensions.
  1086. }
  1087. break;
  1088. case RT_SIZE_REPLAY_SCREENSHOT:
  1089. {
  1090. // Compute all possible resolutions if first time we're running this function
  1091. static bool bReplayInit = false;
  1092. static int m_aScreenshotWidths[ 3 ][ 2 ];
  1093. static ConVarRef replay_screenshotresolution( "replay_screenshotresolution" );
  1094. if ( !bReplayInit )
  1095. {
  1096. bReplayInit = true;
  1097. for ( int iAspect = 0; iAspect < 3; ++iAspect )
  1098. {
  1099. for ( int iRes = 0; iRes < 2; ++iRes )
  1100. {
  1101. int nWidth = (int)FastPow2( 9 + iRes );
  1102. m_aScreenshotWidths[ iAspect ][ iRes ] = nWidth;
  1103. }
  1104. }
  1105. }
  1106. // Get dimensions for unpadded image
  1107. int nUnpaddedWidth, nUnpaddedHeight;
  1108. // Figure out the proper screenshot size to use based on the aspect ratio
  1109. int nScreenWidth, nScreenHeight;
  1110. MaterialSystem()->GetRenderTargetFrameBufferDimensions( nScreenWidth, nScreenHeight );
  1111. float flAspectRatio = (float)nScreenWidth / nScreenHeight;
  1112. // Get the screenshot res
  1113. int iRes = clamp( replay_screenshotresolution.GetInt(), 0, 1 );
  1114. int iAspect;
  1115. if ( flAspectRatio == 16.0f/9 )
  1116. {
  1117. iAspect = 0;
  1118. }
  1119. else if ( flAspectRatio == 16.0f/10 )
  1120. {
  1121. iAspect = 1;
  1122. }
  1123. else
  1124. {
  1125. iAspect = 2; // 4:3
  1126. }
  1127. static float s_flInvAspectRatios[3] = { 9.0f/16.0f, 10.0f/16, 3.0f/4 };
  1128. nUnpaddedWidth = min( nScreenWidth, m_aScreenshotWidths[ iAspect ][ iRes ] );
  1129. nUnpaddedHeight = m_aScreenshotWidths[ iAspect ][ iRes ] * s_flInvAspectRatios[ iAspect ];
  1130. // Get dimensions for padded image based on unpadded size - must be power of 2 for a material/texture
  1131. width = SmallestPowerOfTwoGreaterOrEqual( nUnpaddedWidth );
  1132. height = SmallestPowerOfTwoGreaterOrEqual( nUnpaddedHeight );
  1133. }
  1134. break;
  1135. default:
  1136. {
  1137. if ( !HushAsserts() )
  1138. {
  1139. Assert( m_RenderTargetSizeMode == RT_SIZE_NO_CHANGE );
  1140. Assert( m_nOriginalRenderTargetType == RENDER_TARGET_NO_DEPTH ); // Only can use NO_CHANGE if we don't have a depth buffer.
  1141. }
  1142. }
  1143. break;
  1144. }
  1145. }
  1146. void CTexture::CopyToStagingTexture( ITexture* pDstTex )
  1147. {
  1148. Assert( pDstTex );
  1149. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1150. // Need to flush any commands in flight on our side of things
  1151. materials->Flush( false );
  1152. CTexture* pDstTexActual = assert_cast< CTexture* >( pDstTex );
  1153. // Then do the copy if everything is on the up and up.
  1154. if ( ( m_pTextureHandles == NULL || m_nFrameCount == 0 ) || ( pDstTexActual->m_pTextureHandles == NULL || pDstTexActual->m_nFrameCount == 0 ) )
  1155. {
  1156. Assert( !"Can't copy to a non-existent texture, may need to generate or something." );
  1157. return;
  1158. }
  1159. // Make sure we've actually got the right surface types.
  1160. Assert( m_nFlags & TEXTUREFLAGS_RENDERTARGET );
  1161. Assert( pDstTex->GetFlags() & TEXTUREFLAGS_STAGING_MEMORY );
  1162. g_pShaderAPI->CopyRenderTargetToScratchTexture( m_pTextureHandles[0], pDstTexActual->m_pTextureHandles[0] );
  1163. }
  1164. void CTexture::Map( void** pOutBits, int* pOutPitch )
  1165. {
  1166. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1167. // Must be a staging texture to avoid catastrophic perf fail.
  1168. Assert( m_nFlags & TEXTUREFLAGS_STAGING_MEMORY );
  1169. if ( m_pTextureHandles == NULL || m_nFrameCount == 0 )
  1170. {
  1171. Assert( !"Can't map a non-existent texture, may need to generate or something." );
  1172. return;
  1173. }
  1174. g_pShaderAPI->LockRect( pOutBits, pOutPitch, m_pTextureHandles[ 0 ], 0, 0, 0, GetActualWidth(), GetActualHeight(), false, true );
  1175. }
  1176. void CTexture::Unmap()
  1177. {
  1178. if ( m_pTextureHandles == NULL || m_nFrameCount == 0 )
  1179. {
  1180. Assert( !"Can't unmap a non-existent texture, may need to generate or something." );
  1181. return;
  1182. }
  1183. g_pShaderAPI->UnlockRect( m_pTextureHandles[ 0 ], 0 );
  1184. }
  1185. bool CTexture::MakeResident( ResidencyType_t newResidence )
  1186. {
  1187. Assert( ( GetFlags() & TEXTUREFLAGS_STREAMABLE ) != 0 );
  1188. // If we already think we're supposed to go here, nothing to do and we should report success.
  1189. if ( m_residenceTarget == newResidence )
  1190. return true;
  1191. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  1192. // What are we moving towards?
  1193. switch ( newResidence )
  1194. {
  1195. case RESIDENT_NONE:
  1196. MakeNonResident();
  1197. return true;
  1198. case RESIDENT_PARTIAL:
  1199. MakePartiallyResident();
  1200. return true;
  1201. case RESIDENT_FULL:
  1202. return MakeFullyResident();
  1203. default:
  1204. Assert( !"Missing switch statement" );
  1205. };
  1206. return false;
  1207. }
  1208. void CTexture::UpdateLodBias()
  1209. {
  1210. if ( m_lodBiasInitial == 0.0f )
  1211. return;
  1212. // Only perform adjustment once per frame.
  1213. if ( m_lastLodBiasAdjustFrame == g_FrameNum )
  1214. return;
  1215. bool bPopIn = mat_lodin_time.GetFloat() == 0;
  1216. if ( bPopIn && m_lodBiasInitial == 0.0f )
  1217. return;
  1218. if ( !bPopIn )
  1219. m_lodBiasCurrent = m_lodBiasInitial - ( Plat_FloatTime() - m_lodBiasStartTime ) / mat_lodin_time.GetFloat() * m_lodBiasInitial;
  1220. else
  1221. m_lodBiasCurrent = m_lodBiasInitial = 0.0f;
  1222. // If we're supposed to pop in when the object isn't visible and we have the opportunity...
  1223. if ( mat_lodin_hidden_pop.GetBool() && m_lastLodBiasAdjustFrame != g_FrameNum - 1 )
  1224. m_lodBiasCurrent = m_lodBiasInitial = 0.0f;
  1225. if ( m_lodBiasCurrent <= 0.0f )
  1226. {
  1227. m_lodBiasCurrent = m_lodBiasInitial = 0.0f;
  1228. m_lodBiasStartTime = 0;
  1229. }
  1230. m_lastLodBiasAdjustFrame = g_FrameNum;
  1231. SetFilteringAndClampingMode( true );
  1232. }
  1233. void CTexture::MakeNonResident()
  1234. {
  1235. if ( m_residenceCurrent != RESIDENT_NONE )
  1236. Shutdown();
  1237. m_residenceCurrent = m_residenceTarget = RESIDENT_NONE;
  1238. // Clear our the streamable fine flag to ensure we reload properly.
  1239. m_nFlags &= ~TEXTUREFLAGS_STREAMABLE_FINE;
  1240. }
  1241. void CTexture::MakePartiallyResident()
  1242. {
  1243. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  1244. ResidencyType_t oldCurrentResidence = m_residenceCurrent;
  1245. ResidencyType_t oldTargetResidence = m_residenceTarget;
  1246. m_residenceCurrent = m_residenceTarget = RESIDENT_PARTIAL;
  1247. if ( oldCurrentResidence == RESIDENT_PARTIAL )
  1248. {
  1249. Assert( oldTargetResidence == RESIDENT_FULL ); oldTargetResidence;
  1250. // If we are already partially resident, then just cancel our job to stream in,
  1251. // cause we don't need that data anymore.
  1252. CancelStreamingJob();
  1253. return;
  1254. }
  1255. Assert( oldCurrentResidence == RESIDENT_FULL );
  1256. // Clear the fine bit.
  1257. m_nFlags &= ~TEXTUREFLAGS_STREAMABLE_FINE;
  1258. if ( HardwareConfig()->CanStretchRectFromTextures() )
  1259. {
  1260. m_lodClamp = 0;
  1261. m_lodBiasInitial = m_lodBiasCurrent = 0;
  1262. m_lastLodBiasAdjustFrame = g_FrameNum;
  1263. DownloadTexture( NULL, true );
  1264. }
  1265. else
  1266. {
  1267. // Oops. We were overzealous above--restore the residency to what it was.
  1268. m_residenceCurrent = oldCurrentResidence;
  1269. // Immediately display it as lower res (for consistency) but if we can't (efficiently)
  1270. // copy we just have to re-read everything from disk. Lame!
  1271. m_lodClamp = 3;
  1272. m_lodBiasInitial = m_lodBiasCurrent = 0;
  1273. m_lastLodBiasAdjustFrame = g_FrameNum;
  1274. SetFilteringAndClampingMode( true );
  1275. SafeAssign( &m_pStreamingJob, new CTextureStreamingJob( this ) );
  1276. MaterialSystem()->AsyncFindTexture( GetName(), GetTextureGroupName(), m_pStreamingJob, (void*) RESIDENT_PARTIAL, false, TEXTUREFLAGS_STREAMABLE_COARSE );
  1277. }
  1278. }
  1279. bool CTexture::MakeFullyResident()
  1280. {
  1281. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  1282. ResidencyType_t oldCurrentResidence = m_residenceCurrent;
  1283. ResidencyType_t oldTargetResidence = m_residenceTarget;
  1284. if ( oldCurrentResidence == RESIDENT_FULL )
  1285. {
  1286. // This isn't a requirement, but right now it would be a mistake
  1287. Assert( !HardwareConfig()->CanStretchRectFromTextures() );
  1288. Assert( oldTargetResidence == RESIDENT_PARTIAL ); oldTargetResidence;
  1289. m_residenceCurrent = m_residenceTarget = RESIDENT_FULL;
  1290. m_lodClamp = 0;
  1291. m_lodBiasInitial = m_lodBiasCurrent = 0;
  1292. m_lastLodBiasAdjustFrame = g_FrameNum;
  1293. SetFilteringAndClampingMode( true );
  1294. CancelStreamingJob();
  1295. return true;
  1296. }
  1297. Assert( m_residenceTarget == RESIDENT_PARTIAL && m_residenceCurrent == RESIDENT_PARTIAL );
  1298. Assert( m_pStreamingJob == NULL );
  1299. SafeAssign( &m_pStreamingJob, new CTextureStreamingJob( this ) );
  1300. MaterialSystem()->AsyncFindTexture( GetName(), GetTextureGroupName(), m_pStreamingJob, (void*) RESIDENT_FULL, false, TEXTUREFLAGS_STREAMABLE_FINE );
  1301. m_residenceTarget = RESIDENT_FULL;
  1302. return true;
  1303. }
  1304. void CTexture::CancelStreamingJob( bool bJobMustExist )
  1305. {
  1306. bJobMustExist; // Only used by asserts ensuring correctness, so reference it for release builds.
  1307. // Most callers should be aware of whether the job exists, but for cleanup we don't know and we
  1308. // should be safe in that case.
  1309. Assert( !bJobMustExist || m_pStreamingJob );
  1310. if ( !m_pStreamingJob )
  1311. return;
  1312. // The streaming job and this (this texture) have a circular reference count--each one holds one for the other.
  1313. // As a result, this means that having the streaming job forget about the texture may cause the texture to go
  1314. // away completely! So we need to ensure that after we call "ForgetOwner" that we don't touch any instance
  1315. // variables.
  1316. CTextureStreamingJob* pJob = m_pStreamingJob;
  1317. m_pStreamingJob = NULL;
  1318. pJob->ForgetOwner( this );
  1319. SafeRelease( &pJob );
  1320. }
  1321. void CTexture::OnStreamingJobComplete( ResidencyType_t newResidenceCurrent )
  1322. {
  1323. Assert( m_pStreamingJob );
  1324. // It's probable that if this assert fires, we should just do nothing in here and return--but I'd
  1325. // like to see that happen to be sure.
  1326. Assert( newResidenceCurrent == m_residenceTarget );
  1327. m_residenceCurrent = newResidenceCurrent;
  1328. // Only do lod biasing for stream in. For stream out, just dump to lowest quality right away.
  1329. if ( m_residenceCurrent == RESIDENT_FULL )
  1330. {
  1331. if ( mat_lodin_time.GetFloat() > 0 )
  1332. {
  1333. m_lodBiasCurrent = m_lodBiasInitial = 1.0 * m_nStreamingMips;
  1334. m_lodBiasStartTime = Plat_FloatTime();
  1335. }
  1336. else
  1337. m_lodBiasCurrent = m_lodBiasInitial = 0.0f;
  1338. m_lastLodBiasAdjustFrame = g_FrameNum;
  1339. }
  1340. m_lodClamp = 0;
  1341. m_nStreamingMips = 0;
  1342. SetFilteringAndClampingMode( true );
  1343. // The job is complete, Cancel handles cleanup correctly.
  1344. CancelStreamingJob();
  1345. }
  1346. void CTexture::SetErrorTexture( bool bIsErrorTexture )
  1347. {
  1348. if ( bIsErrorTexture )
  1349. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_ERROR;
  1350. else
  1351. m_nInternalFlags &= ( ~TEXTUREFLAGSINTERNAL_ERROR );
  1352. }
  1353. //-----------------------------------------------------------------------------
  1354. // Creates named render target texture
  1355. //-----------------------------------------------------------------------------
  1356. void CTexture::InitRenderTarget(
  1357. const char *pRTName,
  1358. int w,
  1359. int h,
  1360. RenderTargetSizeMode_t sizeMode,
  1361. ImageFormat fmt,
  1362. RenderTargetType_t type,
  1363. unsigned int textureFlags,
  1364. unsigned int renderTargetFlags )
  1365. {
  1366. if ( pRTName )
  1367. {
  1368. SetName( pRTName );
  1369. }
  1370. else
  1371. {
  1372. static int id = 0;
  1373. char pName[128];
  1374. Q_snprintf( pName, sizeof( pName ), "__render_target_%d", id );
  1375. ++id;
  1376. SetName( pName );
  1377. }
  1378. if ( renderTargetFlags & CREATERENDERTARGETFLAGS_HDR )
  1379. {
  1380. if ( HardwareConfig()->GetHDRType() == HDR_TYPE_FLOAT )
  1381. {
  1382. // slam the format
  1383. fmt = IMAGE_FORMAT_RGBA16161616F;
  1384. }
  1385. }
  1386. int nFrameCount = 1;
  1387. int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_RENDERTARGET;
  1388. nFlags |= textureFlags;
  1389. if ( type == RENDER_TARGET_NO_DEPTH )
  1390. {
  1391. nFlags |= TEXTUREFLAGS_NODEPTHBUFFER;
  1392. }
  1393. else if ( type == RENDER_TARGET_WITH_DEPTH || type == RENDER_TARGET_ONLY_DEPTH || g_pShaderAPI->DoRenderTargetsNeedSeparateDepthBuffer() )
  1394. {
  1395. nFlags |= TEXTUREFLAGS_DEPTHRENDERTARGET;
  1396. ++nFrameCount;
  1397. }
  1398. if ( renderTargetFlags & CREATERENDERTARGETFLAGS_TEMP )
  1399. {
  1400. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET;
  1401. }
  1402. m_nOriginalRenderTargetType = type;
  1403. m_RenderTargetSizeMode = sizeMode;
  1404. m_nOriginalRTWidth = w;
  1405. m_nOriginalRTHeight = h;
  1406. if ( ImageLoader::ImageFormatInfo(fmt).m_NumAlphaBits > 1 )
  1407. {
  1408. nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  1409. }
  1410. else if ( ImageLoader::ImageFormatInfo(fmt).m_NumAlphaBits == 1 )
  1411. {
  1412. nFlags |= TEXTUREFLAGS_ONEBITALPHA;
  1413. }
  1414. ApplyRenderTargetSizeMode( w, h, fmt );
  1415. Init( w, h, 1, fmt, nFlags, nFrameCount );
  1416. m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET;
  1417. }
  1418. void CTexture::OnRestore()
  1419. {
  1420. // May have to change whether or not we have a depth buffer.
  1421. // Are we a render target?
  1422. if ( m_nFlags & TEXTUREFLAGS_RENDERTARGET )
  1423. {
  1424. // Did they not ask for a depth buffer?
  1425. if ( m_nOriginalRenderTargetType == RENDER_TARGET )
  1426. {
  1427. // But, did we force them to have one, or should we force them to have one this time around?
  1428. bool bShouldForce = g_pShaderAPI->DoRenderTargetsNeedSeparateDepthBuffer();
  1429. bool bDidForce = ((m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET) != 0);
  1430. if ( bShouldForce != bDidForce )
  1431. {
  1432. int nFlags = m_nFlags;
  1433. int iFrameCount = m_nFrameCount;
  1434. if ( bShouldForce )
  1435. {
  1436. Assert( !( nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) );
  1437. iFrameCount = 2;
  1438. nFlags |= TEXTUREFLAGS_DEPTHRENDERTARGET;
  1439. }
  1440. else
  1441. {
  1442. Assert( ( nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) );
  1443. iFrameCount = 1;
  1444. nFlags &= ~TEXTUREFLAGS_DEPTHRENDERTARGET;
  1445. }
  1446. Shutdown();
  1447. int newWidth, newHeight;
  1448. ApplyRenderTargetSizeMode( newWidth, newHeight, m_ImageFormat );
  1449. Init( newWidth, newHeight, 1, m_ImageFormat, nFlags, iFrameCount );
  1450. return;
  1451. }
  1452. }
  1453. // If we didn't recreate it up above, then we may need to resize it anyway if the framebuffer
  1454. // got smaller than we are.
  1455. int newWidth, newHeight;
  1456. ApplyRenderTargetSizeMode( newWidth, newHeight, m_ImageFormat );
  1457. if ( newWidth != m_dimsMapping.m_nWidth || newHeight != m_dimsMapping.m_nHeight )
  1458. {
  1459. Shutdown();
  1460. Init( newWidth, newHeight, 1, m_ImageFormat, m_nFlags, m_nFrameCount );
  1461. return;
  1462. }
  1463. }
  1464. else
  1465. {
  1466. if ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE )
  1467. {
  1468. MakeResident( RESIDENT_NONE );
  1469. }
  1470. }
  1471. }
  1472. //-----------------------------------------------------------------------------
  1473. // Creates a procedural texture
  1474. //-----------------------------------------------------------------------------
  1475. void CTexture::InitProceduralTexture( const char *pTextureName, const char *pTextureGroupName, int w, int h, int d, ImageFormat fmt, int nFlags, ITextureRegenerator* generator )
  1476. {
  1477. // We shouldn't be asking for render targets here
  1478. Assert( (nFlags & (TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET)) == 0 );
  1479. SetName( pTextureName );
  1480. // Eliminate flags that are inappropriate...
  1481. nFlags &= ~TEXTUREFLAGS_HINT_DXT5 | TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA |
  1482. TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET;
  1483. // Insert required flags
  1484. nFlags |= TEXTUREFLAGS_PROCEDURAL;
  1485. int nAlphaBits = ImageLoader::ImageFormatInfo(fmt).m_NumAlphaBits;
  1486. if (nAlphaBits > 1)
  1487. {
  1488. nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  1489. }
  1490. else if (nAlphaBits == 1)
  1491. {
  1492. nFlags |= TEXTUREFLAGS_ONEBITALPHA;
  1493. }
  1494. // Procedural textures are always one frame only
  1495. Init( w, h, d, fmt, nFlags, 1 );
  1496. SetTextureRegenerator(generator);
  1497. m_TextureGroupName = pTextureGroupName;
  1498. }
  1499. //-----------------------------------------------------------------------------
  1500. // Creates a file texture
  1501. //-----------------------------------------------------------------------------
  1502. void CTexture::InitFileTexture( const char *pTextureFile, const char *pTextureGroupName )
  1503. {
  1504. // For files, we only really know about the file name
  1505. // At any time, the file contents could change, and we could have
  1506. // a different size, number of frames, etc.
  1507. SetName( pTextureFile );
  1508. m_TextureGroupName = pTextureGroupName;
  1509. }
  1510. //-----------------------------------------------------------------------------
  1511. // Assigns/releases texture IDs for our animation frames
  1512. //-----------------------------------------------------------------------------
  1513. void CTexture::AllocateTextureHandles()
  1514. {
  1515. Assert( !m_pTextureHandles );
  1516. Assert( m_nFrameCount > 0 );
  1517. #ifdef DBGFLAG_ASSERT
  1518. if( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
  1519. {
  1520. Assert( m_nFrameCount >= 2 );
  1521. }
  1522. #endif
  1523. m_pTextureHandles = new ShaderAPITextureHandle_t[m_nFrameCount];
  1524. for( int i = 0; i != m_nFrameCount; ++i )
  1525. m_pTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
  1526. }
  1527. void CTexture::ReleaseTextureHandles()
  1528. {
  1529. if ( m_pTextureHandles )
  1530. {
  1531. delete[] m_pTextureHandles;
  1532. m_pTextureHandles = NULL;
  1533. }
  1534. }
  1535. //-----------------------------------------------------------------------------
  1536. // Creates the texture
  1537. //-----------------------------------------------------------------------------
  1538. bool CTexture::AllocateShaderAPITextures()
  1539. {
  1540. Assert( !HasBeenAllocated() );
  1541. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  1542. int nCount = m_nFrameCount;
  1543. int nCreateFlags = 0;
  1544. if ( ( m_nFlags & TEXTUREFLAGS_ENVMAP ) && HardwareConfig()->SupportsCubeMaps() )
  1545. {
  1546. nCreateFlags |= TEXTURE_CREATE_CUBEMAP;
  1547. }
  1548. bool bIsFloat = ( m_ImageFormat == IMAGE_FORMAT_RGBA16161616F ) || ( m_ImageFormat == IMAGE_FORMAT_R32F ) ||
  1549. ( m_ImageFormat == IMAGE_FORMAT_RGB323232F ) || ( m_ImageFormat == IMAGE_FORMAT_RGBA32323232F );
  1550. // Don't do sRGB on floating point textures
  1551. if ( ( m_nFlags & TEXTUREFLAGS_SRGB ) && !bIsFloat )
  1552. {
  1553. nCreateFlags |= TEXTURE_CREATE_SRGB; // for Posix/GL only
  1554. }
  1555. if ( m_nFlags & TEXTUREFLAGS_RENDERTARGET )
  1556. {
  1557. nCreateFlags |= TEXTURE_CREATE_RENDERTARGET;
  1558. // This here is simply so we can use a different call to
  1559. // create the depth texture below
  1560. if ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) && ( nCount == 2 ) ) //nCount must be 2 on pc
  1561. {
  1562. --nCount;
  1563. }
  1564. }
  1565. else
  1566. {
  1567. // If it's not a render target, use the texture manager in dx
  1568. if ( m_nFlags & TEXTUREFLAGS_STAGING_MEMORY )
  1569. nCreateFlags |= TEXTURE_CREATE_SYSMEM;
  1570. else
  1571. {
  1572. #if defined(IS_WINDOWS_PC)
  1573. static ConVarRef mat_dxlevel("mat_dxlevel");
  1574. if ( mat_dxlevel.GetInt() < 90 || mat_managedtextures.GetBool() )
  1575. #endif
  1576. {
  1577. nCreateFlags |= TEXTURE_CREATE_MANAGED;
  1578. }
  1579. }
  1580. }
  1581. if ( m_nFlags & TEXTUREFLAGS_POINTSAMPLE )
  1582. {
  1583. nCreateFlags |= TEXTURE_CREATE_UNFILTERABLE_OK;
  1584. }
  1585. if ( m_nFlags & TEXTUREFLAGS_VERTEXTEXTURE )
  1586. {
  1587. nCreateFlags |= TEXTURE_CREATE_VERTEXTEXTURE;
  1588. }
  1589. int nCopies = 1;
  1590. if ( IsProcedural() )
  1591. {
  1592. // This is sort of hacky... should we store the # of copies in the VTF?
  1593. if ( !( m_nFlags & TEXTUREFLAGS_SINGLECOPY ) )
  1594. {
  1595. // FIXME: That 6 there is heuristically what I came up with what I
  1596. // need to get eyes not to stall on map alyx3. We need a better way
  1597. // of determining how many copies of the texture we should store.
  1598. nCopies = 6;
  1599. }
  1600. }
  1601. // For depth only render target: adjust texture width/height
  1602. // Currently we just leave it the same size, will update with further testing
  1603. int nShaderApiCreateTextureDepth = ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) && ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ) ) ? 1 : m_dimsAllocated.m_nDepth;
  1604. // Create all animated texture frames in a single call
  1605. g_pShaderAPI->CreateTextures(
  1606. m_pTextureHandles, nCount,
  1607. m_dimsAllocated.m_nWidth, m_dimsAllocated.m_nHeight, nShaderApiCreateTextureDepth, m_ImageFormat, m_dimsAllocated.m_nMipCount,
  1608. nCopies, nCreateFlags, GetName(), GetTextureGroupName() );
  1609. int accountingCount = nCount;
  1610. // Create the depth render target buffer
  1611. if ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
  1612. {
  1613. MEM_ALLOC_CREDIT();
  1614. Assert( nCount == 1 );
  1615. char debugName[128];
  1616. Q_snprintf( debugName, ARRAYSIZE( debugName ), "%s_ZBuffer", GetName() );
  1617. Assert( m_nFrameCount >= 2 );
  1618. m_pTextureHandles[1] = g_pShaderAPI->CreateDepthTexture(
  1619. m_ImageFormat,
  1620. m_dimsAllocated.m_nWidth,
  1621. m_dimsAllocated.m_nHeight,
  1622. debugName,
  1623. ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ) );
  1624. accountingCount += 1;
  1625. }
  1626. 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 ) ) );
  1627. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_ALLOCATED;
  1628. return true;
  1629. }
  1630. //-----------------------------------------------------------------------------
  1631. // Releases the texture's hardware memory
  1632. //-----------------------------------------------------------------------------
  1633. void CTexture::FreeShaderAPITextures()
  1634. {
  1635. if ( m_pTextureHandles && HasBeenAllocated() )
  1636. {
  1637. #ifdef STAGING_ONLY
  1638. // If this hits, there's a leak because we're not deallocating enough textures. Yikes!
  1639. Assert( g_currentTextures[ g_currentTextures.Find( this ) ].m_nFrameCount == m_nFrameCount );
  1640. // Remove ourselves from the list.
  1641. g_currentTextures.Remove( this );
  1642. #endif
  1643. // Release the frames
  1644. for ( int i = m_nFrameCount; --i >= 0; )
  1645. {
  1646. if ( g_pShaderAPI->IsTexture( m_pTextureHandles[i] ) )
  1647. {
  1648. #ifdef WIN32
  1649. Assert( _heapchk() == _HEAPOK );
  1650. #endif
  1651. g_pShaderAPI->DeleteTexture( m_pTextureHandles[i] );
  1652. m_pTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
  1653. }
  1654. }
  1655. }
  1656. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_ALLOCATED;
  1657. // Clear texture streaming stuff, too.
  1658. if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) != 0 )
  1659. {
  1660. m_nFlags &= ~TEXTUREFLAGS_STREAMABLE_FINE;
  1661. m_residenceCurrent = m_residenceTarget = RESIDENT_NONE;
  1662. m_lodClamp = 0;
  1663. m_lodBiasCurrent = m_lodBiasInitial = 0;
  1664. m_lodBiasStartTime = 0;
  1665. }
  1666. }
  1667. void CTexture::MigrateShaderAPITextures()
  1668. {
  1669. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  1670. const int cBytes = m_nFrameCount * sizeof ( ShaderAPITextureHandle_t );
  1671. ShaderAPITextureHandle_t *pTextureHandles = ( ShaderAPITextureHandle_t * ) stackalloc( cBytes );
  1672. Assert( pTextureHandles );
  1673. if ( !pTextureHandles )
  1674. return;
  1675. V_memcpy( pTextureHandles, m_pTextureHandles, cBytes );
  1676. // Pretend we haven't been allocated yet.
  1677. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_ALLOCATED;
  1678. AllocateShaderAPITextures();
  1679. for ( int i = 0; i < m_nFrameCount; ++i )
  1680. {
  1681. Assert( g_pShaderAPI->IsTexture( pTextureHandles[ i ] ) == g_pShaderAPI->IsTexture( m_pTextureHandles[ i ] ) );
  1682. if ( !g_pShaderAPI->IsTexture( pTextureHandles[ i ] ) )
  1683. continue;
  1684. g_pShaderAPI->CopyTextureToTexture( pTextureHandles[ i ], m_pTextureHandles[ i ] );
  1685. }
  1686. for ( int i = 0; i < m_nFrameCount; ++i )
  1687. {
  1688. if ( !g_pShaderAPI->IsTexture( pTextureHandles[ i ] ) )
  1689. continue;
  1690. g_pShaderAPI->DeleteTexture( pTextureHandles[ i ] );
  1691. }
  1692. }
  1693. //-----------------------------------------------------------------------------
  1694. // Computes the actual format of the texture
  1695. //-----------------------------------------------------------------------------
  1696. ImageFormat CTexture::ComputeActualFormat( ImageFormat srcFormat )
  1697. {
  1698. ImageFormat dstFormat;
  1699. bool bIsCompressed = ImageLoader::IsCompressed( srcFormat );
  1700. if ( g_config.bCompressedTextures && HardwareConfig()->SupportsCompressedTextures() && bIsCompressed )
  1701. {
  1702. // for the runtime compressed formats the srcFormat won't equal the dstFormat, and we need to return srcFormat here
  1703. if ( ImageLoader::IsRuntimeCompressed( srcFormat ) )
  1704. {
  1705. return srcFormat;
  1706. }
  1707. // don't do anything since we are already in a compressed format.
  1708. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( srcFormat );
  1709. Assert( dstFormat == srcFormat );
  1710. return dstFormat;
  1711. }
  1712. // NOTE: Below this piece of code is only called when compressed textures are
  1713. // turned off, or if the source texture is not compressed.
  1714. #ifdef DX_TO_GL_ABSTRACTION
  1715. if ( ( srcFormat == IMAGE_FORMAT_UVWQ8888 ) || ( srcFormat == IMAGE_FORMAT_UV88 ) || ( srcFormat == IMAGE_FORMAT_UVLX8888 ) )
  1716. {
  1717. // Danger, this is going to blow up on the Mac. You better know what you're
  1718. // doing with these exotic formats...which were introduced in 1999
  1719. Assert( 0 );
  1720. }
  1721. #endif
  1722. // We use the TEXTUREFLAGS_EIGHTBITALPHA and TEXTUREFLAGS_ONEBITALPHA flags
  1723. // to decide how many bits of alpha we need; vtex checks the alpha channel
  1724. // for all white, etc.
  1725. if( ( srcFormat == IMAGE_FORMAT_UVWQ8888 ) || ( srcFormat == IMAGE_FORMAT_UV88 ) ||
  1726. ( srcFormat == IMAGE_FORMAT_UVLX8888 ) || ( srcFormat == IMAGE_FORMAT_RGBA16161616 ) ||
  1727. ( srcFormat == IMAGE_FORMAT_RGBA16161616F ) )
  1728. {
  1729. #ifdef DX_TO_GL_ABSTRACTION
  1730. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( srcFormat, false ); // Stupid HACK!
  1731. #else
  1732. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( srcFormat, true ); // Stupid HACK!
  1733. #endif
  1734. }
  1735. else if ( m_nFlags & ( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA ) )
  1736. {
  1737. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( IMAGE_FORMAT_BGRA8888 );
  1738. }
  1739. else if ( srcFormat == IMAGE_FORMAT_I8 )
  1740. {
  1741. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( IMAGE_FORMAT_I8 );
  1742. }
  1743. else
  1744. {
  1745. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( IMAGE_FORMAT_BGR888 );
  1746. }
  1747. return dstFormat;
  1748. }
  1749. //-----------------------------------------------------------------------------
  1750. // Calculates info about whether we can make the texture smaller and by how much
  1751. //-----------------------------------------------------------------------------
  1752. int CTexture::ComputeActualSize( bool bIgnorePicmip, IVTFTexture *pVTFTexture, bool bTextureMigration )
  1753. {
  1754. unsigned int stripFlags = 0;
  1755. return ComputeMipSkipCount( GetName(), m_dimsMapping, bIgnorePicmip, pVTFTexture, m_nFlags, m_nDesiredDimensionLimit, &m_nStreamingMips, &m_cachedFileLodSettings, &m_dimsActual, &m_dimsAllocated, &stripFlags );
  1756. Assert( stripFlags == 0 ); // Not necessarily illegal, just needs investigating.
  1757. }
  1758. //-----------------------------------------------------------------------------
  1759. // Used to modify the texture bits (procedural textures only)
  1760. //-----------------------------------------------------------------------------
  1761. void CTexture::SetTextureRegenerator( ITextureRegenerator *pTextureRegen )
  1762. {
  1763. // NOTE: These can only be used by procedural textures
  1764. Assert( IsProcedural() );
  1765. if (m_pTextureRegenerator)
  1766. {
  1767. m_pTextureRegenerator->Release();
  1768. }
  1769. m_pTextureRegenerator = pTextureRegen;
  1770. }
  1771. //-----------------------------------------------------------------------------
  1772. // Gets us modifying a particular frame of our texture
  1773. //-----------------------------------------------------------------------------
  1774. void CTexture::Modify( int iFrame )
  1775. {
  1776. Assert( iFrame >= 0 && iFrame < m_nFrameCount );
  1777. Assert( HasBeenAllocated() );
  1778. g_pShaderAPI->ModifyTexture( m_pTextureHandles[iFrame] );
  1779. }
  1780. //-----------------------------------------------------------------------------
  1781. // Sets the texture clamping state on the currently modified frame
  1782. //-----------------------------------------------------------------------------
  1783. void CTexture::SetWrapState( )
  1784. {
  1785. // Border clamp applies to all texture coordinates
  1786. if ( m_nFlags & TEXTUREFLAGS_BORDER )
  1787. {
  1788. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_BORDER );
  1789. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_BORDER );
  1790. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_BORDER );
  1791. return;
  1792. }
  1793. // Clamp mode in S
  1794. if ( m_nFlags & TEXTUREFLAGS_CLAMPS )
  1795. {
  1796. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_CLAMP );
  1797. }
  1798. else
  1799. {
  1800. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_REPEAT );
  1801. }
  1802. // Clamp mode in T
  1803. if ( m_nFlags & TEXTUREFLAGS_CLAMPT )
  1804. {
  1805. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_CLAMP );
  1806. }
  1807. else
  1808. {
  1809. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_REPEAT );
  1810. }
  1811. // Clamp mode in U
  1812. if ( m_nFlags & TEXTUREFLAGS_CLAMPU )
  1813. {
  1814. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_CLAMP );
  1815. }
  1816. else
  1817. {
  1818. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_REPEAT );
  1819. }
  1820. }
  1821. //-----------------------------------------------------------------------------
  1822. // Sets the texture filtering state on the currently modified frame
  1823. //-----------------------------------------------------------------------------
  1824. void CTexture::SetFilterState()
  1825. {
  1826. // Turns off filtering when we're point sampling
  1827. if( m_nFlags & TEXTUREFLAGS_POINTSAMPLE )
  1828. {
  1829. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_NEAREST );
  1830. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_NEAREST );
  1831. return;
  1832. }
  1833. // NOTE: config.bMipMapTextures and config.bFilterTextures is handled in ShaderAPIDX8
  1834. bool bEnableMipmapping = ( m_nFlags & TEXTUREFLAGS_NOMIP ) ? false : true;
  1835. if( !bEnableMipmapping )
  1836. {
  1837. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
  1838. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
  1839. return;
  1840. }
  1841. // Determing the filtering mode
  1842. bool bIsAnisotropic = false, bIsTrilinear = false;
  1843. if ( HardwareConfig()->GetDXSupportLevel() >= 80 && (g_config.m_nForceAnisotropicLevel > 1) &&
  1844. (HardwareConfig()->MaximumAnisotropicLevel() > 1) )
  1845. {
  1846. bIsAnisotropic = true;
  1847. }
  1848. else if ( g_config.ForceTrilinear() )
  1849. {
  1850. bIsAnisotropic = (( m_nFlags & TEXTUREFLAGS_ANISOTROPIC ) != 0) && (HardwareConfig()->MaximumAnisotropicLevel() > 1);
  1851. bIsTrilinear = true;
  1852. }
  1853. else
  1854. {
  1855. bIsAnisotropic = (( m_nFlags & TEXTUREFLAGS_ANISOTROPIC ) != 0) && (HardwareConfig()->MaximumAnisotropicLevel() > 1);
  1856. bIsTrilinear = ( m_nFlags & TEXTUREFLAGS_TRILINEAR ) != 0;
  1857. }
  1858. if ( bIsAnisotropic )
  1859. {
  1860. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_ANISOTROPIC );
  1861. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_ANISOTROPIC );
  1862. }
  1863. else
  1864. {
  1865. // force trilinear if we are on a dx8 card or above
  1866. if ( bIsTrilinear )
  1867. {
  1868. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR );
  1869. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
  1870. }
  1871. else
  1872. {
  1873. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST );
  1874. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
  1875. }
  1876. }
  1877. SetLodState();
  1878. }
  1879. //-----------------------------------------------------------------------------
  1880. // Sets the lod state on the currently modified frame
  1881. //-----------------------------------------------------------------------------
  1882. void CTexture::SetLodState()
  1883. {
  1884. // Set the lod clamping value to ensure we don't see anything we're not supposed to.
  1885. g_pShaderAPI->TexLodClamp( m_lodClamp );
  1886. g_pShaderAPI->TexLodBias( m_lodBiasCurrent );
  1887. }
  1888. //-----------------------------------------------------------------------------
  1889. // Download bits main entry point!!
  1890. //-----------------------------------------------------------------------------
  1891. void CTexture::DownloadTexture( Rect_t *pRect, bool bCopyFromCurrent )
  1892. {
  1893. // No downloading necessary if there's no graphics
  1894. if ( !g_pShaderDevice->IsUsingGraphics() )
  1895. return;
  1896. // We don't know the actual size of the texture at this stage...
  1897. if ( !pRect )
  1898. {
  1899. ReconstructTexture( bCopyFromCurrent );
  1900. }
  1901. else
  1902. {
  1903. // Not implemented yet.
  1904. Assert( bCopyFromCurrent == false );
  1905. ReconstructPartialTexture( pRect );
  1906. }
  1907. // Iterate over all the frames and set the appropriate wrapping + filtering state
  1908. SetFilteringAndClampingMode();
  1909. // texture bits have been updated, update the exclusion state
  1910. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE )
  1911. {
  1912. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_EXCLUDED;
  1913. }
  1914. else
  1915. {
  1916. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_EXCLUDED;
  1917. }
  1918. // texture bits have been picmipped, update the picmip state
  1919. m_nActualDimensionLimit = m_nDesiredDimensionLimit;
  1920. }
  1921. void CTexture::Download( Rect_t *pRect, int nAdditionalCreationFlags /* = 0 */ )
  1922. {
  1923. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1924. // Only download the bits if we can...
  1925. if ( g_pShaderAPI->CanDownloadTextures() )
  1926. {
  1927. MaterialLock_t hLock = MaterialSystem()->Lock();
  1928. m_nFlags |= nAdditionalCreationFlags; // Path to let stdshaders drive settings like sRGB-ness at creation time
  1929. DownloadTexture( pRect );
  1930. MaterialSystem()->Unlock( hLock );
  1931. }
  1932. }
  1933. // Save texture to a file.
  1934. bool CTexture::SaveToFile( const char *fileName )
  1935. {
  1936. bool bRet = false;
  1937. ITexture *pTexture = materials->FindTexture( "_rt_FullFrameFB1", TEXTURE_GROUP_RENDER_TARGET );
  1938. if ( !pTexture )
  1939. return bRet;
  1940. const int width = GetActualWidth();
  1941. const int height = GetActualHeight();
  1942. if ( pTexture->GetImageFormat() == IMAGE_FORMAT_RGBA8888 ||
  1943. pTexture->GetImageFormat() == IMAGE_FORMAT_ABGR8888 ||
  1944. pTexture->GetImageFormat() == IMAGE_FORMAT_ARGB8888 ||
  1945. pTexture->GetImageFormat() == IMAGE_FORMAT_BGRA8888 ||
  1946. pTexture->GetImageFormat() == IMAGE_FORMAT_BGRX8888 )
  1947. {
  1948. bool bCleanupTexture = false;
  1949. // Need to allocate a temporarily renderable surface. Sadness.
  1950. if ( width > pTexture->GetActualWidth() || height > pTexture->GetActualHeight() )
  1951. {
  1952. materials->OverrideRenderTargetAllocation( true );
  1953. // This one bumps the ref automatically for us.
  1954. pTexture = materials->CreateNamedRenderTargetTextureEx( "_rt_savetofile", width, height, RT_SIZE_LITERAL, IMAGE_FORMAT_BGRA8888, MATERIAL_RT_DEPTH_NONE, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  1955. materials->OverrideRenderTargetAllocation( false );
  1956. if ( !pTexture || pTexture->IsError() )
  1957. {
  1958. SafeRelease( &pTexture );
  1959. Msg( "SaveToFile: texture '_rt_FullFrameFB1' failed. Ptr:%p Format:%d\n", pTexture, ( pTexture ? pTexture->GetImageFormat() : 0 ) );
  1960. return false;
  1961. }
  1962. bCleanupTexture = true;
  1963. }
  1964. Rect_t SrcRect = { 0, 0, width, height };
  1965. Rect_t DstRect = SrcRect;
  1966. if ( ( width > 0 ) && ( height > 0 ) )
  1967. {
  1968. void *pixelValue = malloc( width * height * 2 * sizeof( BGRA8888_t ) );
  1969. if( pixelValue )
  1970. {
  1971. CMatRenderContextPtr pRenderContext( MaterialSystem() );
  1972. // Set the clear color to opaque black
  1973. pRenderContext->ClearColor4ub( 0, 0, 0, 0xFF );
  1974. pRenderContext->ClearBuffers( true, true, true );
  1975. pRenderContext->PushRenderTargetAndViewport( pTexture, 0, 0, width, height );
  1976. pRenderContext->CopyTextureToRenderTargetEx( 0, this, &SrcRect, &DstRect );
  1977. pRenderContext->ReadPixels( 0, 0, width, height, ( unsigned char * )pixelValue, pTexture->GetImageFormat() );
  1978. // Slap the alpha channel at the bottom of the tga file so we don't have to deal with crappy tools that can't
  1979. // handle rgb + alpha well. This means we can just do a "mat_texture_save_fonts" concommand, and then use
  1980. // something like Beyond Compare to look at the fonts differences between various platforms, etc.
  1981. CPixelWriter pixelWriterSrc;
  1982. CPixelWriter pixelWriterDst;
  1983. pixelWriterSrc.SetPixelMemory( pTexture->GetImageFormat(), pixelValue, width * sizeof( BGRA8888_t ) );
  1984. pixelWriterDst.SetPixelMemory( pTexture->GetImageFormat(), pixelValue, width * sizeof( BGRA8888_t ) );
  1985. for (int y = 0; y < height; ++y)
  1986. {
  1987. pixelWriterSrc.Seek( 0, y );
  1988. pixelWriterDst.Seek( 0, y + height );
  1989. for (int x = 0; x < width; ++x)
  1990. {
  1991. int r, g, b, a;
  1992. pixelWriterSrc.ReadPixelNoAdvance( r, g, b, a );
  1993. pixelWriterSrc.WritePixel( a, a, a, 255 );
  1994. pixelWriterDst.WritePixel( r, g, b, 255 );
  1995. }
  1996. }
  1997. if ( TGAWriter::WriteTGAFile( fileName, width, height * 2, pTexture->GetImageFormat(), ( uint8 * )pixelValue, width * sizeof( BGRA8888_t ) ) )
  1998. {
  1999. bRet = true;
  2000. }
  2001. // restore our previous state
  2002. pRenderContext->PopRenderTargetAndViewport();
  2003. free( pixelValue );
  2004. }
  2005. }
  2006. if ( bCleanupTexture )
  2007. SafeRelease( &pTexture );
  2008. }
  2009. else
  2010. {
  2011. Msg( "SaveToFile: texture '_rt_FullFrameFB1' failed. Ptr:%p Format:%d\n", pTexture, ( pTexture ? pTexture->GetImageFormat() : 0 ) );
  2012. }
  2013. return bRet;
  2014. }
  2015. bool CTexture::AsyncReadTextureFromFile( IVTFTexture* pVTFTexture, unsigned int nAdditionalCreationFlags )
  2016. {
  2017. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2018. m_bStreamingFileReadFailed = false; // Optimism!
  2019. char pCacheFileName[ MATERIAL_MAX_PATH ];
  2020. FileHandle_t fileHandle = FILESYSTEM_INVALID_HANDLE;
  2021. GetCacheFilename( pCacheFileName, MATERIAL_MAX_PATH );
  2022. if ( !GetFileHandle( &fileHandle, pCacheFileName, NULL ) )
  2023. {
  2024. m_bStreamingFileReadFailed = true;
  2025. return false;
  2026. }
  2027. if ( V_strstr( GetName(), "c_sniperrifle_scope" ) )
  2028. {
  2029. int i = 0;
  2030. i = 3;
  2031. }
  2032. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - %s", __FUNCTION__, tmDynamicString( TELEMETRY_LEVEL0, pCacheFileName ) );
  2033. // OSX hackery
  2034. int nPreserveFlags = nAdditionalCreationFlags;
  2035. if ( m_nFlags & TEXTUREFLAGS_SRGB )
  2036. nPreserveFlags |= TEXTUREFLAGS_SRGB;
  2037. uint16 dontCareStreamedMips = m_nStreamingMips;
  2038. TextureLODControlSettings_t settings = m_cachedFileLodSettings;
  2039. if ( !SLoadTextureBitsFromFile( &pVTFTexture, fileHandle, m_nFlags | nPreserveFlags, &settings, m_nDesiredDimensionLimit, &dontCareStreamedMips, GetName(), pCacheFileName, &m_dimsMapping ) )
  2040. {
  2041. g_pFullFileSystem->Close( fileHandle );
  2042. m_bStreamingFileReadFailed = true;
  2043. return false;
  2044. }
  2045. g_pFullFileSystem->Close( fileHandle );
  2046. m_pStreamingVTF = pVTFTexture;
  2047. return true;
  2048. }
  2049. void CTexture::AsyncCancelReadTexture( )
  2050. {
  2051. Assert( m_bStreamingFileReadFailed || m_pStreamingVTF != NULL );
  2052. if ( m_pStreamingVTF )
  2053. {
  2054. TextureManager()->ReleaseAsyncScratchVTF( m_pStreamingVTF );
  2055. m_pStreamingVTF = NULL;
  2056. }
  2057. }
  2058. void CTexture::Bind( Sampler_t sampler )
  2059. {
  2060. Bind( sampler, 0 );
  2061. }
  2062. //-----------------------------------------------------------------------------
  2063. // Binds a particular texture (possibly paired)
  2064. //-----------------------------------------------------------------------------
  2065. void CTexture::Bind( Sampler_t sampler1, int nFrame, Sampler_t sampler2 /* = -1 */ )
  2066. {
  2067. if ( g_pShaderDevice->IsUsingGraphics() )
  2068. {
  2069. TextureManager()->RequestAllMipmaps( this );
  2070. if ( nFrame < 0 || nFrame >= m_nFrameCount )
  2071. {
  2072. // FIXME: Use the well-known 'error' id instead of frame 0
  2073. nFrame = 0;
  2074. // Assert(0);
  2075. }
  2076. // Make sure we've actually allocated the texture handle
  2077. if ( HasBeenAllocated() )
  2078. {
  2079. g_pShaderAPI->BindTexture( sampler1, m_pTextureHandles[nFrame] );
  2080. }
  2081. else
  2082. {
  2083. ExecuteNTimes( 20, Warning( "Trying to bind texture %s, but texture handles are not valid. Binding a white texture!\n", GetName() ) );
  2084. g_pShaderAPI->BindStandardTexture( sampler1, TEXTURE_WHITE );
  2085. }
  2086. }
  2087. }
  2088. void CTexture::BindVertexTexture( VertexTextureSampler_t sampler, int nFrame )
  2089. {
  2090. if ( g_pShaderDevice->IsUsingGraphics() )
  2091. {
  2092. if ( nFrame < 0 || nFrame >= m_nFrameCount )
  2093. {
  2094. // FIXME: Use the well-known 'error' id instead of frame 0
  2095. nFrame = 0;
  2096. // Assert(0);
  2097. }
  2098. // Make sure we've actually allocated the texture
  2099. Assert( HasBeenAllocated() );
  2100. g_pShaderAPI->BindVertexTexture( sampler, m_pTextureHandles[nFrame] );
  2101. }
  2102. }
  2103. //-----------------------------------------------------------------------------
  2104. // Set this texture as a render target
  2105. //-----------------------------------------------------------------------------
  2106. bool CTexture::SetRenderTarget( int nRenderTargetID )
  2107. {
  2108. return SetRenderTarget( nRenderTargetID, NULL );
  2109. }
  2110. //-----------------------------------------------------------------------------
  2111. // Set this texture as a render target
  2112. // Optionally bind pDepthTexture as depth buffer
  2113. //-----------------------------------------------------------------------------
  2114. bool CTexture::SetRenderTarget( int nRenderTargetID, ITexture *pDepthTexture )
  2115. {
  2116. if ( ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) == 0 )
  2117. return false;
  2118. // Make sure we've actually allocated the texture handles
  2119. Assert( HasBeenAllocated() );
  2120. ShaderAPITextureHandle_t textureHandle = m_pTextureHandles[0];
  2121. ShaderAPITextureHandle_t depthTextureHandle = (unsigned int)SHADER_RENDERTARGET_DEPTHBUFFER;
  2122. if ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
  2123. {
  2124. Assert( m_nFrameCount >= 2 );
  2125. depthTextureHandle = m_pTextureHandles[1];
  2126. }
  2127. else if ( m_nFlags & TEXTUREFLAGS_NODEPTHBUFFER )
  2128. {
  2129. // GR - render target without depth buffer
  2130. depthTextureHandle = (unsigned int)SHADER_RENDERTARGET_NONE;
  2131. }
  2132. if ( pDepthTexture)
  2133. {
  2134. depthTextureHandle = static_cast<ITextureInternal *>(pDepthTexture)->GetTextureHandle(0);
  2135. }
  2136. g_pShaderAPI->SetRenderTargetEx( nRenderTargetID, textureHandle, depthTextureHandle );
  2137. return true;
  2138. }
  2139. //-----------------------------------------------------------------------------
  2140. // Reference counting
  2141. //-----------------------------------------------------------------------------
  2142. void CTexture::IncrementReferenceCount( void )
  2143. {
  2144. ++m_nRefCount;
  2145. }
  2146. void CTexture::DecrementReferenceCount( void )
  2147. {
  2148. if ( ( --m_nRefCount <= 0 ) && ( m_nFlags & TEXTUREFLAGS_IMMEDIATE_CLEANUP ) != 0 )
  2149. {
  2150. Assert( m_nRefCount == 0 );
  2151. // Just inform the texture manager, it will decide to free us at a later date.
  2152. TextureManager()->MarkUnreferencedTextureForCleanup( this );
  2153. }
  2154. }
  2155. int CTexture::GetReferenceCount( )
  2156. {
  2157. return m_nRefCount;
  2158. }
  2159. //-----------------------------------------------------------------------------
  2160. // Various accessor methods
  2161. //-----------------------------------------------------------------------------
  2162. const char* CTexture::GetName( ) const
  2163. {
  2164. return m_Name.String();
  2165. }
  2166. const char* CTexture::GetTextureGroupName( ) const
  2167. {
  2168. return m_TextureGroupName.String();
  2169. }
  2170. void CTexture::SetName( const char* pName )
  2171. {
  2172. // normalize and convert to a symbol
  2173. char szCleanName[MAX_PATH];
  2174. m_Name = NormalizeTextureName( pName, szCleanName, sizeof( szCleanName ) );
  2175. #ifdef _DEBUG
  2176. if ( m_pDebugName )
  2177. {
  2178. delete [] m_pDebugName;
  2179. }
  2180. int nLen = V_strlen( szCleanName ) + 1;
  2181. m_pDebugName = new char[nLen];
  2182. V_memcpy( m_pDebugName, szCleanName, nLen );
  2183. #endif
  2184. }
  2185. ImageFormat CTexture::GetImageFormat() const
  2186. {
  2187. return m_ImageFormat;
  2188. }
  2189. int CTexture::GetMappingWidth() const
  2190. {
  2191. return m_dimsMapping.m_nWidth;
  2192. }
  2193. int CTexture::GetMappingHeight() const
  2194. {
  2195. return m_dimsMapping.m_nHeight;
  2196. }
  2197. int CTexture::GetMappingDepth() const
  2198. {
  2199. return m_dimsMapping.m_nDepth;
  2200. }
  2201. int CTexture::GetActualWidth() const
  2202. {
  2203. return m_dimsActual.m_nWidth;
  2204. }
  2205. int CTexture::GetActualHeight() const
  2206. {
  2207. return m_dimsActual.m_nHeight;
  2208. }
  2209. int CTexture::GetActualDepth() const
  2210. {
  2211. return m_dimsActual.m_nDepth;
  2212. }
  2213. int CTexture::GetNumAnimationFrames() const
  2214. {
  2215. return m_nFrameCount;
  2216. }
  2217. void CTexture::GetReflectivity( Vector& reflectivity )
  2218. {
  2219. Precache();
  2220. VectorCopy( m_vecReflectivity, reflectivity );
  2221. }
  2222. //-----------------------------------------------------------------------------
  2223. // Little helper polling methods
  2224. //-----------------------------------------------------------------------------
  2225. bool CTexture::IsTranslucent() const
  2226. {
  2227. return ( m_nFlags & (TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA) ) != 0;
  2228. }
  2229. bool CTexture::IsNormalMap( void ) const
  2230. {
  2231. return ( ( m_nFlags & TEXTUREFLAGS_NORMAL ) != 0 );
  2232. }
  2233. bool CTexture::IsCubeMap( void ) const
  2234. {
  2235. return ( ( m_nFlags & TEXTUREFLAGS_ENVMAP ) != 0 );
  2236. }
  2237. bool CTexture::IsRenderTarget( void ) const
  2238. {
  2239. return ( ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) != 0 );
  2240. }
  2241. bool CTexture::IsTempRenderTarget( void ) const
  2242. {
  2243. return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET ) != 0 );
  2244. }
  2245. bool CTexture::IsProcedural() const
  2246. {
  2247. return ( (m_nFlags & TEXTUREFLAGS_PROCEDURAL) != 0 );
  2248. }
  2249. bool CTexture::IsMipmapped() const
  2250. {
  2251. return ( (m_nFlags & TEXTUREFLAGS_NOMIP) == 0 );
  2252. }
  2253. unsigned int CTexture::GetFlags() const
  2254. {
  2255. return m_nFlags;
  2256. }
  2257. void CTexture::ForceLODOverride( int iNumLodsOverrideUpOrDown )
  2258. {
  2259. TextureLodOverride::OverrideInfo oi( iNumLodsOverrideUpOrDown, iNumLodsOverrideUpOrDown );
  2260. TextureLodOverride::Add( GetName(), oi );
  2261. Download( NULL );
  2262. }
  2263. bool CTexture::IsError() const
  2264. {
  2265. return ( (m_nInternalFlags & TEXTUREFLAGSINTERNAL_ERROR) != 0 );
  2266. }
  2267. bool CTexture::HasBeenAllocated() const
  2268. {
  2269. return ( (m_nInternalFlags & TEXTUREFLAGSINTERNAL_ALLOCATED) != 0 );
  2270. }
  2271. bool CTexture::IsVolumeTexture() const
  2272. {
  2273. return (m_dimsMapping.m_nDepth > 1);
  2274. }
  2275. //-----------------------------------------------------------------------------
  2276. // Sets the filtering + clamping modes on the texture
  2277. //-----------------------------------------------------------------------------
  2278. void CTexture::SetFilteringAndClampingMode( bool bOnlyLodValues )
  2279. {
  2280. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2281. if( !HasBeenAllocated() )
  2282. return;
  2283. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  2284. {
  2285. Modify( iFrame ); // Indicate we're changing state with respect to a particular frame
  2286. if ( !bOnlyLodValues )
  2287. {
  2288. SetWrapState(); // Send the appropriate wrap/clamping modes to the shaderapi.
  2289. SetFilterState(); // Set the filtering mode for the texture after downloading it.
  2290. // NOTE: Apparently, the filter state cannot be set until after download
  2291. }
  2292. else
  2293. SetLodState();
  2294. }
  2295. }
  2296. //-----------------------------------------------------------------------------
  2297. // Loads up the non-fallback information about the texture
  2298. //-----------------------------------------------------------------------------
  2299. void CTexture::Precache()
  2300. {
  2301. // We only have to do something in the case of a file texture
  2302. if ( IsRenderTarget() || IsProcedural() )
  2303. return;
  2304. if ( HasBeenAllocated() )
  2305. return;
  2306. // Blow off env_cubemap too...
  2307. if ( !Q_strnicmp( m_Name.String(), "env_cubemap", 12 ))
  2308. return;
  2309. int nAdditionalFlags = 0;
  2310. if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) != 0 )
  2311. {
  2312. // If we were previously streamed in, make sure we still do this time around.
  2313. nAdditionalFlags = TEXTUREFLAGS_STREAMABLE_COARSE;
  2314. Assert( ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE ) == 0 );
  2315. Assert( m_residenceCurrent == RESIDENT_NONE && m_residenceTarget == RESIDENT_NONE );
  2316. Assert( m_lodClamp == 0 );
  2317. Assert( m_lodBiasCurrent == 0 && m_lodBiasInitial == 0 );
  2318. Assert( m_lodBiasStartTime == 0 );
  2319. }
  2320. ScratchVTF scratch( this );
  2321. IVTFTexture *pVTFTexture = scratch.Get();
  2322. // The texture name doubles as the relative file name
  2323. // It's assumed to have already been set by this point
  2324. // Compute the cache name
  2325. char pCacheFileName[MATERIAL_MAX_PATH];
  2326. Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "materials/%s" TEXTURE_FNAME_EXTENSION, m_Name.String() );
  2327. int nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION );
  2328. unsigned char *pMem = (unsigned char *)stackalloc( nHeaderSize );
  2329. CUtlBuffer buf( pMem, nHeaderSize );
  2330. if ( !g_pFullFileSystem->ReadFile( pCacheFileName, NULL, buf, nHeaderSize ) )
  2331. {
  2332. goto precacheFailed;
  2333. }
  2334. if ( !pVTFTexture->Unserialize( buf, true ) )
  2335. {
  2336. Warning( "Error reading material \"%s\"\n", pCacheFileName );
  2337. goto precacheFailed;
  2338. }
  2339. // NOTE: Don't set the image format in case graphics are active
  2340. VectorCopy( pVTFTexture->Reflectivity(), m_vecReflectivity );
  2341. m_dimsMapping.m_nWidth = pVTFTexture->Width();
  2342. m_dimsMapping.m_nHeight = pVTFTexture->Height();
  2343. m_dimsMapping.m_nDepth = pVTFTexture->Depth();
  2344. m_nFlags = pVTFTexture->Flags() | nAdditionalFlags;
  2345. m_nFrameCount = pVTFTexture->FrameCount();
  2346. if ( !m_pTextureHandles )
  2347. {
  2348. // NOTE: m_nFrameCount and m_pTextureHandles are strongly associated
  2349. // whenever one is modified the other must also be modified
  2350. AllocateTextureHandles();
  2351. }
  2352. return;
  2353. precacheFailed:
  2354. m_vecReflectivity.Init( 0, 0, 0 );
  2355. m_dimsMapping.m_nWidth = 32;
  2356. m_dimsMapping.m_nHeight = 32;
  2357. m_dimsMapping.m_nDepth = 1;
  2358. m_nFlags = TEXTUREFLAGS_NOMIP;
  2359. SetErrorTexture( true );
  2360. m_nFrameCount = 1;
  2361. if ( !m_pTextureHandles )
  2362. {
  2363. // NOTE: m_nFrameCount and m_pTextureHandles are strongly associated
  2364. // whenever one is modified the other must also be modified
  2365. AllocateTextureHandles();
  2366. }
  2367. }
  2368. //-----------------------------------------------------------------------------
  2369. // Loads the low-res image from the texture
  2370. //-----------------------------------------------------------------------------
  2371. void CTexture::LoadLowResTexture( IVTFTexture *pTexture )
  2372. {
  2373. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2374. delete [] m_pLowResImage;
  2375. m_pLowResImage = NULL;
  2376. if ( pTexture->LowResWidth() == 0 || pTexture->LowResHeight() == 0 )
  2377. {
  2378. m_LowResImageWidth = m_LowResImageHeight = 0;
  2379. return;
  2380. }
  2381. m_LowResImageWidth = pTexture->LowResWidth();
  2382. m_LowResImageHeight = pTexture->LowResHeight();
  2383. m_pLowResImage = new unsigned char[m_LowResImageWidth * m_LowResImageHeight * 3];
  2384. #ifdef DBGFLAG_ASSERT
  2385. bool retVal =
  2386. #endif
  2387. ImageLoader::ConvertImageFormat( pTexture->LowResImageData(), pTexture->LowResFormat(),
  2388. m_pLowResImage, IMAGE_FORMAT_RGB888, m_LowResImageWidth, m_LowResImageHeight );
  2389. Assert( retVal );
  2390. }
  2391. void *CTexture::GetResourceData( uint32 eDataType, size_t *pnumBytes ) const
  2392. {
  2393. for ( DataChunk const *pDataChunk = m_arrDataChunks.Base(),
  2394. *pDataChunkEnd = pDataChunk + m_arrDataChunks.Count();
  2395. pDataChunk < pDataChunkEnd; ++pDataChunk )
  2396. {
  2397. if ( ( pDataChunk->m_eType & ~RSRCF_MASK ) == eDataType )
  2398. {
  2399. if ( ( pDataChunk->m_eType & RSRCF_HAS_NO_DATA_CHUNK ) == 0 )
  2400. {
  2401. if ( pnumBytes)
  2402. *pnumBytes = pDataChunk->m_numBytes;
  2403. return pDataChunk->m_pvData;
  2404. }
  2405. else
  2406. {
  2407. if ( pnumBytes )
  2408. *pnumBytes = sizeof( pDataChunk->m_numBytes );
  2409. return ( void *)( &pDataChunk->m_numBytes );
  2410. }
  2411. }
  2412. }
  2413. if ( pnumBytes )
  2414. pnumBytes = 0;
  2415. return NULL;
  2416. }
  2417. #pragma pack(1)
  2418. struct DXTColBlock
  2419. {
  2420. unsigned short col0;
  2421. unsigned short col1;
  2422. // no bit fields - use bytes
  2423. unsigned char row[4];
  2424. };
  2425. struct DXTAlphaBlock3BitLinear
  2426. {
  2427. unsigned char alpha0;
  2428. unsigned char alpha1;
  2429. unsigned char stuff[6];
  2430. };
  2431. #pragma pack()
  2432. static void FillCompressedTextureWithSingleColor( int red, int green, int blue, int alpha, unsigned char *pImageData,
  2433. int width, int height, int depth, ImageFormat imageFormat )
  2434. {
  2435. Assert( ( width < 4 ) || !( width % 4 ) );
  2436. Assert( ( height < 4 ) || !( height % 4 ) );
  2437. Assert( ( depth < 4 ) || !( depth % 4 ) );
  2438. if ( width < 4 && width > 0 )
  2439. {
  2440. width = 4;
  2441. }
  2442. if ( height < 4 && height > 0 )
  2443. {
  2444. height = 4;
  2445. }
  2446. if ( depth < 4 && depth > 1 )
  2447. {
  2448. depth = 4;
  2449. }
  2450. int numBlocks = ( width * height ) >> 4;
  2451. numBlocks *= depth;
  2452. DXTColBlock colorBlock;
  2453. memset( &colorBlock, 0, sizeof( colorBlock ) );
  2454. ( ( BGR565_t * )&( colorBlock.col0 ) )->Set( red, green, blue );
  2455. ( ( BGR565_t * )&( colorBlock.col1 ) )->Set( red, green, blue );
  2456. switch( imageFormat )
  2457. {
  2458. case IMAGE_FORMAT_DXT1:
  2459. case IMAGE_FORMAT_ATI1N: // Invalid block data, but correct memory footprint
  2460. {
  2461. int i;
  2462. for( i = 0; i < numBlocks; i++ )
  2463. {
  2464. memcpy( pImageData + i * 8, &colorBlock, sizeof( colorBlock ) );
  2465. }
  2466. }
  2467. break;
  2468. case IMAGE_FORMAT_DXT5:
  2469. case IMAGE_FORMAT_ATI2N:
  2470. {
  2471. int i;
  2472. for( i = 0; i < numBlocks; i++ )
  2473. {
  2474. // memset( pImageData + i * 16, 0, 16 );
  2475. memcpy( pImageData + i * 16 + 8, &colorBlock, sizeof( colorBlock ) );
  2476. // memset( pImageData + i * 16 + 8, 0xffff, 8 ); // alpha block
  2477. }
  2478. }
  2479. break;
  2480. default:
  2481. Assert( 0 );
  2482. break;
  2483. }
  2484. }
  2485. // This table starts out like the programmatic logic that used to be here,
  2486. // but then has some other colors, so that we don't see repeats.
  2487. // Also, there is no black, which seems to be an error condition on OpenGL.
  2488. // There also aren't any zeros in this table, since these colors may get
  2489. // multiplied with, say, vertex colors which are tinted, resulting in black pixels.
  2490. int sg_nMipLevelColors[14][3] = { { 64, 255, 64 }, // Green
  2491. { 255, 64, 64 }, // Red
  2492. { 255, 255, 64 }, // Yellow
  2493. { 64, 64, 255 }, // Blue
  2494. { 64, 255, 255 }, // Cyan
  2495. { 255, 64, 255 }, // Magenta
  2496. { 255, 255, 255 }, // White
  2497. { 255, 150, 150 }, // Light Red
  2498. { 255, 255, 150 }, // Light Yellow
  2499. { 150, 150, 255 }, // Light Blue
  2500. { 150, 255, 255 }, // Light Cyan
  2501. { 255, 150, 255 }, // Light Magenta
  2502. { 150, 150, 128 }, // Light Gray
  2503. { 138, 131, 64 } };// Brown
  2504. //-----------------------------------------------------------------------------
  2505. // Generate a texture that shows the various mip levels
  2506. //-----------------------------------------------------------------------------
  2507. void CTexture::GenerateShowMipLevelsTextures( IVTFTexture *pTexture )
  2508. {
  2509. if( pTexture->FaceCount() > 1 )
  2510. return;
  2511. switch( pTexture->Format() )
  2512. {
  2513. // These are formats that we don't bother with for generating mip level textures.
  2514. case IMAGE_FORMAT_RGBA16161616F:
  2515. case IMAGE_FORMAT_R32F:
  2516. case IMAGE_FORMAT_RGB323232F:
  2517. case IMAGE_FORMAT_RGBA32323232F:
  2518. case IMAGE_FORMAT_UV88:
  2519. break;
  2520. default:
  2521. for (int iFrame = 0; iFrame < pTexture->FrameCount(); ++iFrame )
  2522. {
  2523. for (int iFace = 0; iFace < pTexture->FaceCount(); ++iFace )
  2524. {
  2525. for (int iMip = 0; iMip < pTexture->MipCount(); ++iMip )
  2526. {
  2527. int red = sg_nMipLevelColors[iMip][0];//( ( iMip + 1 ) & 2 ) ? 255 : 0;
  2528. int green = sg_nMipLevelColors[iMip][1];//( ( iMip + 1 ) & 1 ) ? 255 : 0;
  2529. int blue = sg_nMipLevelColors[iMip][2];//( ( iMip + 1 ) & 4 ) ? 255 : 0;
  2530. int nWidth, nHeight, nDepth;
  2531. pTexture->ComputeMipLevelDimensions( iMip, &nWidth, &nHeight, &nDepth );
  2532. if( pTexture->Format() == IMAGE_FORMAT_DXT1 || pTexture->Format() == IMAGE_FORMAT_DXT5 ||
  2533. pTexture->Format() == IMAGE_FORMAT_ATI1N || pTexture->Format() == IMAGE_FORMAT_ATI2N )
  2534. {
  2535. unsigned char *pImageData = pTexture->ImageData( iFrame, iFace, iMip, 0, 0, 0 );
  2536. int alpha = 255;
  2537. FillCompressedTextureWithSingleColor( red, green, blue, alpha, pImageData, nWidth, nHeight, nDepth, pTexture->Format() );
  2538. }
  2539. else
  2540. {
  2541. for ( int z = 0; z < nDepth; ++z )
  2542. {
  2543. CPixelWriter pixelWriter;
  2544. pixelWriter.SetPixelMemory( pTexture->Format(),
  2545. pTexture->ImageData( iFrame, iFace, iMip, 0, 0, z ), pTexture->RowSizeInBytes( iMip ) );
  2546. for (int y = 0; y < nHeight; ++y)
  2547. {
  2548. pixelWriter.Seek( 0, y );
  2549. for (int x = 0; x < nWidth; ++x)
  2550. {
  2551. pixelWriter.WritePixel( red, green, blue, 255 );
  2552. }
  2553. }
  2554. }
  2555. }
  2556. }
  2557. }
  2558. }
  2559. break;
  2560. }
  2561. }
  2562. //-----------------------------------------------------------------------------
  2563. // Generate a texture that shows the various mip levels
  2564. //-----------------------------------------------------------------------------
  2565. void CTexture::CopyLowResImageToTexture( IVTFTexture *pTexture )
  2566. {
  2567. int nFlags = pTexture->Flags();
  2568. nFlags |= TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_POINTSAMPLE;
  2569. nFlags &= ~(TEXTUREFLAGS_TRILINEAR | TEXTUREFLAGS_ANISOTROPIC | TEXTUREFLAGS_HINT_DXT5);
  2570. nFlags &= ~(TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_ENVMAP);
  2571. nFlags &= ~(TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA);
  2572. Assert( pTexture->FrameCount() == 1 );
  2573. Init( pTexture->Width(), pTexture->Height(), 1, IMAGE_FORMAT_BGR888, nFlags, 1 );
  2574. pTexture->Init( m_LowResImageWidth, m_LowResImageHeight, 1, IMAGE_FORMAT_BGR888, nFlags, 1 );
  2575. // Don't bother computing the actual size; it's actually equal to the low-res size
  2576. // With only one mip level
  2577. m_dimsActual.m_nWidth = m_LowResImageWidth;
  2578. m_dimsActual.m_nHeight = m_LowResImageHeight;
  2579. m_dimsActual.m_nDepth = 1;
  2580. m_dimsActual.m_nMipCount = 1;
  2581. // Copy the row-res image into the VTF Texture
  2582. CPixelWriter pixelWriter;
  2583. pixelWriter.SetPixelMemory( pTexture->Format(),
  2584. pTexture->ImageData( 0, 0, 0 ), pTexture->RowSizeInBytes( 0 ) );
  2585. for ( int y = 0; y < m_LowResImageHeight; ++y )
  2586. {
  2587. pixelWriter.Seek( 0, y );
  2588. for ( int x = 0; x < m_LowResImageWidth; ++x )
  2589. {
  2590. int red = m_pLowResImage[0];
  2591. int green = m_pLowResImage[1];
  2592. int blue = m_pLowResImage[2];
  2593. m_pLowResImage += 3;
  2594. pixelWriter.WritePixel( red, green, blue, 255 );
  2595. }
  2596. }
  2597. }
  2598. //-----------------------------------------------------------------------------
  2599. // Sets up debugging texture bits, if appropriate
  2600. //-----------------------------------------------------------------------------
  2601. bool CTexture::SetupDebuggingTextures( IVTFTexture *pVTFTexture )
  2602. {
  2603. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2604. if ( pVTFTexture->Flags() & TEXTUREFLAGS_NODEBUGOVERRIDE )
  2605. return false;
  2606. // The all mips flag is typically used on detail textures, which can
  2607. // really mess up visualization if we apply the debug-colorized
  2608. // versions of them to debug-colorized base textures, so skip 'em
  2609. if ( g_config.nShowMipLevels && !(pVTFTexture->Flags() & TEXTUREFLAGS_ALL_MIPS) )
  2610. {
  2611. // mat_showmiplevels 1 means don't do normal maps
  2612. if ( ( g_config.nShowMipLevels == 1 ) && ( pVTFTexture->Flags() & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_SSBUMP ) ) )
  2613. return false;
  2614. // mat_showmiplevels 2 means don't do base textures
  2615. if ( ( g_config.nShowMipLevels == 2 ) && !( pVTFTexture->Flags() & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_SSBUMP ) ) )
  2616. return false;
  2617. // This mode shows the mip levels as different colors
  2618. GenerateShowMipLevelsTextures( pVTFTexture );
  2619. return true;
  2620. }
  2621. else if ( g_config.bShowLowResImage && pVTFTexture->FrameCount() == 1 &&
  2622. pVTFTexture->FaceCount() == 1 && ((pVTFTexture->Flags() & TEXTUREFLAGS_NORMAL) == 0) &&
  2623. m_LowResImageWidth != 0 && m_LowResImageHeight != 0 )
  2624. {
  2625. // This mode just uses the low res texture
  2626. CopyLowResImageToTexture( pVTFTexture );
  2627. return true;
  2628. }
  2629. return false;
  2630. }
  2631. //-----------------------------------------------------------------------------
  2632. // Converts the texture to the actual format
  2633. // Returns true if conversion applied, false otherwise
  2634. //-----------------------------------------------------------------------------
  2635. bool CTexture::ConvertToActualFormat( IVTFTexture *pVTFTexture )
  2636. {
  2637. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2638. if ( !g_pShaderDevice->IsUsingGraphics() )
  2639. return false;
  2640. bool bConverted = false;
  2641. ImageFormat fmt = m_ImageFormat;
  2642. ImageFormat dstFormat = ComputeActualFormat( pVTFTexture->Format() );
  2643. if ( fmt != dstFormat )
  2644. {
  2645. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - conversion from (%d to %d)", __FUNCTION__, fmt, dstFormat );
  2646. pVTFTexture->ConvertImageFormat( dstFormat, false );
  2647. m_ImageFormat = dstFormat;
  2648. bConverted = true;
  2649. }
  2650. else if ( HardwareConfig()->GetHDRType() == HDR_TYPE_INTEGER &&
  2651. fmt == dstFormat && dstFormat == IMAGE_FORMAT_RGBA16161616F )
  2652. {
  2653. // This is to force at most the precision of int16 for fp16 texture when running the integer path.
  2654. pVTFTexture->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616, false );
  2655. pVTFTexture->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
  2656. bConverted = true;
  2657. }
  2658. return bConverted;
  2659. }
  2660. void CTexture::GetFilename( char *pOut, int maxLen ) const
  2661. {
  2662. const char *pName = m_Name.String();
  2663. bool bIsUNCName = ( pName[0] == '/' && pName[1] == '/' && pName[2] != '/' );
  2664. if ( !bIsUNCName )
  2665. {
  2666. Q_snprintf( pOut, maxLen,
  2667. "materials/%s" TEXTURE_FNAME_EXTENSION, pName );
  2668. }
  2669. else
  2670. {
  2671. Q_snprintf( pOut, maxLen, "%s" TEXTURE_FNAME_EXTENSION, pName );
  2672. }
  2673. }
  2674. void CTexture::ReloadFilesInList( IFileList *pFilesToReload )
  2675. {
  2676. if ( IsProcedural() || IsRenderTarget() )
  2677. return;
  2678. char filename[MAX_PATH];
  2679. GetFilename( filename, sizeof( filename ) );
  2680. if ( pFilesToReload->IsFileInList( filename ) )
  2681. {
  2682. Download();
  2683. }
  2684. }
  2685. //-----------------------------------------------------------------------------
  2686. // Loads the texture bits from a file.
  2687. //-----------------------------------------------------------------------------
  2688. IVTFTexture *CTexture::LoadTextureBitsFromFile( char *pCacheFileName, char **ppResolvedFilename )
  2689. {
  2690. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %s", __FUNCTION__, tmDynamicString( TELEMETRY_LEVEL0, pCacheFileName ) );
  2691. if ( m_bStreamingFileReadFailed )
  2692. {
  2693. Assert( m_pStreamingVTF == NULL );
  2694. return HandleFileLoadFailedTexture( GetScratchVTFTexture() );
  2695. }
  2696. // OSX hackery
  2697. int nPreserveFlags = 0;
  2698. if ( m_nFlags & TEXTUREFLAGS_SRGB )
  2699. nPreserveFlags |= TEXTUREFLAGS_SRGB;
  2700. unsigned int stripFlags = 0;
  2701. IVTFTexture *pVTFTexture = m_pStreamingVTF;
  2702. if ( !pVTFTexture )
  2703. {
  2704. pVTFTexture = GetScratchVTFTexture();
  2705. FileHandle_t fileHandle = FILESYSTEM_INVALID_HANDLE;
  2706. if ( !GetFileHandle( &fileHandle, pCacheFileName, ppResolvedFilename ) )
  2707. return HandleFileLoadFailedTexture( pVTFTexture );
  2708. TextureLODControlSettings_t settings = m_cachedFileLodSettings;
  2709. if ( !SLoadTextureBitsFromFile( &pVTFTexture, fileHandle, m_nFlags | nPreserveFlags, &settings, m_nDesiredDimensionLimit, &m_nStreamingMips, GetName(), pCacheFileName, &m_dimsMapping, &m_dimsActual, &m_dimsAllocated, &stripFlags ) )
  2710. {
  2711. g_pFullFileSystem->Close( fileHandle );
  2712. return HandleFileLoadFailedTexture( pVTFTexture );
  2713. }
  2714. g_pFullFileSystem->Close( fileHandle );
  2715. }
  2716. // Don't reinitialize here if we're streaming in the fine levels, we already have been initialized with coarse.
  2717. if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE ) == 0 )
  2718. {
  2719. // Initing resets these, but we're happy with the values now--so store and restore them around the Init call.
  2720. TexDimensions_t actual = m_dimsActual,
  2721. allocated = m_dimsAllocated;
  2722. // Initialize the texture class with vtf header data before operations
  2723. Init( m_dimsMapping.m_nWidth,
  2724. m_dimsMapping.m_nHeight,
  2725. m_dimsMapping.m_nDepth,
  2726. pVTFTexture->Format(),
  2727. pVTFTexture->Flags() | nPreserveFlags,
  2728. pVTFTexture->FrameCount()
  2729. );
  2730. m_dimsActual = actual;
  2731. m_dimsAllocated = allocated;
  2732. m_nFlags &= ~stripFlags;
  2733. }
  2734. else
  2735. {
  2736. // Not illegal, just needs investigation.
  2737. Assert( stripFlags == 0 );
  2738. }
  2739. if ( m_pStreamingVTF )
  2740. ComputeActualSize( false, pVTFTexture, ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) != 0 );
  2741. VectorCopy( pVTFTexture->Reflectivity(), m_vecReflectivity );
  2742. // If we've only streamed in coarse but haven't started on fine yet, go ahead and mark us as
  2743. // partially resident and set up our clamping values.
  2744. if ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE ) == TEXTUREFLAGS_STREAMABLE_COARSE )
  2745. {
  2746. pVTFTexture->GetMipmapRange( &m_lodClamp, NULL );
  2747. m_residenceTarget = RESIDENT_PARTIAL;
  2748. m_residenceCurrent = RESIDENT_PARTIAL;
  2749. }
  2750. // Build the low-res texture
  2751. LoadLowResTexture( pVTFTexture );
  2752. // Load the resources
  2753. if ( unsigned int uiRsrcCount = pVTFTexture->GetResourceTypes( NULL, 0 ) )
  2754. {
  2755. uint32 *arrRsrcTypes = ( uint32 * )_alloca( uiRsrcCount * sizeof( unsigned int ) );
  2756. pVTFTexture->GetResourceTypes( arrRsrcTypes, uiRsrcCount );
  2757. m_arrDataChunks.EnsureCapacity( uiRsrcCount );
  2758. for ( uint32 *arrRsrcTypesEnd = arrRsrcTypes + uiRsrcCount;
  2759. arrRsrcTypes < arrRsrcTypesEnd; ++arrRsrcTypes )
  2760. {
  2761. switch ( *arrRsrcTypes )
  2762. {
  2763. case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
  2764. case VTF_LEGACY_RSRC_IMAGE:
  2765. // These stock types use specific load routines
  2766. continue;
  2767. default:
  2768. {
  2769. DataChunk dc;
  2770. dc.m_eType = *arrRsrcTypes;
  2771. dc.m_eType &= ~RSRCF_MASK;
  2772. size_t numBytes;
  2773. if ( void *pvData = pVTFTexture->GetResourceData( dc.m_eType, &numBytes ) )
  2774. {
  2775. Assert( numBytes >= sizeof( uint32 ) );
  2776. if ( numBytes == sizeof( dc.m_numBytes ) )
  2777. {
  2778. dc.m_eType |= RSRCF_HAS_NO_DATA_CHUNK;
  2779. dc.m_pvData = NULL;
  2780. memcpy( &dc.m_numBytes, pvData, numBytes );
  2781. }
  2782. else
  2783. {
  2784. dc.Allocate( numBytes );
  2785. memcpy( dc.m_pvData, pvData, numBytes );
  2786. }
  2787. m_arrDataChunks.AddToTail( dc );
  2788. }
  2789. }
  2790. }
  2791. }
  2792. }
  2793. // Try to set up debugging textures, if we're in a debugging mode
  2794. if ( !IsProcedural() )
  2795. SetupDebuggingTextures( pVTFTexture );
  2796. if ( ConvertToActualFormat( pVTFTexture ) )
  2797. pVTFTexture; // STAGING_ONLY_EXEC ( Warning( "\"%s\" not in final format, this is causing stutters or load time bloat!\n", pCacheFileName ) );
  2798. return pVTFTexture;
  2799. }
  2800. IVTFTexture *CTexture::HandleFileLoadFailedTexture( IVTFTexture *pVTFTexture )
  2801. {
  2802. // create the error texture
  2803. // This will make a checkerboard texture to indicate failure
  2804. pVTFTexture->Init( 32, 32, 1, IMAGE_FORMAT_BGRA8888, m_nFlags, 1 );
  2805. Init( pVTFTexture->Width(), pVTFTexture->Height(), pVTFTexture->Depth(), pVTFTexture->Format(),
  2806. pVTFTexture->Flags(), pVTFTexture->FrameCount() );
  2807. m_vecReflectivity.Init( 0.5f, 0.5f, 0.5f );
  2808. // NOTE: For mat_picmip to work, we must use the same size (32x32)
  2809. // Which should work since every card can handle textures of that size
  2810. m_dimsAllocated.m_nWidth = m_dimsActual.m_nWidth = pVTFTexture->Width();
  2811. m_dimsAllocated.m_nHeight = m_dimsActual.m_nHeight = pVTFTexture->Height();
  2812. m_dimsAllocated.m_nDepth = 1;
  2813. m_dimsAllocated.m_nMipCount = m_dimsActual.m_nMipCount = 1;
  2814. m_nStreamingMips = 0;
  2815. // generate the checkerboard
  2816. TextureManager()->GenerateErrorTexture( this, pVTFTexture );
  2817. ConvertToActualFormat( pVTFTexture );
  2818. // Deactivate procedural texture...
  2819. m_nFlags &= ~TEXTUREFLAGS_PROCEDURAL;
  2820. SetErrorTexture( true );
  2821. return pVTFTexture;
  2822. }
  2823. //-----------------------------------------------------------------------------
  2824. // Computes subrect for a particular miplevel
  2825. //-----------------------------------------------------------------------------
  2826. void CTexture::ComputeMipLevelSubRect( const Rect_t* pSrcRect, int nMipLevel, Rect_t *pSubRect )
  2827. {
  2828. if (nMipLevel == 0)
  2829. {
  2830. *pSubRect = *pSrcRect;
  2831. return;
  2832. }
  2833. float flInvShrink = 1.0f / (float)(1 << nMipLevel);
  2834. pSubRect->x = pSrcRect->x * flInvShrink;
  2835. pSubRect->y = pSrcRect->y * flInvShrink;
  2836. pSubRect->width = (int)ceil( (pSrcRect->x + pSrcRect->width) * flInvShrink ) - pSubRect->x;
  2837. pSubRect->height = (int)ceil( (pSrcRect->y + pSrcRect->height) * flInvShrink ) - pSubRect->y;
  2838. }
  2839. //-----------------------------------------------------------------------------
  2840. // Computes the face count + first face
  2841. //-----------------------------------------------------------------------------
  2842. void CTexture::GetDownloadFaceCount( int &nFirstFace, int &nFaceCount )
  2843. {
  2844. nFaceCount = 1;
  2845. nFirstFace = 0;
  2846. if ( IsCubeMap() )
  2847. {
  2848. if ( HardwareConfig()->SupportsCubeMaps() )
  2849. {
  2850. nFaceCount = CUBEMAP_FACE_COUNT-1;
  2851. }
  2852. else
  2853. {
  2854. // This will cause us to use the spheremap instead of the cube faces
  2855. // in the case where we don't support cubemaps
  2856. nFirstFace = CUBEMAP_FACE_SPHEREMAP;
  2857. }
  2858. }
  2859. }
  2860. //-----------------------------------------------------------------------------
  2861. // Fixup a queue loaded texture with the delayed hi-res data
  2862. //-----------------------------------------------------------------------------
  2863. void CTexture::FixupTexture( const void *pData, int nSize, LoaderError_t loaderError )
  2864. {
  2865. if ( loaderError != LOADERERROR_NONE )
  2866. {
  2867. // mark as invalid
  2868. nSize = 0;
  2869. }
  2870. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_QUEUEDLOAD;
  2871. // Make sure we've actually allocated the texture handles
  2872. Assert( HasBeenAllocated() );
  2873. }
  2874. static void QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  2875. {
  2876. reinterpret_cast< CTexture * >( pContext )->FixupTexture( pData, nSize, loaderError );
  2877. }
  2878. //-----------------------------------------------------------------------------
  2879. // Generates the procedural bits
  2880. //-----------------------------------------------------------------------------
  2881. IVTFTexture *CTexture::ReconstructPartialProceduralBits( const Rect_t *pRect, Rect_t *pActualRect )
  2882. {
  2883. // Figure out the actual size for this texture based on the current mode
  2884. bool bIgnorePicmip = ( m_nFlags & ( TEXTUREFLAGS_STAGING_MEMORY | TEXTUREFLAGS_IGNORE_PICMIP ) ) != 0;
  2885. ComputeActualSize( bIgnorePicmip );
  2886. // Figure out how many mip levels we're skipping...
  2887. int nSizeFactor = 1;
  2888. int nWidth = GetActualWidth();
  2889. if ( nWidth != 0 )
  2890. {
  2891. nSizeFactor = GetMappingWidth() / nWidth;
  2892. }
  2893. int nMipSkipCount = 0;
  2894. while (nSizeFactor > 1)
  2895. {
  2896. nSizeFactor >>= 1;
  2897. ++nMipSkipCount;
  2898. }
  2899. // Determine a rectangle appropriate for the actual size...
  2900. // It must bound all partially-covered pixels..
  2901. ComputeMipLevelSubRect( pRect, nMipSkipCount, pActualRect );
  2902. // Create the texture
  2903. IVTFTexture *pVTFTexture = GetScratchVTFTexture();
  2904. // Initialize the texture
  2905. pVTFTexture->Init( m_dimsActual.m_nWidth, m_dimsActual.m_nHeight, m_dimsActual.m_nDepth,
  2906. ComputeActualFormat( m_ImageFormat ), m_nFlags, m_nFrameCount );
  2907. // Generate the bits from the installed procedural regenerator
  2908. if ( m_pTextureRegenerator )
  2909. {
  2910. m_pTextureRegenerator->RegenerateTextureBits( this, pVTFTexture, pActualRect );
  2911. }
  2912. else
  2913. {
  2914. // In this case, we don't have one, so just use a checkerboard...
  2915. TextureManager()->GenerateErrorTexture( this, pVTFTexture );
  2916. }
  2917. return pVTFTexture;
  2918. }
  2919. //-----------------------------------------------------------------------------
  2920. // Regenerates the bits of a texture within a particular rectangle
  2921. //-----------------------------------------------------------------------------
  2922. void CTexture::ReconstructPartialTexture( const Rect_t *pRect )
  2923. {
  2924. // FIXME: for now, only procedural textures can handle sub-rect specification.
  2925. Assert( IsProcedural() );
  2926. // Also, we need procedural textures that have only a single copy!!
  2927. // Otherwise this partial upload will not occur on all copies
  2928. Assert( m_nFlags & TEXTUREFLAGS_SINGLECOPY );
  2929. Rect_t vtfRect;
  2930. IVTFTexture *pVTFTexture = ReconstructPartialProceduralBits( pRect, &vtfRect );
  2931. // FIXME: for now, depth textures do not work with this.
  2932. Assert( pVTFTexture->Depth() == 1 );
  2933. // Make sure we've allocated the API textures
  2934. if ( !HasBeenAllocated() )
  2935. {
  2936. if ( !AllocateShaderAPITextures() )
  2937. return;
  2938. }
  2939. int nFaceCount, nFirstFace;
  2940. GetDownloadFaceCount( nFirstFace, nFaceCount );
  2941. // Blit down portions of the various VTF frames into the board memory
  2942. int nStride;
  2943. Rect_t mipRect;
  2944. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  2945. {
  2946. Modify( iFrame );
  2947. for ( int iFace = 0; iFace < nFaceCount; ++iFace )
  2948. {
  2949. for ( int iMip = 0; iMip < m_dimsActual.m_nMipCount; ++iMip )
  2950. {
  2951. pVTFTexture->ComputeMipLevelSubRect( &vtfRect, iMip, &mipRect );
  2952. nStride = pVTFTexture->RowSizeInBytes( iMip );
  2953. unsigned char *pBits = pVTFTexture->ImageData( iFrame, iFace + nFirstFace, iMip, mipRect.x, mipRect.y, 0 );
  2954. g_pShaderAPI->TexSubImage2D(
  2955. iMip,
  2956. iFace,
  2957. mipRect.x,
  2958. mipRect.y,
  2959. 0,
  2960. mipRect.width,
  2961. mipRect.height,
  2962. pVTFTexture->Format(),
  2963. nStride,
  2964. false,
  2965. pBits );
  2966. }
  2967. }
  2968. }
  2969. }
  2970. //-----------------------------------------------------------------------------
  2971. // Generates the procedural bits
  2972. //-----------------------------------------------------------------------------
  2973. IVTFTexture *CTexture::ReconstructProceduralBits()
  2974. {
  2975. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2976. // Figure out the actual size for this texture based on the current mode
  2977. bool bIgnorePicmip = ( m_nFlags & ( TEXTUREFLAGS_STAGING_MEMORY | TEXTUREFLAGS_IGNORE_PICMIP ) ) != 0;
  2978. ComputeActualSize( bIgnorePicmip );
  2979. // Create the texture
  2980. IVTFTexture *pVTFTexture = NULL;
  2981. {
  2982. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - GetScratchVTFTexture", __FUNCTION__ );
  2983. pVTFTexture = GetScratchVTFTexture();
  2984. }
  2985. {
  2986. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Init", __FUNCTION__ );
  2987. // Initialize the texture
  2988. pVTFTexture->Init( m_dimsActual.m_nWidth, m_dimsActual.m_nHeight, m_dimsActual.m_nDepth,
  2989. ComputeActualFormat( m_ImageFormat ), m_nFlags, m_nFrameCount );
  2990. }
  2991. // Generate the bits from the installed procedural regenerator
  2992. if ( m_pTextureRegenerator )
  2993. {
  2994. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - RegenerateTextureBits", __FUNCTION__ );
  2995. Rect_t rect;
  2996. rect.x = 0; rect.y = 0;
  2997. rect.width = m_dimsActual.m_nWidth;
  2998. rect.height = m_dimsActual.m_nHeight;
  2999. m_pTextureRegenerator->RegenerateTextureBits( this, pVTFTexture, &rect );
  3000. }
  3001. else
  3002. {
  3003. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - GenerateErrorTexture", __FUNCTION__ );
  3004. // In this case, we don't have one, so just use a checkerboard...
  3005. TextureManager()->GenerateErrorTexture( this, pVTFTexture );
  3006. }
  3007. return pVTFTexture;
  3008. }
  3009. void CTexture::WriteDataToShaderAPITexture( int nFrameCount, int nFaceCount, int nFirstFace, int nMipCount, IVTFTexture *pVTFTexture, ImageFormat fmt )
  3010. {
  3011. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  3012. // If we're a staging texture, there's nothing to do.
  3013. if ( ( m_nFlags & TEXTUREFLAGS_STAGING_MEMORY ) != 0 )
  3014. return;
  3015. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  3016. {
  3017. Modify( iFrame );
  3018. g_pShaderAPI->TexImageFromVTF( pVTFTexture, iFrame );
  3019. }
  3020. }
  3021. bool CTexture::IsDepthTextureFormat( ImageFormat fmt )
  3022. {
  3023. return ( ( m_ImageFormat == IMAGE_FORMAT_NV_DST16 ) ||
  3024. ( m_ImageFormat == IMAGE_FORMAT_NV_DST24 ) ||
  3025. ( m_ImageFormat == IMAGE_FORMAT_NV_INTZ ) ||
  3026. ( m_ImageFormat == IMAGE_FORMAT_NV_RAWZ ) ||
  3027. ( m_ImageFormat == IMAGE_FORMAT_ATI_DST16 ) ||
  3028. ( m_ImageFormat == IMAGE_FORMAT_ATI_DST24 ) );
  3029. }
  3030. //-----------------------------------------------------------------------------
  3031. void CTexture::NotifyUnloadedFile()
  3032. {
  3033. // Make sure we have a regular texture that was loaded from a file
  3034. if ( IsProcedural() || IsRenderTarget() || !m_Name.IsValid() )
  3035. return;
  3036. const char *pName = m_Name.String();
  3037. if ( *pName == '\0' )
  3038. return;
  3039. bool bIsUNCName = ( pName[0] == '/' && pName[1] == '/' && pName[2] != '/' );
  3040. if ( bIsUNCName )
  3041. return;
  3042. // Generate the filename
  3043. char pCacheFileName[MATERIAL_MAX_PATH];
  3044. Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "materials/%s" TEXTURE_FNAME_EXTENSION, pName );
  3045. // Let filesystem know that the file is uncached, so it knows
  3046. // what to do with tracking info
  3047. g_pFullFileSystem->NotifyFileUnloaded( pCacheFileName, "GAME" );
  3048. }
  3049. //-----------------------------------------------------------------------------
  3050. // Sets or updates the texture bits
  3051. //-----------------------------------------------------------------------------
  3052. void CTexture::ReconstructTexture( bool bCopyFromCurrent )
  3053. {
  3054. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  3055. Assert( !bCopyFromCurrent || HardwareConfig()->CanStretchRectFromTextures() );
  3056. int oldWidth = m_dimsAllocated.m_nWidth;
  3057. int oldHeight = m_dimsAllocated.m_nHeight;
  3058. int oldDepth = m_dimsAllocated.m_nDepth;
  3059. int oldMipCount = m_dimsAllocated.m_nMipCount;
  3060. int oldFrameCount = m_nFrameCount;
  3061. // FIXME: Should RenderTargets be a special case of Procedural?
  3062. char *pResolvedFilename = NULL;
  3063. IVTFTexture *pVTFTexture = NULL;
  3064. {
  3065. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Begin", __FUNCTION__ );
  3066. if ( IsProcedural() )
  3067. {
  3068. // This will call the installed texture bit regeneration interface
  3069. pVTFTexture = ReconstructProceduralBits();
  3070. }
  3071. else if ( IsRenderTarget() )
  3072. {
  3073. // Compute the actual size + format based on the current mode
  3074. bool bIgnorePicmip = m_RenderTargetSizeMode != RT_SIZE_LITERAL_PICMIP;
  3075. ComputeActualSize( bIgnorePicmip );
  3076. }
  3077. else if ( bCopyFromCurrent )
  3078. {
  3079. ComputeActualSize( false, NULL, true );
  3080. }
  3081. else
  3082. {
  3083. NotifyUnloadedFile();
  3084. char pCacheFileName[ MATERIAL_MAX_PATH ] = { 0 };
  3085. GetCacheFilename( pCacheFileName, ARRAYSIZE( pCacheFileName ) );
  3086. // Get the data from disk...
  3087. // NOTE: Reloading the texture bits can cause the texture size, frames, format, pretty much *anything* can change.
  3088. pVTFTexture = LoadTextureBitsFromFile( pCacheFileName, &pResolvedFilename );
  3089. }
  3090. }
  3091. if ( !HasBeenAllocated() ||
  3092. m_dimsAllocated.m_nWidth != oldWidth ||
  3093. m_dimsAllocated.m_nHeight != oldHeight ||
  3094. m_dimsAllocated.m_nDepth != oldDepth ||
  3095. m_dimsAllocated.m_nMipCount != oldMipCount ||
  3096. m_nFrameCount != oldFrameCount )
  3097. {
  3098. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Allocation", __FUNCTION__ );
  3099. const bool cbCanStretchRectTextures = HardwareConfig()->CanStretchRectFromTextures();
  3100. const bool cbShouldMigrateTextures = ( ( m_nFlags & TEXTUREFLAGS_STREAMABLE_FINE ) != 0 ) && m_nFrameCount == oldFrameCount;
  3101. // If we're just streaming in more data--or demoting ourselves, do a migration instead.
  3102. if ( bCopyFromCurrent || ( cbCanStretchRectTextures && cbShouldMigrateTextures ) )
  3103. {
  3104. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Migration", __FUNCTION__ );
  3105. MigrateShaderAPITextures();
  3106. // Ahh--I feel terrible about this, but we genuinely don't need anything else if we're streaming.
  3107. if ( bCopyFromCurrent )
  3108. return;
  3109. }
  3110. else
  3111. {
  3112. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Deallocate / Allocate", __FUNCTION__ );
  3113. // If we're doing a wholesale copy, we need to restore these values that will be cleared by FreeShaderAPITextures.
  3114. // Record them here, restore them below.
  3115. unsigned int restoreStreamingFlag = ( m_nFlags & TEXTUREFLAGS_STREAMABLE );
  3116. ResidencyType_t restoreResidenceCurrent = m_residenceCurrent;
  3117. ResidencyType_t restoreResidenceTarget = m_residenceTarget;
  3118. if ( HasBeenAllocated() )
  3119. {
  3120. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Deallocate", __FUNCTION__ );
  3121. // This is necessary for the reload case, we may discover there
  3122. // are more frames of a texture animation, for example, which means
  3123. // we can't rely on having the same number of texture frames.
  3124. FreeShaderAPITextures();
  3125. }
  3126. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Allocate", __FUNCTION__ );
  3127. // Create the shader api textures
  3128. if ( !AllocateShaderAPITextures() )
  3129. return;
  3130. // Restored once we successfully allocate the shader api textures, but only if we're
  3131. //
  3132. if ( !cbCanStretchRectTextures && cbShouldMigrateTextures )
  3133. {
  3134. m_nFlags |= restoreStreamingFlag;
  3135. m_residenceCurrent = restoreResidenceCurrent;
  3136. m_residenceTarget = restoreResidenceTarget;
  3137. }
  3138. }
  3139. }
  3140. else if ( bCopyFromCurrent )
  3141. {
  3142. Assert( !"We're about to crash, last chance to examine this texture." );
  3143. }
  3144. // Render Targets just need to be cleared, they have no upload
  3145. if ( IsRenderTarget() )
  3146. {
  3147. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - RT Stuff", __FUNCTION__ );
  3148. // Clear the render target to opaque black
  3149. // Only clear if we're not a depth-stencil texture
  3150. if ( !IsDepthTextureFormat( m_ImageFormat ) )
  3151. {
  3152. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Clearing", __FUNCTION__ );
  3153. CMatRenderContextPtr pRenderContext( MaterialSystem() );
  3154. ITexture *pThisTexture = GetEmbeddedTexture( 0 );
  3155. pRenderContext->PushRenderTargetAndViewport( pThisTexture ); // Push this texture on the stack
  3156. g_pShaderAPI->ClearColor4ub( 0, 0, 0, 0xFF ); // Set the clear color to opaque black
  3157. g_pShaderAPI->ClearBuffers( true, false, false, m_dimsActual.m_nWidth, m_dimsActual.m_nHeight ); // Clear the target
  3158. pRenderContext->PopRenderTargetAndViewport(); // Pop back to previous target
  3159. }
  3160. // no upload
  3161. return;
  3162. }
  3163. // Blit down the texture faces, frames, and mips into the board memory
  3164. int nFirstFace, nFaceCount;
  3165. GetDownloadFaceCount( nFirstFace, nFaceCount );
  3166. WriteDataToShaderAPITexture( m_nFrameCount, nFaceCount, nFirstFace, m_dimsActual.m_nMipCount, pVTFTexture, m_ImageFormat );
  3167. ReleaseScratchVTFTexture( pVTFTexture );
  3168. pVTFTexture = NULL;
  3169. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Final Cleanup", __FUNCTION__ );
  3170. // allocated by strdup
  3171. free( pResolvedFilename );
  3172. // the pc can afford to persist a large buffer
  3173. FreeOptimalReadBuffer( 6*1024*1024 );
  3174. }
  3175. void CTexture::GetCacheFilename( char* pOutBuffer, int nBufferSize ) const
  3176. {
  3177. Assert( pOutBuffer );
  3178. if ( IsProcedural() || IsRenderTarget() )
  3179. {
  3180. pOutBuffer[0] = 0;
  3181. return;
  3182. }
  3183. else
  3184. {
  3185. const char *pName;
  3186. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE )
  3187. {
  3188. pName = "dev/dev_exclude_error";
  3189. }
  3190. else
  3191. {
  3192. pName = m_Name.String();
  3193. }
  3194. bool bIsUNCName = ( pName[ 0 ] == '/' && pName[ 1 ] == '/' && pName[ 2 ] != '/' );
  3195. if ( !bIsUNCName )
  3196. {
  3197. Q_snprintf( pOutBuffer, nBufferSize, "materials/%s" TEXTURE_FNAME_EXTENSION, pName );
  3198. }
  3199. else
  3200. {
  3201. Q_snprintf( pOutBuffer, nBufferSize, "%s" TEXTURE_FNAME_EXTENSION, pName );
  3202. }
  3203. }
  3204. }
  3205. bool CTexture::GetFileHandle( FileHandle_t *pOutFileHandle, char *pCacheFileName, char **ppResolvedFilename ) const
  3206. {
  3207. Assert( pOutFileHandle );
  3208. FileHandle_t& fileHandle = *pOutFileHandle;
  3209. fileHandle = FILESYSTEM_INVALID_HANDLE;
  3210. while ( fileHandle == FILESYSTEM_INVALID_HANDLE ) // run until found a file or out of rules
  3211. {
  3212. fileHandle = g_pFullFileSystem->OpenEx( pCacheFileName, "rb", 0, MaterialSystem()->GetForcedTextureLoadPathID(), ppResolvedFilename );
  3213. if ( fileHandle == FILESYSTEM_INVALID_HANDLE )
  3214. {
  3215. // try any fallbacks.
  3216. char *pHdrExt = Q_stristr( pCacheFileName, ".hdr" TEXTURE_FNAME_EXTENSION );
  3217. if ( pHdrExt )
  3218. {
  3219. DevWarning( "A custom HDR cubemap \"%s\": cannot be found on disk.\n"
  3220. "This really should have a HDR version, trying a fall back to a non-HDR version.\n", pCacheFileName );
  3221. strcpy( pHdrExt, TEXTURE_FNAME_EXTENSION );
  3222. }
  3223. else
  3224. {
  3225. // no more fallbacks
  3226. break;
  3227. }
  3228. }
  3229. }
  3230. if ( fileHandle == FILESYSTEM_INVALID_HANDLE )
  3231. {
  3232. if ( Q_strnicmp( m_Name.String(), "env_cubemap", 12 ) )
  3233. {
  3234. if ( IsPosix() )
  3235. {
  3236. Msg( "\n ##### CTexture::LoadTextureBitsFromFile couldn't find %s\n", pCacheFileName );
  3237. }
  3238. DevWarning( "\"%s\": can't be found on disk\n", pCacheFileName );
  3239. }
  3240. return false;
  3241. }
  3242. return true;
  3243. }
  3244. // Get the shaderapi texture handle associated w/ a particular frame
  3245. ShaderAPITextureHandle_t CTexture::GetTextureHandle( int nFrame, int nTextureChannel )
  3246. {
  3247. if ( nFrame < 0 )
  3248. {
  3249. nFrame = 0;
  3250. Warning( "CTexture::GetTextureHandle(): nFrame is < 0!\n" );
  3251. }
  3252. if ( nFrame >= m_nFrameCount )
  3253. {
  3254. // 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.
  3255. Assert( nFrame < m_nFrameCount );
  3256. return INVALID_SHADERAPI_TEXTURE_HANDLE;
  3257. }
  3258. Assert( nTextureChannel < 2 );
  3259. // Make sure we've actually allocated the texture handles
  3260. Assert( m_pTextureHandles );
  3261. Assert( HasBeenAllocated() );
  3262. if ( m_pTextureHandles == NULL || !HasBeenAllocated() )
  3263. {
  3264. return INVALID_SHADERAPI_TEXTURE_HANDLE;
  3265. }
  3266. // Don't get paired handle here...callers of this function don't know about paired textures
  3267. return m_pTextureHandles[nFrame];
  3268. }
  3269. void CTexture::GetLowResColorSample( float s, float t, float *color ) const
  3270. {
  3271. if ( m_LowResImageWidth <= 0 || m_LowResImageHeight <= 0 )
  3272. {
  3273. // Warning( "Programming error: GetLowResColorSample \"%s\": %dx%d\n", m_pName, ( int )m_LowResImageWidth, ( int )m_LowResImageHeight );
  3274. return;
  3275. }
  3276. // force s and t into [0,1)
  3277. if ( s < 0.0f )
  3278. {
  3279. s = ( 1.0f - ( float )( int )s ) + s;
  3280. }
  3281. if ( t < 0.0f )
  3282. {
  3283. t = ( 1.0f - ( float )( int )t ) + t;
  3284. }
  3285. s = s - ( float )( int )s;
  3286. t = t - ( float )( int )t;
  3287. s *= m_LowResImageWidth;
  3288. t *= m_LowResImageHeight;
  3289. int wholeS, wholeT;
  3290. wholeS = ( int )s;
  3291. wholeT = ( int )t;
  3292. float fracS, fracT;
  3293. fracS = s - ( float )( int )s;
  3294. fracT = t - ( float )( int )t;
  3295. // filter twice in the s dimension.
  3296. float sColor[2][3];
  3297. int wholeSPlusOne = ( wholeS + 1 ) % m_LowResImageWidth;
  3298. int wholeTPlusOne = ( wholeT + 1 ) % m_LowResImageHeight;
  3299. sColor[0][0] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeT * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  3300. sColor[0][1] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeT * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  3301. sColor[0][2] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeT * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  3302. sColor[0][0] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  3303. sColor[0][1] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  3304. sColor[0][2] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  3305. sColor[1][0] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  3306. sColor[1][1] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  3307. sColor[1][2] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  3308. sColor[1][0] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  3309. sColor[1][1] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  3310. sColor[1][2] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  3311. color[0] = sColor[0][0] * ( 1.0f - fracT ) + sColor[1][0] * fracT;
  3312. color[1] = sColor[0][1] * ( 1.0f - fracT ) + sColor[1][1] * fracT;
  3313. color[2] = sColor[0][2] * ( 1.0f - fracT ) + sColor[1][2] * fracT;
  3314. }
  3315. int CTexture::GetApproximateVidMemBytes( void ) const
  3316. {
  3317. ImageFormat format = GetImageFormat();
  3318. int width = GetActualWidth();
  3319. int height = GetActualHeight();
  3320. int depth = GetActualDepth();
  3321. int numFrames = GetNumAnimationFrames();
  3322. bool isMipmapped = IsMipmapped();
  3323. return numFrames * ImageLoader::GetMemRequired( width, height, depth, format, isMipmapped );
  3324. }
  3325. void CTexture::CopyFrameBufferToMe( int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect )
  3326. {
  3327. Assert( m_pTextureHandles && m_nFrameCount >= 1 );
  3328. if ( m_pTextureHandles && m_nFrameCount >= 1 )
  3329. {
  3330. g_pShaderAPI->CopyRenderTargetToTextureEx( m_pTextureHandles[0], nRenderTargetID, pSrcRect, pDstRect );
  3331. }
  3332. }
  3333. void CTexture::CopyMeToFrameBuffer( int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect )
  3334. {
  3335. Assert( m_pTextureHandles && m_nFrameCount >= 1 );
  3336. if ( m_pTextureHandles && m_nFrameCount >= 1 )
  3337. {
  3338. g_pShaderAPI->CopyTextureToRenderTargetEx( nRenderTargetID, m_pTextureHandles[0], pSrcRect, pDstRect );
  3339. }
  3340. }
  3341. ITexture *CTexture::GetEmbeddedTexture( int nIndex )
  3342. {
  3343. return ( nIndex == 0 ) ? this : NULL;
  3344. }
  3345. void CTexture::DeleteIfUnreferenced()
  3346. {
  3347. if ( m_nRefCount > 0 )
  3348. return;
  3349. if ( ThreadInMainThread() )
  3350. {
  3351. // Render thread better not be active or bad things can happen.
  3352. Assert( MaterialSystem()->GetRenderThreadId() == 0xFFFFFFFF );
  3353. TextureManager()->RemoveTexture( this );
  3354. return;
  3355. }
  3356. // Can't actually clean up from render thread--just safely mark this texture as
  3357. // one we should check for cleanup next EndFrame when it's safe.
  3358. TextureManager()->MarkUnreferencedTextureForCleanup( this );
  3359. }
  3360. //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
  3361. void CTexture::SwapContents( ITexture *pOther )
  3362. {
  3363. if( (pOther == NULL) || (pOther == this) )
  3364. return;
  3365. ICallQueue *pCallQueue = materials->GetRenderContext()->GetCallQueue();
  3366. if ( pCallQueue )
  3367. {
  3368. pCallQueue->QueueCall( this, &CTexture::SwapContents, pOther );
  3369. return;
  3370. }
  3371. AssertMsg( dynamic_cast<CTexture *>(pOther) != NULL, "Texture swapping broken" );
  3372. CTexture *pOtherAsCTexture = (CTexture *)pOther;
  3373. CTexture *pTemp = (CTexture *)stackalloc( sizeof( CTexture ) );
  3374. //swap everything. Note that this copies the entire object including the
  3375. // vtable pointer, thus ruining polymorphism. Use with care.
  3376. // The unnecessary casts to (void*) hint to clang that we know what we
  3377. // are doing.
  3378. memcpy( (void*)pTemp, (const void*)this, sizeof( CTexture ) );
  3379. memcpy( (void*)this, (const void*)pOtherAsCTexture, sizeof( CTexture ) );
  3380. memcpy( (void*)pOtherAsCTexture, (const void*)pTemp, sizeof( CTexture ) );
  3381. //we have the other's name, give it back
  3382. memcpy( &pOtherAsCTexture->m_Name, &m_Name, sizeof( m_Name ) );
  3383. //pTemp still has our name
  3384. memcpy( &m_Name, &pTemp->m_Name, sizeof( m_Name ) );
  3385. }
  3386. void CTexture::MarkAsPreloaded( bool bSet )
  3387. {
  3388. if ( bSet )
  3389. {
  3390. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_PRELOADED;
  3391. }
  3392. else
  3393. {
  3394. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_PRELOADED;
  3395. }
  3396. }
  3397. bool CTexture::IsPreloaded() const
  3398. {
  3399. return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_PRELOADED ) != 0 );
  3400. }
  3401. void CTexture::MarkAsExcluded( bool bSet, int nDimensionsLimit )
  3402. {
  3403. if ( bSet )
  3404. {
  3405. // exclusion trumps picmipping
  3406. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE;
  3407. m_nDesiredDimensionLimit = 0;
  3408. }
  3409. else
  3410. {
  3411. // not excluding, but can optionally picmip
  3412. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE;
  3413. m_nDesiredDimensionLimit = nDimensionsLimit;
  3414. }
  3415. }
  3416. bool CTexture::UpdateExcludedState( void )
  3417. {
  3418. bool bDesired = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE ) != 0;
  3419. bool bActual = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_EXCLUDED ) != 0;
  3420. if ( ( bDesired == bActual ) && ( m_nDesiredDimensionLimit == m_nActualDimensionLimit ) )
  3421. {
  3422. return false;
  3423. }
  3424. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD )
  3425. {
  3426. // already scheduled
  3427. return true;
  3428. }
  3429. // force the texture to re-download, causes the texture bits to match its desired exclusion state
  3430. Download();
  3431. return true;
  3432. }
  3433. void CTextureStreamingJob::OnAsyncFindComplete( ITexture* pTex, void* pExtraArgs )
  3434. {
  3435. const int cArgsAsInt = ( int ) pExtraArgs;
  3436. Assert( m_pOwner == NULL || m_pOwner == pTex );
  3437. if ( m_pOwner )
  3438. m_pOwner->OnStreamingJobComplete( static_cast<ResidencyType_t>( cArgsAsInt ) );
  3439. // OnStreamingJobComplete should've cleaned us up
  3440. Assert( m_pOwner == NULL );
  3441. }
  3442. // ------------------------------------------------------------------------------------------------
  3443. int GetThreadId()
  3444. {
  3445. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  3446. // Turns the current thread into a 0-based index for use in accessing statics in this file.
  3447. int retVal = INT_MAX;
  3448. if ( ThreadInMainThread() )
  3449. retVal = 0;
  3450. else if ( MaterialSystem()->GetRenderThreadId() == ThreadGetCurrentId() )
  3451. retVal = 1;
  3452. else if ( TextureManager()->ThreadInAsyncLoadThread() )
  3453. retVal = 2;
  3454. else if ( TextureManager()->ThreadInAsyncReadThread() )
  3455. retVal = 3;
  3456. else
  3457. {
  3458. STAGING_ONLY_EXEC( AssertAlways( !"Unexpected thread in GetThreadId, need to debug this--crash is next. Tell McJohn." ) );
  3459. DebuggerBreakIfDebugging_StagingOnly();
  3460. }
  3461. Assert( retVal < MAX_RENDER_THREADS );
  3462. return retVal;
  3463. }
  3464. // ------------------------------------------------------------------------------------------------
  3465. bool SLoadTextureBitsFromFile( IVTFTexture **ppOutVtfTexture, FileHandle_t hFile, unsigned int nFlags,
  3466. TextureLODControlSettings_t* pInOutCachedFileLodSettings,
  3467. int nDesiredDimensionLimit, unsigned short* pOutStreamedMips,
  3468. const char* pName, const char* pCacheFileName,
  3469. TexDimensions_t* pOptOutDimsMapping,
  3470. TexDimensions_t* pOptOutDimsActual,
  3471. TexDimensions_t* pOptOutDimsAllocated,
  3472. unsigned int* pOptOutStripFlags )
  3473. {
  3474. // NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
  3475. // NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
  3476. // NOTE! NOTE! NOTE! or by the streaming texture code!
  3477. Assert( ppOutVtfTexture != NULL && *ppOutVtfTexture != NULL );
  3478. if ( V_strstr( pName, "c_rocketlauncher/c_rocketlauncher" ) )
  3479. {
  3480. int i = 0;
  3481. i = 3;
  3482. }
  3483. CUtlBuffer buf;
  3484. {
  3485. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - ReadHeaderFromFile", __FUNCTION__ );
  3486. int nHeaderSize = VTFFileHeaderSize( VTF_MAJOR_VERSION );
  3487. // restrict read to the header only!
  3488. // header provides info to avoid reading the entire file
  3489. int nBytesOptimalRead = GetOptimalReadBuffer( &buf, hFile, nHeaderSize );
  3490. int nBytesRead = g_pFullFileSystem->ReadEx( buf.Base(), nBytesOptimalRead, Min( nHeaderSize, ( int ) g_pFullFileSystem->Size( hFile ) ), hFile ); // only read as much as the file has
  3491. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  3492. nBytesRead = nHeaderSize = ( ( VTFFileBaseHeader_t * ) buf.Base() )->headerSize;
  3493. g_pFullFileSystem->Seek( hFile, nHeaderSize, FILESYSTEM_SEEK_HEAD );
  3494. }
  3495. // Unserialize the header only
  3496. // need the header first to determine remainder of data
  3497. if ( !( *ppOutVtfTexture )->Unserialize( buf, true ) )
  3498. {
  3499. Warning( "Error reading texture header \"%s\"\n", pCacheFileName );
  3500. return false;
  3501. }
  3502. // Need to record this now, before we ask for the trimmed down data to potentially be loaded.
  3503. TexDimensions_t dimsMappingCurrent( ( *ppOutVtfTexture )->Width(), ( *ppOutVtfTexture )->Height(), ( *ppOutVtfTexture )->MipCount(), ( *ppOutVtfTexture )->Depth() );
  3504. if ( pOptOutDimsMapping )
  3505. ( *pOptOutDimsMapping ) = dimsMappingCurrent;
  3506. int nFullFlags = ( *ppOutVtfTexture )->Flags()
  3507. | nFlags;
  3508. // Seek the reading back to the front of the buffer
  3509. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  3510. // Compute the actual texture dimensions
  3511. int nMipSkipCount = ComputeMipSkipCount( pName, dimsMappingCurrent, false, *ppOutVtfTexture, nFullFlags, nDesiredDimensionLimit, pOutStreamedMips, pInOutCachedFileLodSettings, pOptOutDimsActual, pOptOutDimsAllocated, pOptOutStripFlags );
  3512. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - ReadDataFromFile", __FUNCTION__ );
  3513. // Determine how much of the file to read in
  3514. int nFileSize = ( *ppOutVtfTexture )->FileSize( nMipSkipCount );
  3515. int nActualFileSize = (int)g_pFullFileSystem->Size( hFile );
  3516. if ( nActualFileSize < nFileSize )
  3517. {
  3518. if ( mat_spew_on_texture_size.GetInt() )
  3519. DevMsg( "Bad VTF data for %s, expected file size:%d actual file size:%d \n", pCacheFileName, nFileSize, nActualFileSize );
  3520. nFileSize = nActualFileSize;
  3521. }
  3522. // Read only the portion of the file that we care about
  3523. g_pFullFileSystem->Seek( hFile, 0, FILESYSTEM_SEEK_HEAD );
  3524. int nBytesOptimalRead = GetOptimalReadBuffer( &buf, hFile, nFileSize );
  3525. int nBytesRead = g_pFullFileSystem->ReadEx( buf.Base(), nBytesOptimalRead, nFileSize, hFile );
  3526. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  3527. // Some hardware doesn't support copying textures to other textures. For them, we need to reread the
  3528. // whole file, so if they are doing the final read (the fine levels) then reread everything by stripping
  3529. // off the flags we are trying to pass in.
  3530. unsigned int nForceFlags = nFullFlags & TEXTUREFLAGS_STREAMABLE;
  3531. if ( !HardwareConfig()->CanStretchRectFromTextures() && ( nForceFlags & TEXTUREFLAGS_STREAMABLE_FINE ) )
  3532. nForceFlags = 0;
  3533. // NOTE: Skipping mip levels here will cause the size to be changed
  3534. bool bRetVal = ( *ppOutVtfTexture )->UnserializeEx( buf, false, nForceFlags, nMipSkipCount );
  3535. FreeOptimalReadBuffer( 6*1024*1024 );
  3536. if ( !bRetVal )
  3537. {
  3538. Warning( "Error reading texture data \"%s\"\n", pCacheFileName );
  3539. }
  3540. return bRetVal;
  3541. }
  3542. //-----------------------------------------------------------------------------
  3543. // Compute the actual mip count based on the actual size
  3544. //-----------------------------------------------------------------------------
  3545. int ComputeActualMipCount( const TexDimensions_t& actualDims, unsigned int nFlags )
  3546. {
  3547. if ( nFlags & TEXTUREFLAGS_ENVMAP )
  3548. {
  3549. if ( !HardwareConfig()->SupportsMipmappedCubemaps() )
  3550. {
  3551. return 1;
  3552. }
  3553. }
  3554. if ( nFlags & TEXTUREFLAGS_NOMIP )
  3555. {
  3556. return 1;
  3557. }
  3558. // Unless ALLMIPS is set, we stop mips at 32x32
  3559. const int nMaxMipSize = 32;
  3560. // Clamp border textures on Posix to fix L4D2 flashlight cookie issue
  3561. #ifdef DX_TO_GL_ABSTRACTION
  3562. if ( ( false && !g_bForceTextureAllMips && !( nFlags & TEXTUREFLAGS_ALL_MIPS ) ) || ( true && ( nFlags & TEXTUREFLAGS_BORDER ) ) )
  3563. #else
  3564. if ( ( true && !g_bForceTextureAllMips && !( nFlags & TEXTUREFLAGS_ALL_MIPS ) ) || ( false && ( nFlags & TEXTUREFLAGS_BORDER ) ) )
  3565. #endif
  3566. {
  3567. int nNumMipLevels = 1;
  3568. int h = actualDims.m_nWidth;
  3569. int w = actualDims.m_nHeight;
  3570. while ( MIN( w, h ) > nMaxMipSize )
  3571. {
  3572. ++nNumMipLevels;
  3573. w >>= 1;
  3574. h >>= 1;
  3575. }
  3576. return nNumMipLevels;
  3577. }
  3578. return ImageLoader::GetNumMipMapLevels( actualDims.m_nWidth, actualDims.m_nHeight, actualDims.m_nDepth );
  3579. }
  3580. // ------------------------------------------------------------------------------------------------
  3581. 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 )
  3582. {
  3583. // NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
  3584. // NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
  3585. // NOTE! NOTE! NOTE! or by the streaming texture code!
  3586. Assert( pName != NULL );
  3587. Assert( pOutStreamedMips != NULL );
  3588. Assert( pInOutCachedFileLodSettings != NULL );
  3589. TexDimensions_t actualDims = mappingDims,
  3590. allocatedDims;
  3591. const bool bTextureMigration = ( nFlags & TEXTUREFLAGS_STREAMABLE ) != 0;
  3592. unsigned int stripFlags = 0;
  3593. int nClampX = actualDims.m_nWidth; // no clamping (clamp to texture dimensions)
  3594. int nClampY = actualDims.m_nHeight;
  3595. int nClampZ = actualDims.m_nDepth;
  3596. // Fetch LOD settings from the VTF if available
  3597. TextureLODControlSettings_t lcs;
  3598. memset( &lcs, 0, sizeof( lcs ) );
  3599. TextureLODControlSettings_t const *pLODInfo = NULL;
  3600. if ( pOptVTFTexture )
  3601. {
  3602. pLODInfo = reinterpret_cast<TextureLODControlSettings_t const *> (
  3603. pOptVTFTexture->GetResourceData( VTF_RSRC_TEXTURE_LOD_SETTINGS, NULL ) );
  3604. // Texture streaming means there are times we call this where we don't have a VTFTexture, even though
  3605. // we're a file. So we need to store off the LOD settings whenever we get in here with a file that has them
  3606. // so that we can use the correct values for when we don't. Otherwise, the texture will be confused about
  3607. // what size to use and everything will die a horrible, horrible death.
  3608. if ( pLODInfo )
  3609. ( *pInOutCachedFileLodSettings ) = ( *pLODInfo );
  3610. }
  3611. else if ( bTextureMigration )
  3612. {
  3613. pLODInfo = pInOutCachedFileLodSettings;
  3614. }
  3615. if ( pLODInfo )
  3616. lcs = *pLODInfo;
  3617. // Prepare the default LOD settings (that essentially result in no clamping)
  3618. TextureLODControlSettings_t default_lod_settings;
  3619. memset( &default_lod_settings, 0, sizeof( default_lod_settings ) );
  3620. {
  3621. for ( int w = actualDims.m_nWidth; w > 1; w >>= 1 )
  3622. ++ default_lod_settings.m_ResolutionClampX;
  3623. for ( int h = actualDims.m_nHeight; h > 1; h >>= 1 )
  3624. ++ default_lod_settings.m_ResolutionClampY;
  3625. }
  3626. // Check for LOD control override
  3627. {
  3628. TextureLodOverride::OverrideInfo oi = TextureLodOverride::Get( pName );
  3629. if ( oi.x && oi.y && !pLODInfo ) // If overriding texture that doesn't have lod info yet, then use default
  3630. lcs = default_lod_settings;
  3631. lcs.m_ResolutionClampX += oi.x;
  3632. lcs.m_ResolutionClampY += oi.y;
  3633. if ( int8( lcs.m_ResolutionClampX ) < 0 )
  3634. lcs.m_ResolutionClampX = 0;
  3635. if ( int8( lcs.m_ResolutionClampY ) < 0 )
  3636. lcs.m_ResolutionClampY = 0;
  3637. }
  3638. // Compute the requested mip0 dimensions
  3639. if ( lcs.m_ResolutionClampX && lcs.m_ResolutionClampY )
  3640. {
  3641. nClampX = (1 << lcs.m_ResolutionClampX );
  3642. nClampY = (1 << lcs.m_ResolutionClampY );
  3643. }
  3644. // In case clamp values exceed texture dimensions, then fix up
  3645. // the clamping values
  3646. nClampX = min( nClampX, (int)actualDims.m_nWidth );
  3647. nClampY = min( nClampY, (int)actualDims.m_nHeight );
  3648. //
  3649. // Honor dimension limit restrictions
  3650. //
  3651. if ( nDesiredDimensionLimit > 0 )
  3652. {
  3653. while ( nClampX > nDesiredDimensionLimit ||
  3654. nClampY > nDesiredDimensionLimit )
  3655. {
  3656. nClampX >>= 1;
  3657. nClampY >>= 1;
  3658. }
  3659. }
  3660. //
  3661. // Unless ignoring picmip, reflect the global picmip level in clamp dimensions
  3662. //
  3663. if ( !bIgnorePicmip )
  3664. {
  3665. // If picmip requests texture degradation, then honor it
  3666. // for loddable textures only
  3667. if ( !( nFlags & TEXTUREFLAGS_NOLOD ) &&
  3668. ( g_config.skipMipLevels > 0 ) )
  3669. {
  3670. for ( int iDegrade = 0; iDegrade < g_config.skipMipLevels; ++ iDegrade )
  3671. {
  3672. // don't go lower than 4, or dxt textures won't work properly
  3673. if ( nClampX > 4 &&
  3674. nClampY > 4 )
  3675. {
  3676. nClampX >>= 1;
  3677. nClampY >>= 1;
  3678. }
  3679. }
  3680. }
  3681. // If picmip requests quality upgrade, then always honor it
  3682. if ( g_config.skipMipLevels < 0 )
  3683. {
  3684. for ( int iUpgrade = 0; iUpgrade < - g_config.skipMipLevels; ++ iUpgrade )
  3685. {
  3686. if ( nClampX < actualDims.m_nWidth &&
  3687. nClampY < actualDims.m_nHeight )
  3688. {
  3689. nClampX <<= 1;
  3690. nClampY <<= 1;
  3691. }
  3692. else
  3693. break;
  3694. }
  3695. }
  3696. }
  3697. //
  3698. // Now use hardware settings to clamp our "clamping dimensions"
  3699. //
  3700. int iHwWidth = HardwareConfig()->MaxTextureWidth();
  3701. int iHwHeight = HardwareConfig()->MaxTextureHeight();
  3702. int iHwDepth = HardwareConfig()->MaxTextureDepth();
  3703. nClampX = min( nClampX, max( iHwWidth, 4 ) );
  3704. nClampY = min( nClampY, max( iHwHeight, 4 ) );
  3705. nClampZ = min( nClampZ, max( iHwDepth, 1 ) );
  3706. // In case clamp values exceed texture dimensions, then fix up
  3707. // the clamping values.
  3708. nClampX = min( nClampX, (int)actualDims.m_nWidth );
  3709. nClampY = min( nClampY, (int)actualDims.m_nHeight );
  3710. nClampZ = min( nClampZ, (int)actualDims.m_nDepth );
  3711. //
  3712. // Clamp to the determined dimensions
  3713. //
  3714. int numMipsSkipped = 0; // will compute now when clamping how many mips we drop
  3715. while ( ( actualDims.m_nWidth > nClampX ) ||
  3716. ( actualDims.m_nHeight > nClampY ) ||
  3717. ( actualDims.m_nDepth > nClampZ ) )
  3718. {
  3719. actualDims.m_nWidth >>= 1;
  3720. actualDims.m_nHeight >>= 1;
  3721. actualDims.m_nDepth = Max( 1, actualDims.m_nDepth >> 1 );
  3722. ++ numMipsSkipped;
  3723. }
  3724. Assert( actualDims.m_nWidth > 0 && actualDims.m_nHeight > 0 && actualDims.m_nDepth > 0 );
  3725. // Now that we've got the actual size, we can figure out the mip count
  3726. actualDims.m_nMipCount = ComputeActualMipCount( actualDims, nFlags );
  3727. // If we're streaming, cut down what we're loading.
  3728. // We can only stream things that have a mipmap pyramid (not just a single mipmap).
  3729. bool bHasSetAllocation = false;
  3730. if ( ( nFlags & TEXTUREFLAGS_STREAMABLE ) == TEXTUREFLAGS_STREAMABLE_COARSE )
  3731. {
  3732. if ( actualDims.m_nMipCount > 1 )
  3733. {
  3734. allocatedDims.m_nWidth = actualDims.m_nWidth;
  3735. allocatedDims.m_nHeight = actualDims.m_nHeight;
  3736. allocatedDims.m_nDepth = actualDims.m_nDepth;
  3737. allocatedDims.m_nMipCount = actualDims.m_nMipCount;
  3738. for ( int i = 0; i < STREAMING_START_MIPMAP; ++i )
  3739. {
  3740. // Stop when width or height is at 4 pixels (or less). We could do better,
  3741. // but some textures really can't function if they're less than 4 pixels (compressed textures, for example).
  3742. if ( allocatedDims.m_nWidth <= 4 || allocatedDims.m_nHeight <= 4 )
  3743. break;
  3744. allocatedDims.m_nWidth >>= 1;
  3745. allocatedDims.m_nHeight >>= 1;
  3746. allocatedDims.m_nDepth = Max( 1, allocatedDims.m_nDepth >> 1 );
  3747. allocatedDims.m_nMipCount = Max( 1, allocatedDims.m_nMipCount - 1 );
  3748. ++numMipsSkipped;
  3749. ++( *pOutStreamedMips );
  3750. }
  3751. bHasSetAllocation = true;
  3752. }
  3753. else
  3754. {
  3755. // Clear out that we're streaming, this isn't a texture we can stream.
  3756. stripFlags |= TEXTUREFLAGS_STREAMABLE_COARSE;
  3757. }
  3758. }
  3759. if ( !bHasSetAllocation )
  3760. {
  3761. allocatedDims.m_nWidth = actualDims.m_nWidth;
  3762. allocatedDims.m_nHeight = actualDims.m_nHeight;
  3763. allocatedDims.m_nDepth = actualDims.m_nDepth;
  3764. allocatedDims.m_nMipCount = actualDims.m_nMipCount;
  3765. }
  3766. if ( pOptOutActualDims )
  3767. *pOptOutActualDims = actualDims;
  3768. if ( pOptOutAllocatedDims )
  3769. *pOptOutAllocatedDims = allocatedDims;
  3770. if ( pOptOutStripFlags )
  3771. ( *pOptOutStripFlags ) = stripFlags;
  3772. // Returns the number we skipped
  3773. return numMipsSkipped;
  3774. }
  3775. //-----------------------------------------------------------------------------
  3776. // Get an optimal read buffer, persists and avoids excessive allocations
  3777. //-----------------------------------------------------------------------------
  3778. int GetOptimalReadBuffer( CUtlBuffer* pOutOptimalBuffer, FileHandle_t hFile, int nSize )
  3779. {
  3780. // NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
  3781. // NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
  3782. // NOTE! NOTE! NOTE! or by the streaming texture code!
  3783. Assert( GetThreadId() < MAX_RENDER_THREADS );
  3784. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s (%d bytes)", __FUNCTION__, nSize );
  3785. Assert( pOutOptimalBuffer != NULL );
  3786. // get an optimal read buffer, only resize if necessary
  3787. const int minSize = 2 * 1024 * 1024; // Uses 2MB min to avoid fragmentation
  3788. nSize = max( nSize, minSize );
  3789. int nBytesOptimalRead = g_pFullFileSystem->GetOptimalReadSize( hFile, nSize );
  3790. const int ti = GetThreadId();
  3791. if ( nBytesOptimalRead > s_nOptimalReadBufferSize[ ti ] )
  3792. {
  3793. FreeOptimalReadBuffer( 0 );
  3794. s_nOptimalReadBufferSize[ ti ] = nBytesOptimalRead;
  3795. s_pOptimalReadBuffer[ ti ] = g_pFullFileSystem->AllocOptimalReadBuffer( hFile, nSize );
  3796. if ( mat_spewalloc.GetBool() )
  3797. {
  3798. Msg( "Allocated optimal read buffer of %d bytes @ 0x%p for thread %d\n", s_nOptimalReadBufferSize[ ti ], s_pOptimalReadBuffer[ ti ], ti );
  3799. }
  3800. }
  3801. // set external buffer and reset to empty
  3802. ( *pOutOptimalBuffer ).SetExternalBuffer( s_pOptimalReadBuffer[ ti ], s_nOptimalReadBufferSize[ ti ], 0 );
  3803. // return the optimal read size
  3804. return nBytesOptimalRead;
  3805. }
  3806. //-----------------------------------------------------------------------------
  3807. // Free the optimal read buffer if it grows too large
  3808. //-----------------------------------------------------------------------------
  3809. void FreeOptimalReadBuffer( int nMaxSize )
  3810. {
  3811. // NOTE! NOTE! NOTE! If you are making changes to this function, be aware that it has threading
  3812. // NOTE! NOTE! NOTE! implications. It can be called synchronously by the Main thread,
  3813. // NOTE! NOTE! NOTE! or by the streaming texture code!
  3814. Assert( GetThreadId() < MAX_RENDER_THREADS );
  3815. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  3816. const int ti = GetThreadId();
  3817. if ( s_pOptimalReadBuffer[ ti ] && s_nOptimalReadBufferSize[ ti ] >= nMaxSize )
  3818. {
  3819. if ( mat_spewalloc.GetBool() )
  3820. {
  3821. Msg( "Freeing optimal read buffer of %d bytes @ 0x%p for thread %d\n", s_nOptimalReadBufferSize[ ti ], s_pOptimalReadBuffer[ ti ], ti );
  3822. }
  3823. g_pFullFileSystem->FreeOptimalReadBuffer( s_pOptimalReadBuffer[ ti ] );
  3824. s_pOptimalReadBuffer[ ti ] = NULL;
  3825. s_nOptimalReadBufferSize[ ti ] = 0;
  3826. }
  3827. }
  3828. #if defined( STAGING_ONLY )
  3829. CON_COMMAND( dumptexallocs, "List currently allocated textures and properties about them" )
  3830. {
  3831. Msg( "Texture Memory Statistics follow:\n" );
  3832. uint64 totalTexMemAllocated = 0;
  3833. FOR_EACH_MAP_FAST( g_currentTextures, i )
  3834. {
  3835. const TexInfo_t& tex = g_currentTextures[ i ];
  3836. uint64 thisTexMem = tex.ComputeTexSize();
  3837. totalTexMemAllocated += thisTexMem;
  3838. Msg( "%s: %llu bytes\n", ( const char * ) tex.m_Name, thisTexMem );
  3839. }
  3840. Msg( "Total Memory Allocated: %llu bytes\n", totalTexMemAllocated );
  3841. }
  3842. #endif
  3843. //////////////////////////////////////////////////////////////////////////
  3844. //
  3845. // Saving all the texture LOD modifications to content
  3846. //
  3847. //////////////////////////////////////////////////////////////////////////
  3848. #ifdef IS_WINDOWS_PC
  3849. static bool SetBufferValue( char *chTxtFileBuffer, char const *szLookupKey, char const *szNewValue )
  3850. {
  3851. bool bResult = false;
  3852. size_t lenTmp = strlen( szNewValue );
  3853. size_t nTxtFileBufferLen = strlen( chTxtFileBuffer );
  3854. for ( char *pch = chTxtFileBuffer;
  3855. ( NULL != ( pch = strstr( pch, szLookupKey ) ) );
  3856. ++ pch )
  3857. {
  3858. char *val = pch + strlen( szLookupKey );
  3859. if ( !V_isspace( *val ) )
  3860. continue;
  3861. else
  3862. ++ val;
  3863. char *pValStart = val;
  3864. // Okay, here comes the value
  3865. while ( *val && V_isspace( *val ) )
  3866. ++ val;
  3867. while ( *val && !V_isspace( *val ) )
  3868. ++ val;
  3869. char *pValEnd = val; // Okay, here ends the value
  3870. memmove( pValStart + lenTmp, pValEnd, chTxtFileBuffer + nTxtFileBufferLen + 1 - pValEnd );
  3871. memcpy( pValStart, szNewValue, lenTmp );
  3872. nTxtFileBufferLen += ( lenTmp - ( pValEnd - pValStart ) );
  3873. bResult = true;
  3874. }
  3875. if ( !bResult )
  3876. {
  3877. char *pchAdd = chTxtFileBuffer + nTxtFileBufferLen;
  3878. strcpy( pchAdd + strlen( pchAdd ), "\n" );
  3879. strcpy( pchAdd + strlen( pchAdd ), szLookupKey );
  3880. strcpy( pchAdd + strlen( pchAdd ), " " );
  3881. strcpy( pchAdd + strlen( pchAdd ), szNewValue );
  3882. strcpy( pchAdd + strlen( pchAdd ), "\n" );
  3883. bResult = true;
  3884. }
  3885. return bResult;
  3886. }
  3887. // Replaces the first occurrence of "szFindData" with "szNewData"
  3888. // Returns the remaining buffer past the replaced data or NULL if
  3889. // no replacement occurred.
  3890. static char * BufferReplace( char *buf, char const *szFindData, char const *szNewData )
  3891. {
  3892. size_t len = strlen( buf ), lFind = strlen( szFindData ), lNew = strlen( szNewData );
  3893. if ( char *pBegin = strstr( buf, szFindData ) )
  3894. {
  3895. memmove( pBegin + lNew, pBegin + lFind, buf + len - ( pBegin + lFind ) );
  3896. memmove( pBegin, szNewData, lNew );
  3897. return pBegin + lNew;
  3898. }
  3899. return NULL;
  3900. }
  3901. class CP4Requirement
  3902. {
  3903. public:
  3904. CP4Requirement();
  3905. ~CP4Requirement();
  3906. protected:
  3907. bool m_bLoadedModule;
  3908. CSysModule *m_pP4Module;
  3909. };
  3910. CP4Requirement::CP4Requirement() :
  3911. m_bLoadedModule( false ),
  3912. m_pP4Module( NULL )
  3913. {
  3914. #ifdef STAGING_ONLY
  3915. if ( p4 )
  3916. return;
  3917. // load the p4 lib
  3918. m_pP4Module = Sys_LoadModule( "p4lib" );
  3919. m_bLoadedModule = true;
  3920. if ( m_pP4Module )
  3921. {
  3922. CreateInterfaceFn factory = Sys_GetFactory( m_pP4Module );
  3923. if ( factory )
  3924. {
  3925. p4 = ( IP4 * )factory( P4_INTERFACE_VERSION, NULL );
  3926. if ( p4 )
  3927. {
  3928. extern CreateInterfaceFn g_fnMatSystemConnectCreateInterface;
  3929. p4->Connect( g_fnMatSystemConnectCreateInterface );
  3930. p4->Init();
  3931. }
  3932. }
  3933. }
  3934. #endif // STAGING_ONLY
  3935. if ( !p4 )
  3936. {
  3937. Warning( "Can't load p4lib.dll\n" );
  3938. }
  3939. }
  3940. CP4Requirement::~CP4Requirement()
  3941. {
  3942. if ( m_bLoadedModule && m_pP4Module )
  3943. {
  3944. if ( p4 )
  3945. {
  3946. p4->Shutdown();
  3947. p4->Disconnect();
  3948. }
  3949. Sys_UnloadModule( m_pP4Module );
  3950. m_pP4Module = NULL;
  3951. p4 = NULL;
  3952. }
  3953. }
  3954. 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." );
  3955. 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 )
  3956. {
  3957. using namespace TextureLodOverride;
  3958. if ( args.ArgC() != 2 )
  3959. goto usage;
  3960. char const *szCmd = args.Arg( 1 );
  3961. Msg( "mat_texture_list_txlod_sync %s...\n", szCmd );
  3962. if ( !stricmp( szCmd, "reset" ) )
  3963. {
  3964. for ( int k = 0; k < s_OverrideMap.GetNumStrings(); ++ k )
  3965. {
  3966. char const *szTx = s_OverrideMap.String( k );
  3967. s_OverrideMap[ k ] = OverrideInfo(); // Reset the override info
  3968. // Force the texture LOD override to get re-processed
  3969. if ( ITexture *pTx = materials->FindTexture( szTx, "" ) )
  3970. pTx->ForceLODOverride( 0 );
  3971. else
  3972. Warning( " mat_texture_list_txlod_sync reset - texture '%s' no longer found.\n", szTx );
  3973. }
  3974. s_OverrideMap.Purge();
  3975. Msg("mat_texture_list_txlod_sync reset : completed.\n");
  3976. return;
  3977. }
  3978. else if ( !stricmp( szCmd, "save" ) )
  3979. {
  3980. CP4Requirement p4req;
  3981. if ( !p4 )
  3982. g_p4factory->SetDummyMode( true );
  3983. for ( int k = 0; k < s_OverrideMap.GetNumStrings(); ++ k )
  3984. {
  3985. char const *szTx = s_OverrideMap.String( k );
  3986. OverrideInfo oi = s_OverrideMap[ k ];
  3987. ITexture *pTx = materials->FindTexture( szTx, "" );
  3988. if ( !oi.x || !oi.y )
  3989. continue;
  3990. if ( !pTx )
  3991. {
  3992. Warning( " mat_texture_list_txlod_sync save - texture '%s' no longer found.\n", szTx );
  3993. continue;
  3994. }
  3995. int iMaxWidth = pTx->GetActualWidth(), iMaxHeight = pTx->GetActualHeight();
  3996. // Save maxwidth and maxheight
  3997. char chMaxWidth[20], chMaxHeight[20];
  3998. sprintf( chMaxWidth, "%d", iMaxWidth ), sprintf( chMaxHeight, "%d", iMaxHeight );
  3999. // We have the texture and path to its content
  4000. char chResolveName[ MAX_PATH ] = {0}, chResolveNameArg[ MAX_PATH ] = {0};
  4001. Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s" TEXTURE_FNAME_EXTENSION, szTx );
  4002. char *szTextureContentPath;
  4003. if ( !mat_texture_list_content_path.GetString()[0] )
  4004. {
  4005. szTextureContentPath = const_cast< char * >( g_pFullFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 ) );
  4006. if ( !szTextureContentPath )
  4007. {
  4008. Warning( " mat_texture_list_txlod_sync save - texture '%s' is not loaded from file system.\n", szTx );
  4009. continue;
  4010. }
  4011. if ( !BufferReplace( szTextureContentPath, "\\game\\", "\\content\\" ) ||
  4012. !BufferReplace( szTextureContentPath, "\\materials\\", "\\materialsrc\\" ) )
  4013. {
  4014. Warning( " mat_texture_list_txlod_sync save - texture '%s' cannot be mapped to content directory.\n", szTx );
  4015. continue;
  4016. }
  4017. }
  4018. else
  4019. {
  4020. V_strncpy( chResolveName, mat_texture_list_content_path.GetString(), MAX_PATH );
  4021. V_strncat( chResolveName, "/", MAX_PATH );
  4022. V_strncat( chResolveName, szTx, MAX_PATH );
  4023. V_strncat( chResolveName, TEXTURE_FNAME_EXTENSION, MAX_PATH );
  4024. szTextureContentPath = chResolveName;
  4025. }
  4026. // Figure out what kind of source content is there:
  4027. // 1. look for TGA - if found, get the txt file (if txt file missing, create one)
  4028. // 2. otherwise look for PSD - affecting psdinfo
  4029. // 3. else error
  4030. char *pExtPut = szTextureContentPath + strlen( szTextureContentPath ) - strlen( TEXTURE_FNAME_EXTENSION ); // compensating the TEXTURE_FNAME_EXTENSION(.vtf) extension
  4031. // 1.tga
  4032. sprintf( pExtPut, ".tga" );
  4033. if ( g_pFullFileSystem->FileExists( szTextureContentPath ) )
  4034. {
  4035. // Have tga - pump in the txt file
  4036. sprintf( pExtPut, ".txt" );
  4037. CUtlBuffer bufTxtFileBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  4038. g_pFullFileSystem->ReadFile( szTextureContentPath, 0, bufTxtFileBuffer );
  4039. for ( int kCh = 0; kCh < 1024; ++kCh ) bufTxtFileBuffer.PutChar( 0 );
  4040. // Now fix maxwidth/maxheight settings
  4041. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxwidth", chMaxWidth );
  4042. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxheight", chMaxHeight );
  4043. bufTxtFileBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, strlen( ( char * ) bufTxtFileBuffer.Base() ) );
  4044. // Check out or add the file
  4045. g_p4factory->SetOpenFileChangeList( "Texture LOD Autocheckout" );
  4046. CP4AutoEditFile autop4_edit( szTextureContentPath );
  4047. // Save the file contents
  4048. if ( g_pFullFileSystem->WriteFile( szTextureContentPath, 0, bufTxtFileBuffer ) )
  4049. {
  4050. Msg(" '%s' : saved.\n", szTextureContentPath );
  4051. CP4AutoAddFile autop4_add( szTextureContentPath );
  4052. }
  4053. else
  4054. {
  4055. Warning( " '%s' : failed to save - set \"maxwidth %d maxheight %d\" manually.\n",
  4056. szTextureContentPath, iMaxWidth, iMaxHeight );
  4057. }
  4058. continue;
  4059. }
  4060. // 2.psd
  4061. sprintf( pExtPut, ".psd" );
  4062. if ( g_pFullFileSystem->FileExists( szTextureContentPath ) )
  4063. {
  4064. char chCommand[MAX_PATH];
  4065. char szTxtFileName[MAX_PATH] = {0};
  4066. GetModSubdirectory( "tmp_lod_psdinfo.txt", szTxtFileName, sizeof( szTxtFileName ) );
  4067. sprintf( chCommand, "/C psdinfo \"%s\" > \"%s\"", szTextureContentPath, szTxtFileName);
  4068. ShellExecute( NULL, NULL, "cmd.exe", chCommand, NULL, SW_HIDE );
  4069. Sleep( 200 );
  4070. CUtlBuffer bufTxtFileBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  4071. g_pFullFileSystem->ReadFile( szTxtFileName, 0, bufTxtFileBuffer );
  4072. for ( int kCh = 0; kCh < 1024; ++ kCh ) bufTxtFileBuffer.PutChar( 0 );
  4073. // Now fix maxwidth/maxheight settings
  4074. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxwidth", chMaxWidth );
  4075. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxheight", chMaxHeight );
  4076. bufTxtFileBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, strlen( ( char * ) bufTxtFileBuffer.Base() ) );
  4077. // Check out or add the file
  4078. // Save the file contents
  4079. if ( g_pFullFileSystem->WriteFile( szTxtFileName, 0, bufTxtFileBuffer ) )
  4080. {
  4081. g_p4factory->SetOpenFileChangeList( "Texture LOD Autocheckout" );
  4082. CP4AutoEditFile autop4_edit( szTextureContentPath );
  4083. sprintf( chCommand, "/C psdinfo -write \"%s\" < \"%s\"", szTextureContentPath, szTxtFileName );
  4084. Sleep( 200 );
  4085. ShellExecute( NULL, NULL, "cmd.exe", chCommand, NULL, SW_HIDE );
  4086. Sleep( 200 );
  4087. Msg(" '%s' : saved.\n", szTextureContentPath );
  4088. CP4AutoAddFile autop4_add( szTextureContentPath );
  4089. }
  4090. else
  4091. {
  4092. Warning( " '%s' : failed to save - set \"maxwidth %d maxheight %d\" manually.\n",
  4093. szTextureContentPath, iMaxWidth, iMaxHeight );
  4094. }
  4095. continue;
  4096. }
  4097. // 3. - error
  4098. sprintf( pExtPut, "" );
  4099. {
  4100. Warning( " '%s' : doesn't specify a valid TGA or PSD file!\n", szTextureContentPath );
  4101. continue;
  4102. }
  4103. }
  4104. Msg("mat_texture_list_txlod_sync save : completed.\n");
  4105. return;
  4106. }
  4107. else
  4108. goto usage;
  4109. return;
  4110. usage:
  4111. Warning(
  4112. "Usage:\n"
  4113. " mat_texture_list_txlod_sync reset - resets all run-time changes to LOD overrides;\n"
  4114. " mat_texture_list_txlod_sync save - saves all changes to material content files.\n"
  4115. );
  4116. }
  4117. #endif