Counter Strike : Global Offensive Source Code
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.

5905 lines
177 KiB

  1. //========= Copyright (c) Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #ifndef _X360
  7. #ifdef PROTECTED_THINGS_ENABLE
  8. #undef PROTECTED_THINGS_ENABLE
  9. #endif
  10. #endif
  11. #include "platform.h"
  12. // HACK: Need ShellExecute for PSD updates
  13. #ifdef IS_WINDOWS_PC
  14. #include <windows.h>
  15. #include <shellapi.h>
  16. #pragma comment ( lib, "shell32" )
  17. #endif
  18. #include "shaderapi/ishaderapi.h"
  19. #include "materialsystem_global.h"
  20. #include "itextureinternal.h"
  21. #include "utlsymbol.h"
  22. #include "time.h"
  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. #include "bitmap/imageformat.h"
  26. #include "bitmap/tgaloader.h"
  27. #include "bitmap/tgawriter.h"
  28. #ifdef _WIN32
  29. #include "direct.h"
  30. #endif
  31. #include "colorspace.h"
  32. #include "string.h"
  33. #ifndef _PS3
  34. #include <malloc.h>
  35. #endif
  36. #include <stdlib.h>
  37. #include "utlmemory.h"
  38. #include "IHardwareConfigInternal.h"
  39. #include "filesystem.h"
  40. #include "tier1/strtools.h"
  41. #include "vtf/vtf.h"
  42. #include "materialsystem/materialsystem_config.h"
  43. #include "mempool.h"
  44. #include "texturemanager.h"
  45. #include "utlbuffer.h"
  46. #include "pixelwriter.h"
  47. #include "tier1/callqueue.h"
  48. #include "tier1/UtlStringMap.h"
  49. #include "filesystem/IQueuedLoader.h"
  50. #include "tier2/fileutils.h"
  51. #include "filesystem.h"
  52. #include "tier2/p4helpers.h"
  53. #include "tier2/tier2.h"
  54. #include "p4lib/ip4.h"
  55. #include "ctype.h"
  56. #include "ifilelist.h"
  57. #include "tier0/icommandline.h"
  58. #include "datacache/imdlcache.h"
  59. #include "tier0/vprof.h"
  60. #ifndef _PS3
  61. #define MATSYS_INTERNAL
  62. #endif
  63. #include "cmaterialsystem.h"
  64. // NOTE: This must be the last file included!!!
  65. #include "tier0/memdbgon.h"
  66. // this allows the command line to force the "all mips" flag to on for all textures
  67. bool g_bForceTextureAllMips = false;
  68. #define TEXTURE_FNAME_EXTENSION PLATFORM_EXT ".vtf"
  69. #define TEXTURE_FNAME_EXTENSION_NORMAL "_normal" PLATFORM_EXT ".vtf"
  70. #define TEXTURE_FNAME_EXTENSION_LEN ( sizeof( TEXTURE_FNAME_EXTENSION ) - 1 )
  71. ConVar mat_spewalloc( "mat_spewalloc", "0", FCVAR_ARCHIVE );
  72. ConVar mat_exclude_async_update( "mat_exclude_async_update", "1" );
  73. extern CMaterialSystem g_MaterialSystem;
  74. //-----------------------------------------------------------------------------
  75. // Internal texture flags
  76. //-----------------------------------------------------------------------------
  77. enum InternalTextureFlags
  78. {
  79. TEXTUREFLAGSINTERNAL_ERROR = 0x00000001,
  80. TEXTUREFLAGSINTERNAL_ALLOCATED = 0x00000002,
  81. TEXTUREFLAGSINTERNAL_PRELOADED = 0x00000004, // CONSOLE: textures that went through the preload process
  82. TEXTUREFLAGSINTERNAL_QUEUEDLOAD = 0x00000008, // CONSOLE: load using the queued loader
  83. TEXTUREFLAGSINTERNAL_EXCLUDED = 0x00000020, // actual exclusion state
  84. TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE = 0x00000040, // desired exclusion state
  85. TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET = 0x00000080, // 360: should only allocate texture bits upon first resolve, destroy at level end
  86. TEXTUREFLAGSINTERNAL_CACHEABLE = 0x00000100, // 360: candidate for cacheing
  87. TEXTUREFLAGSINTERNAL_REDUCED = 0x00000200, // CONSOLE: true dimensions forced smaller (i.e. exclusion)
  88. TEXTUREFLAGSINTERNAL_TEMPEXCLUDED = 0x00000400, // actual temporary exclusion state
  89. TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE = 0x00000800, // desired temporary exclusion state
  90. TEXTUREFLAGSINTERNAL_TEMPEXCLUDE_UPDATE = 0x00001000, // private state bit used by temp exclusions
  91. TEXTUREFLAGSINTERNAL_FORCED_TO_EXCLUDE = 0x00002000, // private state bit used to track/undo an artifical exclusion
  92. TEXTUREFLAGSINTERNAL_ASYNC_DONE = 0x00004000, // async download for texture is done
  93. };
  94. //-----------------------------------------------------------------------------
  95. // Use Warning to show texture flags.
  96. //-----------------------------------------------------------------------------
  97. static void PrintFlags( unsigned int flags )
  98. {
  99. if ( flags & TEXTUREFLAGS_NOMIP )
  100. {
  101. Warning( "TEXTUREFLAGS_NOMIP|" );
  102. }
  103. if ( flags & TEXTUREFLAGS_NOLOD )
  104. {
  105. Warning( "TEXTUREFLAGS_NOLOD|" );
  106. }
  107. if ( flags & TEXTUREFLAGS_POINTSAMPLE )
  108. {
  109. Warning( "TEXTUREFLAGS_POINTSAMPLE|" );
  110. }
  111. if ( flags & TEXTUREFLAGS_TRILINEAR )
  112. {
  113. Warning( "TEXTUREFLAGS_TRILINEAR|" );
  114. }
  115. if ( flags & TEXTUREFLAGS_CLAMPS )
  116. {
  117. Warning( "TEXTUREFLAGS_CLAMPS|" );
  118. }
  119. if ( flags & TEXTUREFLAGS_CLAMPT )
  120. {
  121. Warning( "TEXTUREFLAGS_CLAMPT|" );
  122. }
  123. if ( flags & TEXTUREFLAGS_HINT_DXT5 )
  124. {
  125. Warning( "TEXTUREFLAGS_HINT_DXT5|" );
  126. }
  127. if ( flags & TEXTUREFLAGS_ANISOTROPIC )
  128. {
  129. Warning( "TEXTUREFLAGS_ANISOTROPIC|" );
  130. }
  131. if ( flags & TEXTUREFLAGS_PROCEDURAL )
  132. {
  133. Warning( "TEXTUREFLAGS_PROCEDURAL|" );
  134. }
  135. if ( flags & TEXTUREFLAGS_ALL_MIPS )
  136. {
  137. Warning( "TEXTUREFLAGS_ALL_MIPS|" );
  138. }
  139. if ( flags & TEXTUREFLAGS_MOST_MIPS )
  140. {
  141. Warning( "TEXTUREFLAGS_MOST_MIPS|" );
  142. }
  143. if ( flags & TEXTUREFLAGS_SINGLECOPY )
  144. {
  145. Warning( "TEXTUREFLAGS_SINGLECOPY|" );
  146. }
  147. }
  148. namespace TextureLodOverride
  149. {
  150. struct OverrideInfo
  151. {
  152. OverrideInfo() : x( 0 ), y( 0 ) {}
  153. OverrideInfo( int8 x_, int8 y_ ) : x( x_ ), y( y_ ) {}
  154. int8 x, y;
  155. };
  156. // Override map
  157. typedef CUtlStringMap< OverrideInfo > OverrideMap_t;
  158. OverrideMap_t s_OverrideMap;
  159. // Retrieves the override info adjustments
  160. OverrideInfo Get( char const *szName )
  161. {
  162. UtlSymId_t idx = s_OverrideMap.Find( szName );
  163. if ( idx != s_OverrideMap.InvalidIndex() )
  164. return s_OverrideMap[ idx ];
  165. else
  166. return OverrideInfo();
  167. }
  168. // Combines the existing override info adjustments with the given one
  169. void Add( char const *szName, OverrideInfo oi )
  170. {
  171. OverrideInfo oiex = Get( szName );
  172. oiex.x += oi.x;
  173. oiex.y += oi.y;
  174. s_OverrideMap[ szName ] = oiex;
  175. }
  176. }; // end namespace TextureLodOverride
  177. namespace TextureLodExclude
  178. {
  179. typedef CUtlStringMap< int > ExcludeMap_t;
  180. ExcludeMap_t s_ExcludeMap;
  181. int Get( char const *szName )
  182. {
  183. UtlSymId_t idx = s_ExcludeMap.Find( szName );
  184. if ( idx != s_ExcludeMap.InvalidIndex() )
  185. {
  186. return s_ExcludeMap[ idx ];
  187. }
  188. else
  189. {
  190. return -1;
  191. }
  192. }
  193. void Add( char const *szName, int iOverride )
  194. {
  195. UtlSymId_t idx = s_ExcludeMap.Find( szName );
  196. if ( idx != s_ExcludeMap.InvalidIndex() )
  197. {
  198. int &x = s_ExcludeMap[ idx ];
  199. x = iOverride;
  200. }
  201. else
  202. {
  203. s_ExcludeMap[ szName ] = iOverride;
  204. }
  205. }
  206. }; // end namespace TextureLodExclude
  207. //-----------------------------------------------------------------------------
  208. // Base texture class
  209. //-----------------------------------------------------------------------------
  210. class CTexture : public ITextureInternal
  211. {
  212. public:
  213. CTexture();
  214. virtual ~CTexture();
  215. virtual const char *GetName( void ) const;
  216. const char *GetTextureGroupName( void ) const;
  217. // Stats about the texture itself
  218. virtual ImageFormat GetImageFormat() const;
  219. virtual int GetMappingWidth() const;
  220. virtual int GetMappingHeight() const;
  221. virtual int GetActualWidth() const;
  222. virtual int GetActualHeight() const;
  223. virtual int GetNumAnimationFrames() const;
  224. virtual bool IsTranslucent() const;
  225. virtual void GetReflectivity( Vector& reflectivity );
  226. // Reference counting
  227. virtual void IncrementReferenceCount( );
  228. virtual void DecrementReferenceCount( );
  229. virtual int GetReferenceCount() const;
  230. // Used to modify the texture bits (procedural textures only)
  231. virtual void SetTextureRegenerator( ITextureRegenerator *pTextureRegen, bool releaseExisting = true );
  232. // Little helper polling methods
  233. virtual bool IsNormalMap( ) const;
  234. virtual bool IsCubeMap( void ) const;
  235. virtual bool IsRenderTarget( ) const;
  236. virtual bool IsTempRenderTarget( void ) const;
  237. virtual bool IsProcedural() const;
  238. virtual bool IsMipmapped() const;
  239. virtual bool IsError() const;
  240. virtual bool IsDefaultPool() const;
  241. // For volume textures
  242. virtual bool IsVolumeTexture() const;
  243. virtual int GetMappingDepth() const;
  244. virtual int GetActualDepth() const;
  245. // Various ways of initializing the texture
  246. void InitFileTexture( const char *pTextureFile, const char *pTextureGroupName );
  247. void InitProceduralTexture( const char *pTextureName, const char *pTextureGroupName, int w, int h, int d, ImageFormat fmt, int nFlags );
  248. // Releases the texture's hw memory
  249. void Release();
  250. virtual void OnRestore();
  251. // Sets the filtering modes on the texture we're modifying
  252. void SetFilteringAndClampingMode();
  253. void Download( Rect_t *pRect = NULL, int nAdditionalCreationFlags = 0 );
  254. // Loads up information about the texture
  255. virtual void Precache();
  256. // FIXME: Bogus methods... can we please delete these?
  257. virtual void GetLowResColorSample( float s, float t, float *color ) const;
  258. // Gets texture resource data of the specified type.
  259. // Params:
  260. // eDataType type of resource to retrieve.
  261. // pnumBytes on return is the number of bytes available in the read-only data buffer or is undefined
  262. // Returns:
  263. // pointer to the resource data, or NULL. Note that the data from this pointer can disappear when
  264. // the texture goes away - you want to copy this data!
  265. virtual void *GetResourceData( uint32 eDataType, size_t *pNumBytes ) const;
  266. virtual int GetApproximateVidMemBytes( void ) const;
  267. // Stretch blit the framebuffer into this texture.
  268. virtual void CopyFrameBufferToMe( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL );
  269. virtual void CopyMeToFrameBuffer( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL );
  270. virtual ITexture *GetEmbeddedTexture( int nIndex );
  271. // Get the shaderapi texture handle associated w/ a particular frame
  272. virtual ShaderAPITextureHandle_t GetTextureHandle( int nFrame, int nChannel = 0 );
  273. // Sets the texture as the render target
  274. virtual void Bind( Sampler_t sampler, TextureBindFlags_t nBindFlags );
  275. virtual void Bind( Sampler_t sampler1, TextureBindFlags_t nBindFlags, int nFrame, Sampler_t sampler2 = SHADER_SAMPLER_INVALID );
  276. virtual void BindVertexTexture( VertexTextureSampler_t stage, int nFrame );
  277. // Set this texture as a render target
  278. bool SetRenderTarget( int nRenderTargetID );
  279. // Set this texture as a render target (optionally set depth texture as depth buffer as well)
  280. bool SetRenderTarget( int nRenderTargetID, ITexture *pDepthTexture);
  281. virtual void MarkAsPreloaded( bool bSet );
  282. virtual bool IsPreloaded() const;
  283. virtual void MarkAsExcluded( bool bSet, int nDimensionsLimit, bool bMarkAsTrumpedExclude );
  284. virtual bool UpdateExcludedState();
  285. // Retrieve the vtf flags mask
  286. virtual unsigned int GetFlags( void ) const;
  287. virtual void ForceLODOverride( int iNumLodsOverrideUpOrDown );
  288. virtual void ForceExcludeOverride( int iExcludeOverride );
  289. //this isn't a MultiRenderTarget texture, do nothing
  290. virtual void AddDownsizedSubTarget( const char *pName, int iDownsizePow2, MaterialRenderTargetDepth_t depth ) {}
  291. virtual void SetActiveSubTarget( const char *pName ) {}
  292. void GetFilename( char *pOut, int maxLen ) const;
  293. virtual void ReloadFilesInList( IFileList *pFilesToReload );
  294. #ifdef _PS3
  295. virtual void Ps3gcmRawBufferAlias( char const *pRTName );
  296. #endif
  297. virtual bool MarkAsTempExcluded( bool bSet, int nExcludedDimensionLimit );
  298. virtual bool IsTempExcluded() const;
  299. virtual bool CanBeTempExcluded() const;
  300. virtual bool FinishAsyncDownload( AsyncTextureContext_t *pContext, void *pData, int nNumReadBytes, bool bAbort, float flMaxTimeMs );
  301. virtual bool IsForceExcluded() const;
  302. virtual bool ClearForceExclusion();
  303. virtual bool IsAsyncDone() const;
  304. protected:
  305. bool IsDepthTextureFormat( ImageFormat fmt );
  306. void ReconstructTexture( void *pSourceData = NULL, int nSourceDataSize = 0 );
  307. void ReconstructPartialTexture( const Rect_t *pRect );
  308. bool HasBeenAllocated() const;
  309. void WriteDataToShaderAPITexture( int nFrameCount, int nFaceCount, int nFirstFace, int nMipCount, IVTFTexture *pVTFTexture, ImageFormat fmt );
  310. // Initializes/shuts down the texture
  311. void Init( int w, int h, int d, ImageFormat fmt, int iFlags, int iFrameCount );
  312. void Shutdown();
  313. // Sets the texture name
  314. void SetName( const char* pName );
  315. // Assigns/releases texture IDs for our animation frames
  316. void AllocateTextureHandles();
  317. void ReleaseTextureHandles();
  318. // Calculates info about whether we can make the texture smaller and by how much
  319. // Returns the number of skipped mip levels
  320. int ComputeActualSize( bool bIgnorePicmip = false, IVTFTexture *pVTFTexture = NULL );
  321. // Computes the actual format of the texture given a desired src format
  322. ImageFormat ComputeActualFormat( ImageFormat srcFormat );
  323. // Compute the actual mip count based on the actual size
  324. int ComputeActualMipCount( ) const;
  325. // Creates/releases the shader api texture
  326. virtual bool AllocateShaderAPITextures();
  327. virtual void FreeShaderAPITextures();
  328. // Download bits
  329. void DownloadTexture( Rect_t *pRect, void *pSourceData = NULL, int nSourceDataSize = 0 );
  330. bool DownloadAsyncTexture( AsyncTextureContext_t *pContext, void *pSourceData, int nSourceDataSize, float flMaxTimeMs );
  331. void ReconstructTextureBits( Rect_t *pRect );
  332. // Gets us modifying a particular frame of our texture
  333. void Modify( int iFrame );
  334. // Sets the texture clamping state on the currently modified frame
  335. void SetWrapState( );
  336. // Sets the texture filtering state on the currently modified frame
  337. void SetFilterState();
  338. // Loads the texture bits from a file. Optionally provides absolute path
  339. IVTFTexture *LoadTexttureBitsFromFileOrData( void *pSourceData, int nSourceDataSize, char **pResolvedFilename );
  340. IVTFTexture *LoadTextureBitsFromFile( char *pCacheFileName, char **pResolvedFilename );
  341. IVTFTexture *LoadTextureBitsFromData( char *pCacheFileName, void *pSourceData, int nSourceDataSize );
  342. IVTFTexture *HandleFileLoadFailedTexture( IVTFTexture *pVTFTexture );
  343. // Generates the procedural bits
  344. IVTFTexture *ReconstructProceduralBits( );
  345. IVTFTexture *ReconstructPartialProceduralBits( const Rect_t *pRect, Rect_t *pActualRect );
  346. // Sets up debugging texture bits, if appropriate
  347. bool SetupDebuggingTextures( IVTFTexture *pTexture );
  348. // Generate a texture that shows the various mip levels
  349. void GenerateShowMipLevelsTextures( IVTFTexture *pTexture );
  350. // Generate a RGBA 128 128 128 255 gray texture
  351. void GenerateGrayTexture( IVTFTexture *pTexture );
  352. void Cleanup( void );
  353. // Converts a source image read from disk into its actual format
  354. bool ConvertToActualFormat( IVTFTexture *pTexture );
  355. // Builds the low-res image from the texture
  356. void LoadLowResTexture( IVTFTexture *pTexture );
  357. void CopyLowResImageToTexture( IVTFTexture *pTexture );
  358. void GetDownloadFaceCount( int &nFirstFace, int &nFaceCount );
  359. void ComputeMipLevelSubRect( const Rect_t* pSrcRect, int nMipLevel, Rect_t *pSubRect );
  360. IVTFTexture *GetScratchVTFTexture();
  361. IVTFTexture *GetScratchVTFAsyncTexture();
  362. int GetOptimalReadBuffer( FileHandle_t hFile, int nFileSize, CUtlBuffer &optimalBuffer );
  363. void FreeOptimalReadBuffer( int nMaxSize );
  364. void ApplyRenderTargetSizeMode( int &width, int &height, ImageFormat fmt );
  365. virtual bool IsMultiRenderTarget( void ) { return false; }
  366. void LoadResourceData( IVTFTexture *pTexture );
  367. void FreeResourceData();
  368. protected:
  369. #ifdef _DEBUG
  370. char *m_pDebugName;
  371. #endif
  372. // Reflectivity vector
  373. Vector m_vecReflectivity;
  374. CUtlSymbol m_Name;
  375. CUtlSymbol m_ExcludedResolvedFileName;
  376. CUtlSymbol m_ResolvedFileName;
  377. FSAsyncControl_t m_hAsyncControl;
  378. // What texture group this texture is in (winds up setting counters based on the group name,
  379. // then the budget panel views the counters).
  380. CUtlSymbol m_TextureGroupName;
  381. unsigned int m_nFlags;
  382. unsigned int m_nInternalFlags;
  383. CInterlockedInt m_nRefCount;
  384. // This is the *desired* image format, which may or may not represent reality
  385. ImageFormat m_ImageFormat;
  386. // mappingWidth/Height and actualWidth/Height only differ
  387. // if g_config.skipMipLevels != 0, or if the card has a hard limit
  388. // on the maximum texture size
  389. // This is the iWidth/iHeight for the data that m_pImageData points to.
  390. unsigned short m_nMappingWidth;
  391. unsigned short m_nMappingHeight;
  392. unsigned short m_nMappingDepth;
  393. // This is the iWidth/iHeight for whatever is downloaded to the card.
  394. unsigned short m_nActualWidth; // needed for procedural
  395. unsigned short m_nActualHeight; // needed for procedural
  396. unsigned short m_nActualDepth;
  397. unsigned short m_nActualMipCount; // Mip count when it's actually used
  398. unsigned short m_nFrameCount;
  399. unsigned short m_nOriginalRTWidth; // The values they initially specified. We generated a different width
  400. unsigned short m_nOriginalRTHeight; // and height based on screen size and the flags they specify.
  401. unsigned char m_LowResImageWidth;
  402. unsigned char m_LowResImageHeight;
  403. short m_nDesiredDimensionLimit; // part of texture exclusion
  404. short m_nDesiredTempDimensionLimit;
  405. short m_nActualDimensionLimit; // value not necessarily accurate, but mismatch denotes dirty state
  406. unsigned short m_nMipSkipCount;
  407. // The set of texture ids for each animation frame
  408. ShaderAPITextureHandle_t *m_pTextureHandles;
  409. // a temporary copy of the texture handles used to support the temporary exclude feature
  410. ShaderAPITextureHandle_t *m_pTempTextureHandles;
  411. // lowresimage info - used for getting color data from a texture
  412. // without having a huge system mem overhead.
  413. // FIXME: We should keep this in compressed form. .is currently decompressed at load time.
  414. #if !defined( _GAMECONSOLE )
  415. unsigned char *m_pLowResImage;
  416. #else
  417. unsigned char m_LowResImageSample[4];
  418. #endif
  419. ITextureRegenerator *m_pTextureRegenerator;
  420. // Used to help decide whether or not to recreate the render target if AA changes.
  421. RenderTargetType_t m_nOriginalRenderTargetType;
  422. RenderTargetSizeMode_t m_RenderTargetSizeMode;
  423. // Fixed-size allocator
  424. // DECLARE_FIXEDSIZE_ALLOCATOR( CTexture );
  425. public:
  426. void InitRenderTarget( const char *pRTName, int w, int h, RenderTargetSizeMode_t sizeMode,
  427. ImageFormat fmt, RenderTargetType_t type, unsigned int textureFlags,
  428. unsigned int renderTargetFlags );
  429. virtual void DeleteIfUnreferenced();
  430. #if defined( _GAMECONSOLE )
  431. virtual bool ClearTexture( int r, int g, int b, int a );
  432. #endif
  433. #if defined( _X360 )
  434. virtual bool CreateRenderTargetSurface( int width, int height, ImageFormat format, bool bSameAsTexture, RTMultiSampleCount360_t multiSampleCount = RT_MULTISAMPLE_NONE );
  435. #endif
  436. void FixupTexture( const void *pData, int nSize, LoaderError_t loaderError );
  437. void SwapContents( ITexture *pOther );
  438. protected:
  439. // private data, generally from VTF resource extensions
  440. struct DataChunk
  441. {
  442. void Allocate( unsigned int numBytes )
  443. {
  444. m_pvData = new unsigned char[ numBytes ];
  445. m_numBytes = numBytes;
  446. }
  447. void Deallocate() const { delete [] m_pvData; }
  448. unsigned int m_eType;
  449. unsigned int m_numBytes;
  450. unsigned char *m_pvData;
  451. };
  452. CUtlVector< DataChunk > m_arrDataChunks;
  453. private:
  454. bool ScheduleExcludeAsyncDownload();
  455. bool ScheduleAsyncDownload();
  456. };
  457. //a render target that is actually multiple textures that can be swapped out depending on the situation. Mostly to support recursive water in portal
  458. class CTexture_MultipleRenderTarget : public CTexture
  459. {
  460. public:
  461. typedef CTexture BaseClass;
  462. CTexture_MultipleRenderTarget() :
  463. m_nActiveTarget( -1 ),
  464. m_nQueuedActiveTarget( -1 )
  465. {
  466. }
  467. virtual void Bind( Sampler_t sampler, TextureBindFlags_t nBindFlags )
  468. {
  469. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  470. Assert( m_nActiveTarget < m_Targets.Count() );
  471. if( m_nActiveTarget < 0 )
  472. return BaseClass::Bind( sampler, nBindFlags );
  473. if( m_nActiveTarget < m_Targets.Count() )
  474. {
  475. g_pShaderAPI->BindTexture( sampler, nBindFlags, m_Targets[m_nActiveTarget].handle );
  476. }
  477. else
  478. {
  479. g_pShaderAPI->BindTexture( sampler, nBindFlags, INVALID_SHADERAPI_TEXTURE_HANDLE );
  480. }
  481. }
  482. virtual void Bind( Sampler_t sampler1, TextureBindFlags_t nBindFlags, int nFrame, Sampler_t sampler2 = SHADER_SAMPLER_INVALID )
  483. {
  484. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  485. Assert( m_nActiveTarget < m_Targets.Count() );
  486. if( m_nActiveTarget < 0 )
  487. return BaseClass::Bind( sampler1, nBindFlags, nFrame, sampler2 );
  488. if ( g_pShaderDevice->IsUsingGraphics() )
  489. {
  490. if( m_nActiveTarget < m_Targets.Count() )
  491. {
  492. g_pShaderAPI->BindTexture( sampler1, nBindFlags, m_Targets[m_nActiveTarget].handle );
  493. }
  494. else
  495. {
  496. g_pShaderAPI->BindTexture( sampler1, nBindFlags, INVALID_SHADERAPI_TEXTURE_HANDLE );
  497. }
  498. }
  499. }
  500. virtual ShaderAPITextureHandle_t GetTextureHandle( int nFrame, int nTextureChannel =0 )
  501. {
  502. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  503. Assert( m_nActiveTarget < m_Targets.Count() );
  504. if( m_nActiveTarget < 0 )
  505. return BaseClass::GetTextureHandle( nFrame, nTextureChannel );
  506. if( m_nActiveTarget < m_Targets.Count() )
  507. {
  508. return m_Targets[m_nActiveTarget].handle;
  509. }
  510. else
  511. {
  512. return INVALID_SHADERAPI_TEXTURE_HANDLE;
  513. }
  514. }
  515. virtual void AddDownsizedSubTarget( const char *szName, int iDownsizePow2, MaterialRenderTargetDepth_t depth )
  516. {
  517. // normalize and convert to a symbol
  518. char szCleanName[MAX_PATH];
  519. SubTarget_t temp;
  520. temp.name = NormalizeTextureName( szName, szCleanName, sizeof( szCleanName ) );
  521. temp.iDownSizePow2 = iDownsizePow2;
  522. temp.handle = INVALID_SHADERAPI_TEXTURE_HANDLE;
  523. temp.depthHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
  524. temp.bHasSeparateDepth = (depth == MATERIAL_RT_DEPTH_SEPARATE) || (depth == MATERIAL_RT_DEPTH_ONLY);
  525. if( IsX360() )
  526. {
  527. if( HasBeenAllocated() )
  528. {
  529. //need to initialize these handles now
  530. extern int GetCreationFlags( int iTextureFlags, int iInternalTextureFlags, ImageFormat fmt ); //defined about 1000 lines down where it makes more logical sense to be
  531. int nCreateFlags = GetCreationFlags( m_nFlags, m_nInternalFlags, m_ImageFormat );
  532. // For depth only render target: adjust texture width/height
  533. // Currently we just leave it the same size, will update with further testing
  534. int nShaderApiCreateTextureDepth = ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) && ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ) ) ? 1 : m_nActualDepth;
  535. // Create all animated texture frames in a single call
  536. g_pShaderAPI->CreateTextures(
  537. &temp.handle, 1,
  538. m_nActualWidth / iDownsizePow2, m_nActualHeight / iDownsizePow2, nShaderApiCreateTextureDepth, m_ImageFormat, m_nActualMipCount,
  539. 1, nCreateFlags, GetName(), GetTextureGroupName() );
  540. // Create the depth render target buffer
  541. if ( temp.bHasSeparateDepth )
  542. {
  543. MEM_ALLOC_CREDIT();
  544. char debugName[128];
  545. sprintf( debugName, "%s_ZBuffer", GetName() );
  546. temp.depthHandle = g_pShaderAPI->CreateDepthTexture(
  547. m_ImageFormat,
  548. m_nActualWidth / iDownsizePow2,
  549. m_nActualHeight / iDownsizePow2,
  550. debugName,
  551. ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ) );
  552. }
  553. #if defined( PLATFORM_X360 )
  554. //if ( !( renderTargetFlags & CREATERENDERTARGETFLAGS_NOEDRAM ) )
  555. {
  556. // RT surface is expected at end of array
  557. temp.surfaceHandle = g_pShaderAPI->CreateRenderTargetSurface( m_nActualWidth / iDownsizePow2, m_nActualHeight / iDownsizePow2, m_ImageFormat, RT_MULTISAMPLE_NONE, GetName(), TEXTURE_GROUP_RENDER_TARGET_SURFACE );
  558. }
  559. #endif
  560. }
  561. }
  562. else
  563. {
  564. Assert( HasBeenAllocated() );
  565. }
  566. m_Targets.AddToTail( temp );
  567. }
  568. virtual void SetActiveSubTarget( const char *szName )
  569. {
  570. ICallQueue *pCallQueue = materials->GetRenderContext()->GetCallQueue();
  571. if ( pCallQueue )
  572. {
  573. m_nQueuedActiveTarget = -1;
  574. if( szName == NULL )
  575. {
  576. return;
  577. }
  578. char szCleanName[MAX_PATH];
  579. NormalizeTextureName( szName, szCleanName, sizeof( szCleanName ) );
  580. for( int i = 0; i != m_Targets.Count(); ++i )
  581. {
  582. if( m_Targets[i].name == szCleanName )
  583. {
  584. m_nQueuedActiveTarget = i;
  585. break;
  586. }
  587. }
  588. pCallQueue->QueueCall( this, &CTexture_MultipleRenderTarget::SetActiveSubTarget, szName );
  589. return;
  590. }
  591. else
  592. {
  593. m_nActiveTarget = -1;
  594. if( szName == NULL )
  595. {
  596. return;
  597. }
  598. char szCleanName[MAX_PATH];
  599. NormalizeTextureName( szName, szCleanName, sizeof( szCleanName ) );
  600. for( int i = 0; i != m_Targets.Count(); ++i )
  601. {
  602. if( m_Targets[i].name == szCleanName )
  603. {
  604. m_nActiveTarget = i;
  605. break;
  606. }
  607. }
  608. }
  609. }
  610. virtual int GetActualWidth() const
  611. {
  612. int iDownSize = 1;
  613. ICallQueue *pCallQueue = materials->GetRenderContext()->GetCallQueue();
  614. if ( pCallQueue )
  615. {
  616. Assert( m_nQueuedActiveTarget < m_Targets.Count() );
  617. if( (m_nQueuedActiveTarget >= 0) && (m_nQueuedActiveTarget < m_Targets.Count()) )
  618. {
  619. iDownSize = m_Targets[m_nQueuedActiveTarget].iDownSizePow2;
  620. }
  621. }
  622. else
  623. {
  624. Assert( m_nActiveTarget < m_Targets.Count() );
  625. if( (m_nActiveTarget >= 0) && (m_nActiveTarget < m_Targets.Count()) )
  626. {
  627. iDownSize = m_Targets[m_nActiveTarget].iDownSizePow2;
  628. }
  629. }
  630. return BaseClass::GetActualWidth() / iDownSize;
  631. }
  632. virtual int GetActualHeight() const
  633. {
  634. int iDownSize = 1;
  635. ICallQueue *pCallQueue = materials->GetRenderContext()->GetCallQueue();
  636. if ( pCallQueue )
  637. {
  638. Assert( m_nQueuedActiveTarget < m_Targets.Count() );
  639. if( (m_nQueuedActiveTarget >= 0) && (m_nQueuedActiveTarget < m_Targets.Count()) )
  640. {
  641. iDownSize = m_Targets[m_nQueuedActiveTarget].iDownSizePow2;
  642. }
  643. }
  644. else
  645. {
  646. Assert( m_nActiveTarget < m_Targets.Count() );
  647. if( (m_nActiveTarget >= 0) && (m_nActiveTarget < m_Targets.Count()) )
  648. {
  649. iDownSize = m_Targets[m_nActiveTarget].iDownSizePow2;
  650. }
  651. }
  652. return BaseClass::GetActualHeight() / iDownSize;
  653. }
  654. virtual bool AllocateShaderAPITextures()
  655. {
  656. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  657. Assert( !HasBeenAllocated() );
  658. if ( !BaseClass::AllocateShaderAPITextures() )
  659. return false;
  660. if( m_Targets.Count() == 0 )
  661. return true;
  662. extern int GetCreationFlags( int iTextureFlags, int iInternalTextureFlags, ImageFormat fmt ); //defined about 1000 lines down where it makes more logical sense to be
  663. int nCreateFlags = GetCreationFlags( m_nFlags, m_nInternalFlags, m_ImageFormat );
  664. // For depth only render target: adjust texture width/height
  665. // Currently we just leave it the same size, will update with further testing
  666. int nShaderApiCreateTextureDepth = ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) && ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ) ) ? 1 : m_nActualDepth;
  667. for( int i = 0; i != m_Targets.Count(); ++i )
  668. {
  669. // Create all animated texture frames in a single call
  670. g_pShaderAPI->CreateTextures(
  671. &m_Targets[i].handle, 1,
  672. m_nActualWidth / m_Targets[i].iDownSizePow2, m_nActualHeight / m_Targets[i].iDownSizePow2, nShaderApiCreateTextureDepth, m_ImageFormat, m_nActualMipCount,
  673. 1, nCreateFlags, GetName(), GetTextureGroupName() );
  674. // Create the depth render target buffer
  675. if ( m_Targets[i].bHasSeparateDepth )
  676. {
  677. MEM_ALLOC_CREDIT();
  678. char debugName[128];
  679. sprintf( debugName, "%s_ZBuffer", GetName() );
  680. m_Targets[i].depthHandle = g_pShaderAPI->CreateDepthTexture(
  681. m_ImageFormat,
  682. m_nActualWidth / m_Targets[i].iDownSizePow2,
  683. m_nActualHeight / m_Targets[i].iDownSizePow2,
  684. debugName,
  685. ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ) );
  686. }
  687. #if defined( PLATFORM_X360 )
  688. //if ( !( renderTargetFlags & CREATERENDERTARGETFLAGS_NOEDRAM ) )
  689. {
  690. // RT surface is expected at end of array
  691. m_Targets[i].surfaceHandle = g_pShaderAPI->CreateRenderTargetSurface( m_nActualWidth / m_Targets[i].iDownSizePow2, m_nActualHeight / m_Targets[i].iDownSizePow2, m_ImageFormat, RT_MULTISAMPLE_NONE, GetName(), TEXTURE_GROUP_RENDER_TARGET_SURFACE );
  692. }
  693. #endif
  694. }
  695. return true;
  696. }
  697. virtual void FreeShaderAPITextures()
  698. {
  699. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  700. for( int i = 0; i != m_Targets.Count(); ++i )
  701. {
  702. if ( g_pShaderAPI->IsTexture( m_Targets[i].handle ) )
  703. {
  704. g_pShaderAPI->DeleteTexture( m_Targets[i].handle );
  705. m_Targets[i].handle = INVALID_SHADERAPI_TEXTURE_HANDLE;
  706. }
  707. if ( g_pShaderAPI->IsTexture( m_Targets[i].depthHandle ) )
  708. {
  709. g_pShaderAPI->DeleteTexture( m_Targets[i].depthHandle );
  710. m_Targets[i].depthHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
  711. }
  712. #if defined( PLATFORM_X360 )
  713. if ( g_pShaderAPI->IsTexture( m_Targets[i].surfaceHandle ) )
  714. {
  715. g_pShaderAPI->DeleteTexture( m_Targets[i].surfaceHandle );
  716. m_Targets[i].surfaceHandle = INVALID_SHADERAPI_TEXTURE_HANDLE;
  717. }
  718. #endif
  719. }
  720. BaseClass::FreeShaderAPITextures();
  721. }
  722. //-----------------------------------------------------------------------------
  723. // Set this texture as a render target
  724. //-----------------------------------------------------------------------------
  725. virtual bool SetRenderTarget( int nRenderTargetID )
  726. {
  727. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  728. return SetRenderTarget( nRenderTargetID, NULL );
  729. }
  730. //-----------------------------------------------------------------------------
  731. // Set this texture as a render target
  732. // Optionally bind pDepthTexture as depth buffer
  733. //-----------------------------------------------------------------------------
  734. bool SetRenderTarget( int nRenderTargetID, ITexture *pDepthTexture )
  735. {
  736. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  737. Assert( m_nActiveTarget < m_Targets.Count() );
  738. if( (m_nActiveTarget < 0) || (m_nActiveTarget >= m_Targets.Count()) )
  739. return BaseClass::SetRenderTarget( nRenderTargetID, pDepthTexture );
  740. if ( ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) == 0 )
  741. return false;
  742. // Make sure we've actually allocated the texture handles
  743. Assert( HasBeenAllocated() );
  744. ShaderAPITextureHandle_t textureHandle;
  745. #if !defined( PLATFORM_X360 )
  746. {
  747. textureHandle = m_Targets[m_nActiveTarget].handle;
  748. }
  749. #else
  750. {
  751. textureHandle = m_Targets[m_nActiveTarget].surfaceHandle;
  752. }
  753. #endif
  754. ShaderAPITextureHandle_t depthTextureHandle = (unsigned int)SHADER_RENDERTARGET_DEPTHBUFFER;
  755. if ( m_Targets[m_nActiveTarget].bHasSeparateDepth )
  756. {
  757. depthTextureHandle = m_Targets[m_nActiveTarget].depthHandle;
  758. }
  759. else if ( m_nFlags & TEXTUREFLAGS_NODEPTHBUFFER )
  760. {
  761. // GR - render target without depth buffer
  762. depthTextureHandle = (unsigned int)SHADER_RENDERTARGET_NONE;
  763. }
  764. if ( pDepthTexture)
  765. {
  766. depthTextureHandle = static_cast<ITextureInternal *>(pDepthTexture)->GetTextureHandle(0);
  767. }
  768. g_pShaderAPI->SetRenderTargetEx( nRenderTargetID, textureHandle, depthTextureHandle );
  769. return true;
  770. }
  771. // Stretch blit the framebuffer into this texture.
  772. virtual void CopyFrameBufferToMe( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL )
  773. {
  774. Assert( materials->GetRenderContext()->GetCallQueue() == NULL );
  775. Assert( m_nActiveTarget < m_Targets.Count() );
  776. if( (m_nActiveTarget < 0) || (m_nActiveTarget >= m_Targets.Count()) )
  777. return BaseClass::CopyFrameBufferToMe( nRenderTargetID, pSrcRect, pDstRect );
  778. Assert( m_pTextureHandles && m_nFrameCount >= 1 );
  779. if ( IsX360() &&
  780. ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET ) &&
  781. !HasBeenAllocated() )
  782. {
  783. //need to create the texture bits now
  784. //to avoid creating the texture bits previously, we simply skipped this step
  785. if ( !AllocateShaderAPITextures() )
  786. return;
  787. }
  788. if ( m_pTextureHandles && m_nFrameCount >= 1 )
  789. {
  790. g_pShaderAPI->CopyRenderTargetToTextureEx( m_Targets[m_nActiveTarget].handle, nRenderTargetID, pSrcRect, pDstRect );
  791. }
  792. }
  793. virtual bool IsMultiRenderTarget( void ) { return true; }
  794. struct SubTarget_t
  795. {
  796. CUtlSymbol name;
  797. int iDownSizePow2;
  798. ShaderAPITextureHandle_t handle;
  799. ShaderAPITextureHandle_t depthHandle;
  800. #if defined( PLATFORM_X360 )
  801. ShaderAPITextureHandle_t surfaceHandle;
  802. #endif
  803. bool bHasSeparateDepth;
  804. };
  805. CUtlVector< SubTarget_t > m_Targets;
  806. int m_nActiveTarget;
  807. int m_nQueuedActiveTarget;
  808. };
  809. //////////////////////////////////////////////////////////////////////////
  810. //
  811. // CReferenceToHandleTexture is a special implementation of ITexture
  812. // to be used solely for binding the texture handle when rendering.
  813. // It is used when a D3D texture handle is available, but should be used
  814. // at a higher level of abstraction requiring an ITexture or ITextureInternal.
  815. //
  816. //////////////////////////////////////////////////////////////////////////
  817. class CReferenceToHandleTexture : public ITextureInternal
  818. {
  819. public:
  820. CReferenceToHandleTexture();
  821. virtual ~CReferenceToHandleTexture();
  822. virtual const char *GetName( void ) const { return m_Name.String(); }
  823. const char *GetTextureGroupName( void ) const { return m_TextureGroupName.String(); }
  824. // Stats about the texture itself
  825. virtual ImageFormat GetImageFormat() const { return IMAGE_FORMAT_UNKNOWN; }
  826. virtual int GetMappingWidth() const { return 1; }
  827. virtual int GetMappingHeight() const { return 1; }
  828. virtual int GetActualWidth() const { return m_nActualWidth; }
  829. virtual int GetActualHeight() const { return m_nActualHeight; }
  830. virtual int GetNumAnimationFrames() const { return 1; }
  831. virtual bool IsTranslucent() const { return false; }
  832. virtual void GetReflectivity( Vector& reflectivity ) { reflectivity.Zero(); }
  833. // Reference counting
  834. virtual void IncrementReferenceCount( ) { ++ m_nRefCount; }
  835. virtual void DecrementReferenceCount( ) { -- m_nRefCount; }
  836. virtual int GetReferenceCount( ) const { return m_nRefCount; }
  837. // Used to modify the texture bits (procedural textures only)
  838. virtual void SetTextureRegenerator( ITextureRegenerator *pTextureRegen, bool releaseExisting = true ) { NULL; }
  839. // Little helper polling methods
  840. virtual bool IsNormalMap( ) const { return false; }
  841. virtual bool IsCubeMap( void ) const { return false; }
  842. virtual bool IsRenderTarget( ) const { return false; }
  843. virtual bool IsTempRenderTarget( void ) const { return false; }
  844. virtual bool IsProcedural() const { return true; }
  845. virtual bool IsMipmapped() const { return false; }
  846. virtual bool IsError() const { return false; }
  847. virtual bool IsDefaultPool() const { return false; }
  848. // For volume textures
  849. virtual bool IsVolumeTexture() const { return false; }
  850. virtual int GetMappingDepth() const { return 1; }
  851. virtual int GetActualDepth() const { return 1; }
  852. // Releases the texture's hw memory
  853. void Release() { NULL; }
  854. virtual void OnRestore() { NULL; }
  855. // Sets the filtering modes on the texture we're modifying
  856. void SetFilteringAndClampingMode() { NULL; }
  857. void Download( Rect_t *pRect = NULL, int nAdditionalCreationFlags = 0 ) { NULL; }
  858. // Loads up information about the texture
  859. virtual void Precache() { NULL; }
  860. // FIXME: Bogus methods... can we please delete these?
  861. virtual void GetLowResColorSample( float s, float t, float *color ) const { NULL; }
  862. // Gets texture resource data of the specified type.
  863. // Params:
  864. // eDataType type of resource to retrieve.
  865. // pnumBytes on return is the number of bytes available in the read-only data buffer or is undefined
  866. // Returns:
  867. // pointer to the resource data, or NULL. Note that the data from this pointer can disappear when
  868. // the texture goes away - you want to copy this data!
  869. virtual void *GetResourceData( uint32 eDataType, size_t *pNumBytes ) const { return NULL; }
  870. virtual int GetApproximateVidMemBytes( void ) const { return 32; }
  871. // Stretch blit the framebuffer into this texture.
  872. virtual void CopyFrameBufferToMe( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ) { NULL; }
  873. virtual void CopyMeToFrameBuffer( int nRenderTargetID = 0, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ) { NULL; }
  874. virtual ITexture *GetEmbeddedTexture( int nIndex ) { return ( nIndex == 0 ) ? this : NULL; }
  875. // Get the shaderapi texture handle associated w/ a particular frame
  876. virtual ShaderAPITextureHandle_t GetTextureHandle( int nFrame, int nTextureChannel = 0 ) { return m_hTexture; }
  877. // Bind the texture
  878. virtual void Bind( Sampler_t sampler, TextureBindFlags_t nBindFlags );
  879. virtual void Bind( Sampler_t sampler1, TextureBindFlags_t nBindFlags, int nFrame, Sampler_t sampler2 = SHADER_SAMPLER_INVALID );
  880. virtual void BindVertexTexture( VertexTextureSampler_t stage, int nFrame );
  881. // Set this texture as a render target
  882. bool SetRenderTarget( int nRenderTargetID ) { return SetRenderTarget( nRenderTargetID, NULL ); }
  883. // Set this texture as a render target (optionally set depth texture as depth buffer as well)
  884. bool SetRenderTarget( int nRenderTargetID, ITexture *pDepthTexture) { return false; }
  885. virtual void MarkAsPreloaded( bool bSet ) { NULL; }
  886. virtual bool IsPreloaded() const { return true; }
  887. virtual void MarkAsExcluded( bool bSet, int nDimensionsLimit, bool bMarkAsTrumpedExclude ) { NULL; }
  888. virtual bool UpdateExcludedState() { return true; }
  889. // Retrieve the vtf flags mask
  890. virtual unsigned int GetFlags( void ) const { return 0; }
  891. virtual void ForceLODOverride( int iNumLodsOverrideUpOrDown ) { NULL; }
  892. virtual void ForceExcludeOverride( int iExcludeOverride ) { NULL; };
  893. virtual void ReloadFilesInList( IFileList *pFilesToReload ) {}
  894. #ifdef _PS3
  895. virtual void Ps3gcmRawBufferAlias( char const *pRTName ) {}
  896. #endif
  897. virtual void AddDownsizedSubTarget( const char *szName, int iDownsizePow2, MaterialRenderTargetDepth_t depth ) { NULL; }
  898. virtual void SetActiveSubTarget( const char *szName ) { NULL; }
  899. virtual bool IsMultiRenderTarget( void ) { return false; }
  900. virtual bool MarkAsTempExcluded( bool bSet, int nExcludedDimensionLimit ) { return false; }
  901. virtual bool IsTempExcluded() const { return false; }
  902. virtual bool CanBeTempExcluded() const { return false; }
  903. virtual bool FinishAsyncDownload( AsyncTextureContext_t *pContext, void *pData, int nNumReadBytes, bool bAbort, float flMaxTimeMs ) { return true; }
  904. virtual bool IsForceExcluded() const { return false; }
  905. virtual bool ClearForceExclusion() { return false; }
  906. virtual bool IsAsyncDone() const { return true; }
  907. protected:
  908. #ifdef _DEBUG
  909. char *m_pDebugName;
  910. #endif
  911. CUtlSymbol m_Name;
  912. // What texture group this texture is in (winds up setting counters based on the group name,
  913. // then the budget panel views the counters).
  914. CUtlSymbol m_TextureGroupName;
  915. // The set of texture ids for each animation frame
  916. ShaderAPITextureHandle_t m_hTexture;
  917. // Refcount
  918. int m_nRefCount;
  919. int m_nActualWidth;
  920. int m_nActualHeight;
  921. int m_nActualDepth;
  922. public:
  923. virtual void DeleteIfUnreferenced();
  924. #if defined( _GAMECONSOLE )
  925. virtual bool ClearTexture( int r, int g, int b, int a ) { return false; }
  926. #endif
  927. #if defined( _X360 )
  928. virtual bool CreateRenderTargetSurface( int width, int height, ImageFormat format, bool bSameAsTexture, RTMultiSampleCount360_t multiSampleCount = RT_MULTISAMPLE_NONE ) { return false; }
  929. #endif
  930. void FixupTexture( const void *pData, int nSize, LoaderError_t loaderError ) { NULL; }
  931. void SwapContents( ITexture *pOther ) { NULL; }
  932. public:
  933. void SetName( char const *szName );
  934. void InitFromHandle(
  935. const char *pTextureName,
  936. const char *pTextureGroupName,
  937. ShaderAPITextureHandle_t hTexture );
  938. };
  939. CReferenceToHandleTexture::CReferenceToHandleTexture() :
  940. m_hTexture( INVALID_SHADERAPI_TEXTURE_HANDLE ),
  941. #ifdef _DEBUG
  942. m_pDebugName( NULL ),
  943. #endif
  944. m_nRefCount( 0 ),
  945. m_nActualWidth( 0 ),
  946. m_nActualHeight( 0 ),
  947. m_nActualDepth( 1 )
  948. {
  949. NULL;
  950. }
  951. CReferenceToHandleTexture::~CReferenceToHandleTexture()
  952. {
  953. #ifdef _DEBUG
  954. if ( m_nRefCount != 0 )
  955. {
  956. Warning( "Reference Count(%d) != 0 in ~CReferenceToHandleTexture for texture \"%s\"\n", m_nRefCount, m_Name.String() );
  957. }
  958. if ( m_pDebugName )
  959. {
  960. delete [] m_pDebugName;
  961. }
  962. #endif
  963. }
  964. void CReferenceToHandleTexture::SetName( char const *szName )
  965. {
  966. // normalize and convert to a symbol
  967. char szCleanName[MAX_PATH];
  968. m_Name = NormalizeTextureName( szName, szCleanName, sizeof( szCleanName ) );
  969. #ifdef _DEBUG
  970. if ( m_pDebugName )
  971. {
  972. delete [] m_pDebugName;
  973. }
  974. int nLen = V_strlen( szCleanName ) + 1;
  975. m_pDebugName = new char[nLen];
  976. V_memcpy( m_pDebugName, szCleanName, nLen );
  977. #endif
  978. }
  979. void CReferenceToHandleTexture::InitFromHandle( const char *pTextureName, const char *pTextureGroupName, ShaderAPITextureHandle_t hTexture )
  980. {
  981. SetName( pTextureName );
  982. m_TextureGroupName = pTextureGroupName;
  983. m_hTexture = hTexture;
  984. g_pShaderAPI->GetTextureDimensions( hTexture, m_nActualWidth, m_nActualHeight, m_nActualDepth );
  985. }
  986. void CReferenceToHandleTexture::Bind( Sampler_t sampler, TextureBindFlags_t nBindFlags )
  987. {
  988. if ( g_pShaderDevice->IsUsingGraphics() )
  989. {
  990. g_pShaderAPI->BindTexture( sampler, nBindFlags, m_hTexture );
  991. }
  992. }
  993. void CReferenceToHandleTexture::Bind( Sampler_t sampler1, TextureBindFlags_t nBindFlags, int nFrame, Sampler_t sampler2 /* = -1 */ )
  994. {
  995. if ( g_pShaderDevice->IsUsingGraphics() )
  996. {
  997. g_pShaderAPI->BindTexture( sampler1, nBindFlags, m_hTexture );
  998. }
  999. }
  1000. void CReferenceToHandleTexture::BindVertexTexture( VertexTextureSampler_t sampler, int nFrame )
  1001. {
  1002. if ( g_pShaderDevice->IsUsingGraphics() )
  1003. {
  1004. g_pShaderAPI->BindVertexTexture( sampler, m_hTexture );
  1005. }
  1006. }
  1007. void CReferenceToHandleTexture::DeleteIfUnreferenced()
  1008. {
  1009. if ( m_nRefCount > 0 )
  1010. return;
  1011. TextureManager()->RemoveTexture( this );
  1012. }
  1013. //-----------------------------------------------------------------------------
  1014. // Fixed-size allocator
  1015. //-----------------------------------------------------------------------------
  1016. //DEFINE_FIXEDSIZE_ALLOCATOR( CTexture, 1024, true );
  1017. //-----------------------------------------------------------------------------
  1018. // Static instance of VTF texture
  1019. //-----------------------------------------------------------------------------
  1020. static IVTFTexture *s_pVTFTexture = NULL;
  1021. static IVTFTexture *s_pVTFAsyncTexture = NULL;
  1022. static void *s_pOptimalReadBuffer = NULL;
  1023. static int s_nOptimalReadBufferSize = 0;
  1024. //-----------------------------------------------------------------------------
  1025. // Class factory methods
  1026. //-----------------------------------------------------------------------------
  1027. ITextureInternal *ITextureInternal::CreateFileTexture( const char *pFileName, const char *pTextureGroupName )
  1028. {
  1029. CTexture *pTex = new CTexture;
  1030. pTex->InitFileTexture( pFileName, pTextureGroupName );
  1031. return pTex;
  1032. }
  1033. ITextureInternal *ITextureInternal::CreateReferenceTextureFromHandle(
  1034. const char *pTextureName,
  1035. const char *pTextureGroupName,
  1036. ShaderAPITextureHandle_t hTexture )
  1037. {
  1038. CReferenceToHandleTexture *pTex = new CReferenceToHandleTexture;
  1039. pTex->InitFromHandle( pTextureName, pTextureGroupName, hTexture );
  1040. return pTex;
  1041. }
  1042. ITextureInternal *ITextureInternal::CreateProceduralTexture(
  1043. const char *pTextureName,
  1044. const char *pTextureGroupName,
  1045. int w,
  1046. int h,
  1047. int d,
  1048. ImageFormat fmt,
  1049. int nFlags )
  1050. {
  1051. CTexture *pTex = new CTexture;
  1052. pTex->InitProceduralTexture( pTextureName, pTextureGroupName, w, h, d, fmt, nFlags );
  1053. pTex->IncrementReferenceCount();
  1054. return pTex;
  1055. }
  1056. // GR - named RT
  1057. ITextureInternal *ITextureInternal::CreateRenderTarget(
  1058. const char *pRTName,
  1059. int w,
  1060. int h,
  1061. RenderTargetSizeMode_t sizeMode,
  1062. ImageFormat fmt,
  1063. RenderTargetType_t type,
  1064. unsigned int textureFlags,
  1065. unsigned int renderTargetFlags,
  1066. bool bMultipleTargets )
  1067. {
  1068. CTexture *pTex = bMultipleTargets ? new CTexture_MultipleRenderTarget : new CTexture;
  1069. pTex->InitRenderTarget( pRTName, w, h, sizeMode, fmt, type, textureFlags, renderTargetFlags );
  1070. return pTex;
  1071. }
  1072. //-----------------------------------------------------------------------------
  1073. // Rebuild and exisiting render target in place.
  1074. //-----------------------------------------------------------------------------
  1075. void ITextureInternal::ChangeRenderTarget(
  1076. ITextureInternal *pTex,
  1077. int w,
  1078. int h,
  1079. RenderTargetSizeMode_t sizeMode,
  1080. ImageFormat fmt,
  1081. RenderTargetType_t type,
  1082. unsigned int textureFlags,
  1083. unsigned int renderTargetFlags )
  1084. {
  1085. pTex->Release();
  1086. dynamic_cast< CTexture * >(pTex)->InitRenderTarget( pTex->GetName(), w, h, sizeMode, fmt, type, textureFlags, renderTargetFlags );
  1087. }
  1088. void ITextureInternal::Destroy( ITextureInternal *pTex )
  1089. {
  1090. delete pTex;
  1091. }
  1092. //-----------------------------------------------------------------------------
  1093. // Constructor, destructor
  1094. //-----------------------------------------------------------------------------
  1095. CTexture::CTexture() : m_ImageFormat( IMAGE_FORMAT_UNKNOWN )
  1096. {
  1097. m_nActualMipCount = 0;
  1098. m_nMappingWidth = 0;
  1099. m_nMappingHeight = 0;
  1100. m_nMappingDepth = 1;
  1101. m_nActualWidth = 0;
  1102. m_nActualHeight = 0;
  1103. m_nActualDepth = 1;
  1104. m_nRefCount = 0;
  1105. m_nFlags = 0;
  1106. m_nInternalFlags = 0;
  1107. m_pTextureHandles = NULL;
  1108. m_pTempTextureHandles = NULL;
  1109. m_nFrameCount = 0;
  1110. VectorClear( m_vecReflectivity );
  1111. m_pTextureRegenerator = NULL;
  1112. m_nOriginalRenderTargetType = NO_RENDER_TARGET;
  1113. m_RenderTargetSizeMode = RT_SIZE_NO_CHANGE;
  1114. m_nOriginalRTWidth = m_nOriginalRTHeight = 1;
  1115. m_LowResImageWidth = 0;
  1116. m_LowResImageHeight = 0;
  1117. #if !defined( _GAMECONSOLE )
  1118. m_pLowResImage = NULL;
  1119. #else
  1120. *(unsigned int *)m_LowResImageSample = 0;
  1121. #endif
  1122. m_nDesiredDimensionLimit = 0;
  1123. m_nDesiredTempDimensionLimit = 0;
  1124. m_nActualDimensionLimit = 0;
  1125. m_hAsyncControl = NULL;
  1126. m_nMipSkipCount = 0;
  1127. #ifdef _DEBUG
  1128. m_pDebugName = NULL;
  1129. #endif
  1130. }
  1131. CTexture::~CTexture()
  1132. {
  1133. #ifdef _DEBUG
  1134. if ( m_nRefCount != 0 )
  1135. {
  1136. Warning( "Reference Count(%d) != 0 in ~CTexture for texture \"%s\"\n", (int)m_nRefCount, m_Name.String() );
  1137. }
  1138. if ( m_pDebugName )
  1139. {
  1140. delete [] m_pDebugName;
  1141. }
  1142. #endif
  1143. Shutdown();
  1144. // Deliberately stomp our VTable so that we can detect cases where code tries to access freed materials.
  1145. int *p = (int *)this;
  1146. *p = 0xdeadbeef;
  1147. }
  1148. //-----------------------------------------------------------------------------
  1149. // Initializes the texture
  1150. //-----------------------------------------------------------------------------
  1151. void CTexture::Init( int w, int h, int d, ImageFormat fmt, int iFlags, int iFrameCount )
  1152. {
  1153. Assert( iFrameCount > 0 );
  1154. // This is necessary to prevent blowing away the allocated state,
  1155. // which is necessary for the ReleaseTextureHandles call below to work.
  1156. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_ERROR;
  1157. // free and release previous data
  1158. // cannot change to new intialization parameters yet
  1159. FreeShaderAPITextures();
  1160. ReleaseTextureHandles();
  1161. // update to new initialization parameters
  1162. // these are the *desired* new values
  1163. m_nMappingWidth = w;
  1164. m_nMappingHeight = h;
  1165. m_nMappingDepth = d;
  1166. m_ImageFormat = fmt;
  1167. m_nFrameCount = iFrameCount;
  1168. // We don't know the actual width and height until we get it ready to render
  1169. m_nActualWidth = m_nActualHeight = 0;
  1170. m_nActualDepth = 1;
  1171. m_nActualMipCount = 0;
  1172. m_nFlags = iFlags;
  1173. m_nMipSkipCount = 0;
  1174. AllocateTextureHandles();
  1175. }
  1176. //-----------------------------------------------------------------------------
  1177. // Shuts down the texture
  1178. //-----------------------------------------------------------------------------
  1179. void CTexture::Shutdown()
  1180. {
  1181. // Clean up the low-res texture
  1182. #if !defined( _GAMECONSOLE )
  1183. delete[] m_pLowResImage;
  1184. m_pLowResImage = 0;
  1185. #endif
  1186. FreeResourceData();
  1187. // Frees the texture regen class
  1188. if ( m_pTextureRegenerator )
  1189. {
  1190. m_pTextureRegenerator->Release();
  1191. m_pTextureRegenerator = NULL;
  1192. }
  1193. // This deletes the textures
  1194. FreeShaderAPITextures();
  1195. ReleaseTextureHandles();
  1196. }
  1197. void CTexture::Release()
  1198. {
  1199. FreeShaderAPITextures();
  1200. }
  1201. IVTFTexture *CTexture::GetScratchVTFTexture()
  1202. {
  1203. if ( !s_pVTFTexture )
  1204. {
  1205. s_pVTFTexture = CreateVTFTexture();
  1206. }
  1207. return s_pVTFTexture;
  1208. }
  1209. IVTFTexture *CTexture::GetScratchVTFAsyncTexture()
  1210. {
  1211. if (!s_pVTFAsyncTexture)
  1212. {
  1213. s_pVTFAsyncTexture = CreateVTFTexture();
  1214. }
  1215. return s_pVTFAsyncTexture;
  1216. }
  1217. //-----------------------------------------------------------------------------
  1218. // Get an optimal read buffer, persists and avoids excessive allocations
  1219. //-----------------------------------------------------------------------------
  1220. int CTexture::GetOptimalReadBuffer( FileHandle_t hFile, int nSize, CUtlBuffer &optimalBuffer )
  1221. {
  1222. // get an optimal read buffer, only resize if necessary
  1223. int minSize = IsGameConsole() ? 0 : 2 * 1024 * 1024; // 360 has no min, PC uses 2MB min to avoid fragmentation
  1224. nSize = MAX(nSize, minSize);
  1225. int nBytesOptimalRead = g_pFullFileSystem->GetOptimalReadSize( hFile, nSize );
  1226. if ( nBytesOptimalRead > s_nOptimalReadBufferSize )
  1227. {
  1228. FreeOptimalReadBuffer( 0 );
  1229. s_nOptimalReadBufferSize = nBytesOptimalRead;
  1230. s_pOptimalReadBuffer = g_pFullFileSystem->AllocOptimalReadBuffer( hFile, nSize );
  1231. if ( mat_spewalloc.GetBool() )
  1232. {
  1233. Msg( "Allocated optimal read buffer of %d bytes @ 0x%p\n", s_nOptimalReadBufferSize, s_pOptimalReadBuffer );
  1234. }
  1235. }
  1236. // set external buffer and reset to empty
  1237. optimalBuffer.SetExternalBuffer( s_pOptimalReadBuffer, s_nOptimalReadBufferSize, 0, CUtlBuffer::READ_ONLY );
  1238. // return the optimal read size
  1239. return nBytesOptimalRead;
  1240. }
  1241. //-----------------------------------------------------------------------------
  1242. // Free the optimal read buffer if it grows too large
  1243. //-----------------------------------------------------------------------------
  1244. void CTexture::FreeOptimalReadBuffer( int nMaxSize )
  1245. {
  1246. if ( s_pOptimalReadBuffer && s_nOptimalReadBufferSize >= nMaxSize )
  1247. {
  1248. if ( mat_spewalloc.GetBool() )
  1249. {
  1250. Msg( "Freeing optimal read buffer of %d bytes @ 0x%p\n", s_nOptimalReadBufferSize, s_pOptimalReadBuffer );
  1251. }
  1252. g_pFullFileSystem->FreeOptimalReadBuffer( s_pOptimalReadBuffer );
  1253. s_pOptimalReadBuffer = NULL;
  1254. s_nOptimalReadBufferSize = 0;
  1255. }
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. //
  1259. // Various initialization methods
  1260. //
  1261. //-----------------------------------------------------------------------------
  1262. void CTexture::ApplyRenderTargetSizeMode( int &width, int &height, ImageFormat fmt )
  1263. {
  1264. width = m_nOriginalRTWidth;
  1265. height = m_nOriginalRTHeight;
  1266. switch ( m_RenderTargetSizeMode )
  1267. {
  1268. case RT_SIZE_FULL_FRAME_BUFFER:
  1269. {
  1270. MaterialSystem()->GetBackBufferDimensions( width, height );
  1271. }
  1272. break;
  1273. case RT_SIZE_FULL_FRAME_BUFFER_ROUNDED_UP:
  1274. {
  1275. MaterialSystem()->GetBackBufferDimensions( width, height );
  1276. }
  1277. break;
  1278. case RT_SIZE_PICMIP:
  1279. {
  1280. int fbWidth, fbHeight;
  1281. MaterialSystem()->GetBackBufferDimensions( fbWidth, fbHeight );
  1282. int picmip = g_config.skipMipLevels;
  1283. while( picmip > 0 )
  1284. {
  1285. width >>= 1;
  1286. height >>= 1;
  1287. picmip--;
  1288. }
  1289. while( width > fbWidth )
  1290. {
  1291. width >>= 1;
  1292. }
  1293. while( height > fbHeight )
  1294. {
  1295. height >>= 1;
  1296. }
  1297. }
  1298. break;
  1299. case RT_SIZE_DEFAULT:
  1300. {
  1301. // Assume that the input is pow2.
  1302. Assert( ( width & ( width - 1 ) ) == 0 );
  1303. Assert( ( height & ( height - 1 ) ) == 0 );
  1304. int fbWidth, fbHeight;
  1305. MaterialSystem()->GetBackBufferDimensions( fbWidth, fbHeight );
  1306. while( width > fbWidth )
  1307. {
  1308. width >>= 1;
  1309. }
  1310. while( height > fbHeight )
  1311. {
  1312. height >>= 1;
  1313. }
  1314. }
  1315. break;
  1316. case RT_SIZE_HDR:
  1317. {
  1318. MaterialSystem()->GetBackBufferDimensions( width, height );
  1319. width = width / 4;
  1320. height = height / 4;
  1321. }
  1322. break;
  1323. case RT_SIZE_OFFSCREEN:
  1324. {
  1325. int fbWidth, fbHeight;
  1326. MaterialSystem()->GetBackBufferDimensions( fbWidth, fbHeight );
  1327. // On 360, don't do this resizing for formats related to the shadow depth texture
  1328. #if defined( _GAMECONSOLE )
  1329. if ( !( (fmt == IMAGE_FORMAT_D16) || (fmt == IMAGE_FORMAT_D24S8) || (fmt == IMAGE_FORMAT_D24FS8) || (fmt == IMAGE_FORMAT_BGR565) || (fmt == IMAGE_FORMAT_D24X8_SHADOW) || (fmt == IMAGE_FORMAT_D16_SHADOW) ) )
  1330. #endif
  1331. {
  1332. // Shrink the buffer if it's bigger than back buffer. Otherwise, don't mess with it.
  1333. while( (width > fbWidth) || (height > fbHeight) )
  1334. {
  1335. width >>= 1;
  1336. height >>= 1;
  1337. }
  1338. }
  1339. }
  1340. break;
  1341. default:
  1342. {
  1343. Assert( m_RenderTargetSizeMode == RT_SIZE_NO_CHANGE );
  1344. // Cannot use RT_SIZE_NO_CHANGE if they are sharing the depth buffer.
  1345. Assert( m_nOriginalRenderTargetType != RENDER_TARGET );
  1346. }
  1347. break;
  1348. }
  1349. }
  1350. //-----------------------------------------------------------------------------
  1351. // Creates named render target texture
  1352. //-----------------------------------------------------------------------------
  1353. void CTexture::InitRenderTarget(
  1354. const char *pRTName,
  1355. int w,
  1356. int h,
  1357. RenderTargetSizeMode_t sizeMode,
  1358. ImageFormat fmt,
  1359. RenderTargetType_t type,
  1360. unsigned int textureFlags,
  1361. unsigned int renderTargetFlags )
  1362. {
  1363. if ( pRTName )
  1364. {
  1365. SetName( pRTName );
  1366. }
  1367. else
  1368. {
  1369. static int id = 0;
  1370. char pName[128];
  1371. Q_snprintf( pName, sizeof( pName ), "__render_target_%d", id );
  1372. ++id;
  1373. SetName( pName );
  1374. }
  1375. if ( renderTargetFlags & CREATERENDERTARGETFLAGS_HDR )
  1376. {
  1377. if ( HardwareConfig()->GetHDRType() == HDR_TYPE_FLOAT )
  1378. {
  1379. // slam the format
  1380. fmt = IMAGE_FORMAT_RGBA16161616F;
  1381. }
  1382. }
  1383. int nFrameCount = 1;
  1384. int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_RENDERTARGET;
  1385. nFlags |= textureFlags;
  1386. if ( type == RENDER_TARGET_NO_DEPTH )
  1387. {
  1388. nFlags |= TEXTUREFLAGS_NODEPTHBUFFER;
  1389. }
  1390. else if ( type == RENDER_TARGET_WITH_DEPTH || type == RENDER_TARGET_ONLY_DEPTH || g_pShaderAPI->DoRenderTargetsNeedSeparateDepthBuffer() )
  1391. {
  1392. nFlags |= TEXTUREFLAGS_DEPTHRENDERTARGET;
  1393. ++nFrameCount;
  1394. }
  1395. if ( IsX360() )
  1396. {
  1397. // 360 RT needs its coupled surface, expected at [nFrameCount-1]
  1398. ++nFrameCount;
  1399. }
  1400. if ( renderTargetFlags & CREATERENDERTARGETFLAGS_TEMP )
  1401. {
  1402. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET;
  1403. }
  1404. m_nOriginalRenderTargetType = type;
  1405. m_RenderTargetSizeMode = sizeMode;
  1406. m_nOriginalRTWidth = w;
  1407. m_nOriginalRTHeight = h;
  1408. if ( ImageLoader::ImageFormatInfo(fmt).m_nNumAlphaBits > 1 )
  1409. {
  1410. nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  1411. }
  1412. else if ( ImageLoader::ImageFormatInfo(fmt).m_nNumAlphaBits == 1 )
  1413. {
  1414. nFlags |= TEXTUREFLAGS_ONEBITALPHA;
  1415. }
  1416. #ifdef _X360
  1417. if ( renderTargetFlags & CREATERENDERTARGETFLAGS_ALIASCOLORANDDEPTHSURFACES )
  1418. {
  1419. nFlags |= TEXTUREFLAGS_ALIAS_COLOR_AND_DEPTH_SURFACES;
  1420. }
  1421. #endif
  1422. ApplyRenderTargetSizeMode( w, h, fmt );
  1423. Init( w, h, 1, fmt, nFlags, nFrameCount );
  1424. m_TextureGroupName = TEXTURE_GROUP_RENDER_TARGET;
  1425. }
  1426. void CTexture::OnRestore()
  1427. {
  1428. // May have to change whether or not we have a depth buffer.
  1429. // Are we a render target?
  1430. if ( IsPC() && ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) )
  1431. {
  1432. // Did they not ask for a depth buffer?
  1433. if ( m_nOriginalRenderTargetType == RENDER_TARGET )
  1434. {
  1435. // But, did we force them to have one, or should we force them to have one this time around?
  1436. bool bShouldForce = g_pShaderAPI->DoRenderTargetsNeedSeparateDepthBuffer();
  1437. bool bDidForce = ((m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET) != 0);
  1438. if ( bShouldForce != bDidForce )
  1439. {
  1440. int nFlags = m_nFlags;
  1441. int iFrameCount = m_nFrameCount;
  1442. if ( bShouldForce )
  1443. {
  1444. Assert( !( nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) );
  1445. iFrameCount = 2;
  1446. nFlags |= TEXTUREFLAGS_DEPTHRENDERTARGET;
  1447. }
  1448. else
  1449. {
  1450. Assert( ( nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) );
  1451. iFrameCount = 1;
  1452. nFlags &= ~TEXTUREFLAGS_DEPTHRENDERTARGET;
  1453. }
  1454. Shutdown();
  1455. int newWidth, newHeight;
  1456. ApplyRenderTargetSizeMode( newWidth, newHeight, m_ImageFormat );
  1457. Init( newWidth, newHeight, 1, m_ImageFormat, nFlags, iFrameCount );
  1458. return;
  1459. }
  1460. }
  1461. // If we didn't recreate it up above, then we may need to resize it anyway if the framebuffer
  1462. // got smaller than we are.
  1463. int newWidth, newHeight;
  1464. ApplyRenderTargetSizeMode( newWidth, newHeight, m_ImageFormat );
  1465. if ( newWidth != m_nMappingWidth || newHeight != m_nMappingHeight )
  1466. {
  1467. Shutdown();
  1468. Init( newWidth, newHeight, 1, m_ImageFormat, m_nFlags, m_nFrameCount );
  1469. return;
  1470. }
  1471. }
  1472. }
  1473. //-----------------------------------------------------------------------------
  1474. // Creates a procedural texture
  1475. //-----------------------------------------------------------------------------
  1476. void CTexture::InitProceduralTexture( const char *pTextureName, const char *pTextureGroupName, int w, int h, int d, ImageFormat fmt, int nFlags )
  1477. {
  1478. // Compressed textures aren't allowed for procedural textures, except the runtime ones
  1479. Assert( !ImageLoader::IsCompressed( fmt ) || ImageLoader::IsRuntimeCompressed( fmt ) );
  1480. // We shouldn't be asking for render targets here
  1481. Assert( (nFlags & (TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET)) == 0 );
  1482. SetName( pTextureName );
  1483. // Eliminate flags that are inappropriate...
  1484. nFlags &= ~TEXTUREFLAGS_HINT_DXT5 | TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA |
  1485. TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET;
  1486. // Insert required flags
  1487. nFlags |= TEXTUREFLAGS_PROCEDURAL;
  1488. int nAlphaBits = ImageLoader::ImageFormatInfo(fmt).m_nNumAlphaBits;
  1489. if (nAlphaBits > 1)
  1490. {
  1491. nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  1492. }
  1493. else if (nAlphaBits == 1)
  1494. {
  1495. nFlags |= TEXTUREFLAGS_ONEBITALPHA;
  1496. }
  1497. // Procedural textures are always one frame only
  1498. Init( w, h, d, fmt, nFlags, 1 );
  1499. m_TextureGroupName = pTextureGroupName;
  1500. }
  1501. //-----------------------------------------------------------------------------
  1502. // Creates a file texture
  1503. //-----------------------------------------------------------------------------
  1504. void CTexture::InitFileTexture( const char *pTextureFile, const char *pTextureGroupName )
  1505. {
  1506. // For files, we only really know about the file name
  1507. // At any time, the file contents could change, and we could have
  1508. // a different size, number of frames, etc.
  1509. SetName( pTextureFile );
  1510. m_TextureGroupName = pTextureGroupName;
  1511. }
  1512. //-----------------------------------------------------------------------------
  1513. // Assigns/releases texture IDs for our animation frames
  1514. //-----------------------------------------------------------------------------
  1515. void CTexture::AllocateTextureHandles()
  1516. {
  1517. Assert( !m_pTextureHandles );
  1518. if ( m_nFrameCount <= 0 )
  1519. {
  1520. AssertMsg( false, "CTexture::AllocateTextureHandles attempted to allocate 0 frames of texture handles!" );
  1521. Warning( "CTexture::AllocateTextureHandles \"%s\" attempted to allocate 0 frames of texture handles!", GetName() );
  1522. m_nFrameCount = 1;
  1523. }
  1524. m_pTextureHandles = new ShaderAPITextureHandle_t[m_nFrameCount];
  1525. if ( m_pTextureHandles == NULL )
  1526. {
  1527. MemOutOfMemory( sizeof(ShaderAPITextureHandle_t) * m_nFrameCount );
  1528. }
  1529. else
  1530. {
  1531. for( int i = 0; i != m_nFrameCount; ++i )
  1532. {
  1533. m_pTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
  1534. }
  1535. }
  1536. }
  1537. void CTexture::ReleaseTextureHandles()
  1538. {
  1539. if ( m_pTextureHandles )
  1540. {
  1541. delete[] m_pTextureHandles;
  1542. m_pTextureHandles = NULL;
  1543. }
  1544. }
  1545. int GetCreationFlags( int iTextureFlags, int iInternalTextureFlags, ImageFormat fmt )
  1546. {
  1547. int nCreateFlags = 0;
  1548. if ( iTextureFlags & TEXTUREFLAGS_ENVMAP )
  1549. {
  1550. nCreateFlags |= TEXTURE_CREATE_CUBEMAP;
  1551. }
  1552. bool bIsFloat = ( fmt == IMAGE_FORMAT_RGBA16161616F ) || ( fmt == IMAGE_FORMAT_R32F ) ||
  1553. ( fmt == IMAGE_FORMAT_RGB323232F ) || ( fmt == IMAGE_FORMAT_RGBA32323232F );
  1554. // Don't do sRGB on floating point textures
  1555. if ( ( iTextureFlags & TEXTUREFLAGS_SRGB ) && !bIsFloat )
  1556. {
  1557. nCreateFlags |= TEXTURE_CREATE_SRGB; // for Posix/GL only
  1558. }
  1559. if ( iTextureFlags & TEXTUREFLAGS_ANISOTROPIC )
  1560. {
  1561. nCreateFlags |= TEXTURE_CREATE_ANISOTROPIC; // for Posix/GL only
  1562. }
  1563. if ( iTextureFlags & TEXTUREFLAGS_RENDERTARGET )
  1564. {
  1565. nCreateFlags |= TEXTURE_CREATE_RENDERTARGET;
  1566. }
  1567. else
  1568. {
  1569. // If it's not a render target, use the texture manager in dx
  1570. nCreateFlags |= TEXTURE_CREATE_MANAGED;
  1571. }
  1572. if ( iTextureFlags & TEXTUREFLAGS_DEFAULT_POOL )
  1573. {
  1574. // Needs to be created in default pool, and be marked as dynamic.
  1575. nCreateFlags &= ~TEXTURE_CREATE_MANAGED;
  1576. nCreateFlags |= TEXTURE_CREATE_DYNAMIC;
  1577. }
  1578. if ( iTextureFlags & TEXTUREFLAGS_POINTSAMPLE )
  1579. {
  1580. nCreateFlags |= TEXTURE_CREATE_UNFILTERABLE_OK;
  1581. }
  1582. if ( iTextureFlags & TEXTUREFLAGS_VERTEXTEXTURE )
  1583. {
  1584. nCreateFlags |= TEXTURE_CREATE_VERTEXTEXTURE;
  1585. }
  1586. if ( IsGameConsole() )
  1587. {
  1588. if ( iInternalTextureFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD )
  1589. {
  1590. // queued load, no d3d bits until data arrival
  1591. nCreateFlags |= TEXTURE_CREATE_NOD3DMEMORY;
  1592. }
  1593. if ( iInternalTextureFlags & TEXTUREFLAGSINTERNAL_REDUCED )
  1594. {
  1595. // propagate this information
  1596. nCreateFlags |= TEXTURE_CREATE_REDUCED;
  1597. }
  1598. if ( iInternalTextureFlags & TEXTUREFLAGSINTERNAL_ERROR )
  1599. {
  1600. // propagate this information
  1601. nCreateFlags |= TEXTURE_CREATE_ERROR;
  1602. }
  1603. if ( iInternalTextureFlags & ( TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE | TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE ) )
  1604. {
  1605. // propagate this information
  1606. nCreateFlags |= TEXTURE_CREATE_EXCLUDED;
  1607. }
  1608. if ( IsPS3() )
  1609. {
  1610. if ( iTextureFlags & TEXTUREFLAGS_PROCEDURAL )
  1611. {
  1612. nCreateFlags |= TEXTURE_CREATE_DYNAMIC;
  1613. }
  1614. }
  1615. if ( IsX360() )
  1616. {
  1617. if ( iTextureFlags & TEXTUREFLAGS_PROCEDURAL )
  1618. {
  1619. nCreateFlags |= TEXTURE_CREATE_CANCONVERTFORMAT;
  1620. }
  1621. if ( iTextureFlags & TEXTUREFLAGS_PWL_CORRECTED )
  1622. {
  1623. nCreateFlags |= TEXTURE_CREATE_PWLCORRECTED;
  1624. }
  1625. if ( iInternalTextureFlags & TEXTUREFLAGSINTERNAL_CACHEABLE )
  1626. {
  1627. nCreateFlags |= TEXTURE_CREATE_CACHEABLE;
  1628. }
  1629. }
  1630. }
  1631. return nCreateFlags;
  1632. }
  1633. //-----------------------------------------------------------------------------
  1634. // Creates the texture
  1635. //-----------------------------------------------------------------------------
  1636. bool CTexture::AllocateShaderAPITextures()
  1637. {
  1638. Assert( !HasBeenAllocated() );
  1639. if ( !g_pShaderAPI->CanDownloadTextures() )
  1640. return false;
  1641. int nCreateFlags = GetCreationFlags( m_nFlags, m_nInternalFlags, m_ImageFormat );
  1642. int nCount = m_nFrameCount;
  1643. if ( m_nFlags & TEXTUREFLAGS_RENDERTARGET )
  1644. {
  1645. // This here is simply so we can use a different call to
  1646. // create the depth texture below
  1647. // nCount must be 2 on pc/ps3, must be 3 on 360
  1648. if ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) &&
  1649. ( ( ( IsPC() || IsPS3() ) && (nCount == 2)) || (IsX360() && (nCount == 3)) ) )
  1650. {
  1651. --nCount;
  1652. }
  1653. }
  1654. int nCopies = 1;
  1655. if ( IsProcedural() )
  1656. {
  1657. // This is sort of hacky... should we store the # of copies in the VTF?
  1658. if ( !( m_nFlags & TEXTUREFLAGS_SINGLECOPY ) )
  1659. {
  1660. // FIXME: That 6 there is heuristically what I came up with what I
  1661. // need to get eyes not to stall on map alyx3. We need a better way
  1662. // of determining how many copies of the texture we should store.
  1663. nCopies = 6;
  1664. }
  1665. }
  1666. if ( IsGameConsole() )
  1667. {
  1668. if ( IsX360() && ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) )
  1669. {
  1670. // 360 render targets allocates one additional handle for optional EDRAM surface
  1671. --nCount;
  1672. m_pTextureHandles[m_nFrameCount - 1] = INVALID_SHADERAPI_TEXTURE_HANDLE;
  1673. }
  1674. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD )
  1675. {
  1676. // Artificially increment reference count (per frame) to ensure
  1677. // a queued texture stays resident until it's wholly finalized.
  1678. m_nRefCount += nCount;
  1679. }
  1680. }
  1681. // For depth only render target: adjust texture width/height
  1682. // Currently we just leave it the same size, will update with further testing
  1683. int nShaderApiCreateTextureDepth = ( ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET ) && ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ) ) ? 1 : m_nActualDepth;
  1684. if ( m_pTempTextureHandles )
  1685. {
  1686. // send the prior handles (should be available) for expected reuse
  1687. nCreateFlags |= TEXTURE_CREATE_REUSEHANDLES;
  1688. for ( int i = 0; i < m_nFrameCount; i++ )
  1689. {
  1690. m_pTextureHandles[i] = m_pTempTextureHandles[i];
  1691. }
  1692. }
  1693. // Create all animated texture frames in a single call
  1694. g_pShaderAPI->CreateTextures(
  1695. m_pTextureHandles, nCount,
  1696. m_nActualWidth, m_nActualHeight, nShaderApiCreateTextureDepth, m_ImageFormat, m_nActualMipCount,
  1697. nCopies, nCreateFlags, GetName(), GetTextureGroupName() );
  1698. // Create the depth render target buffer
  1699. if ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
  1700. {
  1701. MEM_ALLOC_CREDIT();
  1702. Assert( nCount == 1 );
  1703. char debugName[128];
  1704. sprintf( debugName, "%s_ZBuffer", GetName() );
  1705. bool bAliasColorAndDepthSurfaces360 = false;
  1706. #ifdef _X360
  1707. bAliasColorAndDepthSurfaces360 = ( m_nFlags & TEXTUREFLAGS_ALIAS_COLOR_AND_DEPTH_SURFACES ) != 0;
  1708. #endif
  1709. m_pTextureHandles[1] = g_pShaderAPI->CreateDepthTexture(
  1710. m_ImageFormat,
  1711. m_nActualWidth,
  1712. m_nActualHeight,
  1713. debugName,
  1714. ( m_nOriginalRenderTargetType == RENDER_TARGET_ONLY_DEPTH ),
  1715. bAliasColorAndDepthSurfaces360 );
  1716. }
  1717. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_ALLOCATED;
  1718. return true;
  1719. }
  1720. //-----------------------------------------------------------------------------
  1721. // Releases the texture's hardware memory
  1722. //-----------------------------------------------------------------------------
  1723. void CTexture::FreeShaderAPITextures()
  1724. {
  1725. if ( m_pTextureHandles && HasBeenAllocated() )
  1726. {
  1727. // Release the frames
  1728. for ( int i = m_nFrameCount; --i >= 0; )
  1729. {
  1730. if ( g_pShaderAPI->IsTexture( m_pTextureHandles[i] ) )
  1731. {
  1732. #ifdef WIN32
  1733. Assert( _heapchk() == _HEAPOK );
  1734. #endif
  1735. g_pShaderAPI->DeleteTexture( m_pTextureHandles[i] );
  1736. m_pTextureHandles[i] = INVALID_SHADERAPI_TEXTURE_HANDLE;
  1737. }
  1738. }
  1739. }
  1740. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_ALLOCATED;
  1741. }
  1742. //-----------------------------------------------------------------------------
  1743. // Computes the actual format of the texture
  1744. //-----------------------------------------------------------------------------
  1745. ImageFormat CTexture::ComputeActualFormat( ImageFormat srcFormat )
  1746. {
  1747. ImageFormat dstFormat;
  1748. bool bIsCompressed = ImageLoader::IsCompressed( srcFormat );
  1749. if ( g_config.bCompressedTextures && bIsCompressed )
  1750. {
  1751. // for the runtime compressed formats the srcFormat won't equal the dstFormat, and we need to return srcFormat here
  1752. if ( ImageLoader::IsRuntimeCompressed( srcFormat ) )
  1753. {
  1754. return srcFormat;
  1755. }
  1756. // don't do anything since we are already in a compressed format.
  1757. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( srcFormat );
  1758. Assert( dstFormat == srcFormat );
  1759. return dstFormat;
  1760. }
  1761. if ( IsGameConsole() && ( srcFormat == IMAGE_FORMAT_A8 ) )
  1762. {
  1763. // these are the right alpha formats for xbox
  1764. return IMAGE_FORMAT_A8;
  1765. }
  1766. #if defined( _X360 )
  1767. if ( srcFormat == IMAGE_FORMAT_LINEAR_I8 )
  1768. {
  1769. return IMAGE_FORMAT_LINEAR_I8;
  1770. }
  1771. #endif
  1772. // NOTE: Below this piece of code is only called when compressed textures are
  1773. // turned off, or if the source texture is not compressed.
  1774. #ifdef DX_TO_GL_ABSTRACTION
  1775. if ( ( srcFormat == IMAGE_FORMAT_UVWQ8888 ) || ( srcFormat == IMAGE_FORMAT_UV88 ) || ( srcFormat == IMAGE_FORMAT_UVLX8888 ) )
  1776. {
  1777. // Danger, this is going to blow up on the Mac. You better know what you're
  1778. // doing with these exotic formats...which were introduced in 1999
  1779. Assert( 0 );
  1780. }
  1781. #endif
  1782. // We use the TEXTUREFLAGS_EIGHTBITALPHA and TEXTUREFLAGS_ONEBITALPHA flags
  1783. // to decide how many bits of alpha we need; vtex checks the alpha channel
  1784. // for all white, etc.
  1785. if( (srcFormat == IMAGE_FORMAT_UVWQ8888) || ( srcFormat == IMAGE_FORMAT_UV88 ) ||
  1786. ( srcFormat == IMAGE_FORMAT_UVLX8888 ) || ( srcFormat == IMAGE_FORMAT_RGBA16161616 ) ||
  1787. ( srcFormat == IMAGE_FORMAT_RGBA16161616F ) || ( srcFormat == IMAGE_FORMAT_RGBA32323232F ) ||
  1788. ( srcFormat == IMAGE_FORMAT_R32F ) )
  1789. {
  1790. #ifdef DX_TO_GL_ABSTRACTION
  1791. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( srcFormat, false ); // Stupid HACK!
  1792. #else
  1793. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( srcFormat, true ); // Stupid HACK!
  1794. #endif
  1795. }
  1796. else if ( m_nFlags & ( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA ) )
  1797. {
  1798. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( IMAGE_FORMAT_BGRA8888 );
  1799. }
  1800. else if ( srcFormat == IMAGE_FORMAT_I8 )
  1801. {
  1802. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( IMAGE_FORMAT_I8 );
  1803. }
  1804. else
  1805. {
  1806. dstFormat = g_pShaderAPI->GetNearestSupportedFormat( IMAGE_FORMAT_BGR888 );
  1807. }
  1808. return dstFormat;
  1809. }
  1810. //-----------------------------------------------------------------------------
  1811. // Compute the actual mip count based on the actual size
  1812. //-----------------------------------------------------------------------------
  1813. int CTexture::ComputeActualMipCount() const
  1814. {
  1815. bool bForceTextureAllMips = g_bForceTextureAllMips; // Init with global set from -forceallmips on the command line
  1816. // If the current hardware doesn't support mipped cubemaps, return 1
  1817. if ( ( m_nFlags & TEXTUREFLAGS_ENVMAP ) && ( !HardwareConfig()->SupportsMipmappedCubemaps() ) )
  1818. {
  1819. return 1;
  1820. }
  1821. // "nomip 1" - If the artists requested no mips in the .txt file of their source art, return 1
  1822. if ( m_nFlags & TEXTUREFLAGS_NOMIP )
  1823. {
  1824. return 1;
  1825. }
  1826. // "allmips 1" - If the artists requested all mips in the .txt file of their source art, load all mips on all platforms
  1827. if ( m_nFlags & TEXTUREFLAGS_ALL_MIPS )
  1828. {
  1829. bForceTextureAllMips = true;
  1830. }
  1831. // "mostmips 1" - If the artists requested most mips in the .txt file of their source art, don't load the bottom mips, ever
  1832. bool bMostMips = false;
  1833. if ( m_nFlags & TEXTUREFLAGS_MOST_MIPS )
  1834. {
  1835. bMostMips = true;
  1836. }
  1837. // OpenGL - Don't ever drop mips
  1838. if ( IsOpenGL() )
  1839. {
  1840. bForceTextureAllMips = true;
  1841. bMostMips = false;
  1842. }
  1843. // If on the PC and running a newer OS than WinXP, then don't drop mips.
  1844. // XP can crash if we run out of paged pool memory since each mip consumes ~1kb of paged pool memory.
  1845. #if defined( WIN32 ) && !defined( _GAMECONSOLE )
  1846. {
  1847. OSVERSIONINFOEX osvi;
  1848. ZeroMemory( &osvi, sizeof( OSVERSIONINFOEX ) );
  1849. osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFOEX );
  1850. if ( GetVersionEx( ( OSVERSIONINFO * )&osvi ) )
  1851. {
  1852. if ( osvi.dwMajorVersion >= 6 ) // Major version 6 is Windows Vista and Win7
  1853. {
  1854. // Windows Vista or newer, so it's safe to load all mips
  1855. bForceTextureAllMips = true;
  1856. }
  1857. }
  1858. }
  1859. #endif
  1860. if ( IsX360() )
  1861. {
  1862. bForceTextureAllMips = true;
  1863. }
  1864. bool bIsFlashlightTextureOnGL = false;
  1865. #ifdef DX_TO_GL_ABSTRACTION
  1866. // Hack to only recognize the border bit (for the purposes of truncating the mip chain) on "flashlight" textures on Mac
  1867. const char *pTexName = m_Name.String();
  1868. bIsFlashlightTextureOnGL = ( m_nFlags & TEXTUREFLAGS_BORDER ) && V_stristr( pTexName, "flashlight" );
  1869. #endif
  1870. // If we are not loading all mips, then count the number of mips we want to load
  1871. if ( ( !IsOpenGL() && !bForceTextureAllMips ) || bMostMips || bIsFlashlightTextureOnGL )
  1872. {
  1873. // Stop loading mips when width or height is < 32
  1874. int nMaxMipSize = 32; // Default for windows XP
  1875. if ( IsPS3() )
  1876. {
  1877. nMaxMipSize = 4;
  1878. }
  1879. if ( bMostMips )
  1880. {
  1881. // !!! This overrides all other settings !!!
  1882. nMaxMipSize = 32;
  1883. }
  1884. int nNumMipLevels = 1;
  1885. int h = m_nActualWidth;
  1886. int w = m_nActualHeight;
  1887. while ( MIN( w, h ) > nMaxMipSize )
  1888. {
  1889. nNumMipLevels++;
  1890. w >>= 1;
  1891. h >>= 1;
  1892. }
  1893. return nNumMipLevels;
  1894. }
  1895. else
  1896. {
  1897. // Load all mips
  1898. return ImageLoader::GetNumMipMapLevels( m_nActualWidth, m_nActualHeight, m_nActualDepth );
  1899. }
  1900. }
  1901. //-----------------------------------------------------------------------------
  1902. // Calculates info about whether we can make the texture smaller and by how much
  1903. //-----------------------------------------------------------------------------
  1904. int CTexture::ComputeActualSize( bool bIgnorePicmip, IVTFTexture *pVTFTexture )
  1905. {
  1906. // Must skip mip levels if the texture is too large for our board to handle
  1907. m_nActualWidth = m_nMappingWidth;
  1908. m_nActualHeight = m_nMappingHeight;
  1909. m_nActualDepth = m_nMappingDepth;
  1910. int nClampX = m_nActualWidth; // no clamping (clamp to texture dimensions)
  1911. int nClampY = m_nActualHeight;
  1912. int nClampZ = m_nActualDepth;
  1913. //
  1914. // PC:
  1915. // Fetch clamping dimensions from special LOD control settings block
  1916. // or runtime texture lod override.
  1917. //
  1918. if ( IsPC() )
  1919. {
  1920. // Fetch LOD settings from the VTF if available
  1921. TextureLODControlSettings_t lcs;
  1922. memset( &lcs, 0, sizeof( lcs ) );
  1923. TextureLODControlSettings_t const *pLODInfo = NULL;
  1924. if ( pVTFTexture )
  1925. {
  1926. pLODInfo = reinterpret_cast<TextureLODControlSettings_t const *> (
  1927. pVTFTexture->GetResourceData( VTF_RSRC_TEXTURE_LOD_SETTINGS, NULL ) );
  1928. if ( pLODInfo )
  1929. lcs = *pLODInfo;
  1930. }
  1931. // Prepare the default LOD settings (that essentially result in no clamping)
  1932. TextureLODControlSettings_t default_lod_settings;
  1933. memset( &default_lod_settings, 0, sizeof( default_lod_settings ) );
  1934. {
  1935. for ( int w = m_nActualWidth; w > 1; w >>= 1 )
  1936. ++ default_lod_settings.m_ResolutionClampX;
  1937. for ( int h = m_nActualHeight; h > 1; h >>= 1 )
  1938. ++ default_lod_settings.m_ResolutionClampY;
  1939. }
  1940. // Check for LOD control override
  1941. {
  1942. TextureLodOverride::OverrideInfo oi = TextureLodOverride::Get( GetName() );
  1943. if ( oi.x && oi.y && !pLODInfo ) // If overriding texture that doesn't have lod info yet, then use default
  1944. lcs = default_lod_settings;
  1945. lcs.m_ResolutionClampX += oi.x;
  1946. lcs.m_ResolutionClampY += oi.y;
  1947. if ( int8( lcs.m_ResolutionClampX ) < 0 )
  1948. lcs.m_ResolutionClampX = 0;
  1949. if ( int8( lcs.m_ResolutionClampY ) < 0 )
  1950. lcs.m_ResolutionClampY = 0;
  1951. }
  1952. // Compute the requested mip0 dimensions
  1953. if ( lcs.m_ResolutionClampX && lcs.m_ResolutionClampY )
  1954. {
  1955. nClampX = (1 << lcs.m_ResolutionClampX );
  1956. nClampY = (1 << lcs.m_ResolutionClampY );
  1957. }
  1958. // Check for exclude settings
  1959. {
  1960. int iExclude = TextureLodExclude::Get( GetName() );
  1961. if ( iExclude > 0 )
  1962. {
  1963. // Mip request by exclude rules
  1964. nClampX = MIN( iExclude, nClampX );
  1965. nClampY = MIN( iExclude, nClampY );
  1966. }
  1967. else if ( iExclude == 0 )
  1968. {
  1969. // Texture should be excluded completely
  1970. // we cannot actually exclude it, we need
  1971. // to clamp it down to 4x4 for dxt to work.
  1972. // The texture will never be loaded when honoring
  1973. // the real exclude list rules.
  1974. nClampX = MIN( 4, nClampX );
  1975. nClampY = MIN( 4, nClampY );
  1976. }
  1977. }
  1978. // In case clamp values exceed texture dimensions, then fix up
  1979. // the clamping values
  1980. nClampX = MIN( nClampX, m_nActualWidth );
  1981. nClampY = MIN( nClampY, m_nActualHeight );
  1982. }
  1983. //
  1984. // Honor dimension limit restrictions
  1985. //
  1986. int nDimensionLimit = 0;
  1987. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE )
  1988. {
  1989. nDimensionLimit = m_nDesiredTempDimensionLimit;
  1990. }
  1991. else
  1992. {
  1993. nDimensionLimit = m_nDesiredDimensionLimit;
  1994. }
  1995. if ( nDimensionLimit < 0 )
  1996. {
  1997. nDimensionLimit = 0;
  1998. }
  1999. if ( IsGameConsole() )
  2000. {
  2001. // limiting large textures
  2002. static int s_nMaxDimensionLimit = 0;
  2003. if ( !s_nMaxDimensionLimit )
  2004. {
  2005. bool bNo256 = ( CommandLine()->FindParm( "-no256" ) != 0 );
  2006. bool bNo512 = ( CommandLine()->FindParm( "-no512" ) != 0 );
  2007. bool bNo1024 = CommandLine()->FindParm( "-no1024" ) && !CommandLine()->FindParm( "-allow1024" );
  2008. if ( bNo256 )
  2009. {
  2010. s_nMaxDimensionLimit = 128;
  2011. }
  2012. else if ( bNo512 )
  2013. {
  2014. s_nMaxDimensionLimit = 256;
  2015. }
  2016. else if ( g_pFullFileSystem->IsDVDHosted() || bNo1024 )
  2017. {
  2018. s_nMaxDimensionLimit = 512;
  2019. }
  2020. else
  2021. {
  2022. s_nMaxDimensionLimit = 1024;
  2023. }
  2024. }
  2025. if ( nDimensionLimit > 0 )
  2026. {
  2027. nDimensionLimit = MIN( nDimensionLimit, s_nMaxDimensionLimit );
  2028. }
  2029. else if ( !( m_nFlags & (TEXTUREFLAGS_NOLOD|TEXTUREFLAGS_NOMIP|TEXTUREFLAGS_PROCEDURAL|TEXTUREFLAGS_RENDERTARGET|TEXTUREFLAGS_DEPTHRENDERTARGET) ) )
  2030. {
  2031. nDimensionLimit = s_nMaxDimensionLimit;
  2032. }
  2033. }
  2034. else if ( IsPlatformOSX() )
  2035. {
  2036. // limiting large textures on OSX to 1024, override with -allow2048 on cl
  2037. static int s_nMaxDimensionLimit = 0;
  2038. if (!s_nMaxDimensionLimit)
  2039. {
  2040. bool bAllow2048 = !!CommandLine()->FindParm( "-allow2048" );
  2041. if ( !bAllow2048 && !( m_nFlags & (TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET) ) )
  2042. {
  2043. s_nMaxDimensionLimit = 1024;
  2044. }
  2045. }
  2046. if ( !( m_nFlags & (TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_RENDERTARGET | TEXTUREFLAGS_DEPTHRENDERTARGET) ) )
  2047. {
  2048. nDimensionLimit = s_nMaxDimensionLimit;
  2049. }
  2050. }
  2051. // Special case: If the top mipmap level is <= 128KB, and the width is really wide (2048), and its height is <= 64, and we know it's mipmapped, then allow one axis to be > 1024, otherwise just restrict to 1024.
  2052. // This purposely convoluted logic is useful on things like the confetti particle effect's texture (used in sp_a2_column_blocker), which is 2048x64, and is very noticeable when it's cut down to 1024x32.
  2053. if ( nDimensionLimit == 1024 )
  2054. {
  2055. if ( ( ImageLoader::GetMemRequired( m_nActualWidth, m_nActualHeight, 1, m_ImageFormat, false ) <= 128 * 1024 ) &&
  2056. ( m_nActualWidth == 2048 ) && ( m_nActualHeight <= 64 ) &&
  2057. ( pVTFTexture ) && ( pVTFTexture->MipCount() > 1 ) )
  2058. {
  2059. nDimensionLimit = 2048;
  2060. }
  2061. }
  2062. //
  2063. // Unless ignoring picmip, reflect the global picmip level in clamp dimensions
  2064. //
  2065. if ( !bIgnorePicmip )
  2066. {
  2067. // If picmip requests texture degradation, then honor it
  2068. // for loddable textures only
  2069. if ( !( m_nFlags & TEXTUREFLAGS_NOLOD ) &&
  2070. ( g_config.skipMipLevels > 0 ) )
  2071. {
  2072. for ( int iDegrade = 0; iDegrade < g_config.skipMipLevels; ++ iDegrade )
  2073. {
  2074. // don't go lower than 4, or dxt textures won't work properly
  2075. if ( nClampX > 4 &&
  2076. nClampY > 4 )
  2077. {
  2078. nClampX >>= 1;
  2079. nClampY >>= 1;
  2080. }
  2081. }
  2082. }
  2083. // If picmip requests quality upgrade, then always honor it
  2084. if ( g_config.skipMipLevels < 0 )
  2085. {
  2086. for ( int iUpgrade = 0; iUpgrade < - g_config.skipMipLevels; ++ iUpgrade )
  2087. {
  2088. if ( nClampX < m_nActualWidth &&
  2089. nClampY < m_nActualHeight )
  2090. {
  2091. nClampX <<= 1;
  2092. nClampY <<= 1;
  2093. }
  2094. else
  2095. break;
  2096. }
  2097. }
  2098. }
  2099. // honor dimension limit after picmip downgrade/upgrade
  2100. if ( nDimensionLimit > 0 )
  2101. {
  2102. while ( nClampX > nDimensionLimit ||
  2103. nClampY > nDimensionLimit )
  2104. {
  2105. nClampX >>= 1;
  2106. nClampY >>= 1;
  2107. }
  2108. }
  2109. //
  2110. // Now use hardware settings to clamp our "clamping dimensions"
  2111. //
  2112. int iHwWidth = HardwareConfig()->MaxTextureWidth();
  2113. int iHwHeight = HardwareConfig()->MaxTextureHeight();
  2114. int iHwDepth = HardwareConfig()->MaxTextureDepth();
  2115. nClampX = MIN( nClampX, MAX( iHwWidth, 4 ) );
  2116. nClampY = MIN( nClampY, MAX( iHwHeight, 4 ) );
  2117. nClampZ = MIN( nClampZ, MAX( iHwDepth, 1 ) );
  2118. Assert( nClampZ >= 1 );
  2119. // In case clamp values exceed texture dimensions, then fix up
  2120. // the clamping values.
  2121. nClampX = MIN( nClampX, m_nActualWidth );
  2122. nClampY = MIN( nClampY, m_nActualHeight );
  2123. nClampZ = MIN( nClampZ, m_nActualDepth );
  2124. //
  2125. // Clamp to the determined dimensions
  2126. //
  2127. int numMipsSkipped = 0; // will compute now when clamping how many mips we drop
  2128. while ( ( m_nActualWidth > nClampX ) ||
  2129. ( m_nActualHeight > nClampY ) ||
  2130. ( m_nActualDepth > nClampZ ) )
  2131. {
  2132. m_nActualWidth >>= 1;
  2133. m_nActualHeight >>= 1;
  2134. m_nActualDepth >>= 1;
  2135. if ( m_nActualDepth < 1 )
  2136. m_nActualDepth = 1;
  2137. ++ numMipsSkipped;
  2138. }
  2139. Assert( m_nActualWidth > 0 && m_nActualHeight > 0 && m_nActualDepth > 0 );
  2140. // Now that we've got the actual size, we can figure out the mip count
  2141. m_nActualMipCount = ComputeActualMipCount();
  2142. // Returns the number we skipped
  2143. return numMipsSkipped;
  2144. }
  2145. //-----------------------------------------------------------------------------
  2146. // Used to modify the texture bits (procedural textures only)
  2147. //-----------------------------------------------------------------------------
  2148. void CTexture::SetTextureRegenerator( ITextureRegenerator *pTextureRegen, bool releaseExisting )
  2149. {
  2150. // NOTE: These can only be used by procedural textures
  2151. Assert( IsProcedural() );
  2152. if ( m_pTextureRegenerator && releaseExisting )
  2153. {
  2154. m_pTextureRegenerator->Release();
  2155. }
  2156. m_pTextureRegenerator = pTextureRegen;
  2157. }
  2158. //-----------------------------------------------------------------------------
  2159. // Gets us modifying a particular frame of our texture
  2160. //-----------------------------------------------------------------------------
  2161. void CTexture::Modify( int iFrame )
  2162. {
  2163. Assert( iFrame >= 0 && iFrame < m_nFrameCount );
  2164. Assert( HasBeenAllocated() );
  2165. g_pShaderAPI->ModifyTexture( m_pTextureHandles[iFrame] );
  2166. }
  2167. //-----------------------------------------------------------------------------
  2168. // Sets the texture clamping state on the currently modified frame
  2169. //-----------------------------------------------------------------------------
  2170. void CTexture::SetWrapState( )
  2171. {
  2172. // Border clamp applies to all texture coordinates
  2173. if ( m_nFlags & TEXTUREFLAGS_BORDER )
  2174. {
  2175. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_BORDER );
  2176. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_BORDER );
  2177. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_BORDER );
  2178. return;
  2179. }
  2180. // Clamp mode in S
  2181. if ( m_nFlags & TEXTUREFLAGS_CLAMPS )
  2182. {
  2183. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_CLAMP );
  2184. }
  2185. else
  2186. {
  2187. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_S, SHADER_TEXWRAPMODE_REPEAT );
  2188. }
  2189. // Clamp mode in T
  2190. if ( m_nFlags & TEXTUREFLAGS_CLAMPT )
  2191. {
  2192. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_CLAMP );
  2193. }
  2194. else
  2195. {
  2196. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_T, SHADER_TEXWRAPMODE_REPEAT );
  2197. }
  2198. // Clamp mode in U
  2199. if ( m_nFlags & TEXTUREFLAGS_CLAMPU )
  2200. {
  2201. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_CLAMP );
  2202. }
  2203. else
  2204. {
  2205. g_pShaderAPI->TexWrap( SHADER_TEXCOORD_U, SHADER_TEXWRAPMODE_REPEAT );
  2206. }
  2207. }
  2208. //-----------------------------------------------------------------------------
  2209. // Sets the texture filtering state on the currently modified frame
  2210. //-----------------------------------------------------------------------------
  2211. void CTexture::SetFilterState()
  2212. {
  2213. // Turns off filtering when we're point sampling
  2214. if( m_nFlags & TEXTUREFLAGS_POINTSAMPLE )
  2215. {
  2216. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_NEAREST );
  2217. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_NEAREST );
  2218. return;
  2219. }
  2220. // NOTE: config.bMipMapTextures and config.bFilterTextures is handled in ShaderAPIDX8
  2221. bool bEnableMipmapping = ( m_nFlags & TEXTUREFLAGS_NOMIP ) ? false : true;
  2222. if( !bEnableMipmapping )
  2223. {
  2224. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR );
  2225. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
  2226. return;
  2227. }
  2228. // Determing the filtering mode
  2229. bool bIsAnisotropic = false; bool bIsTrilinear = false;
  2230. if ( (g_config.m_nForceAnisotropicLevel > 1) && (HardwareConfig()->MaximumAnisotropicLevel() > 1) )
  2231. {
  2232. bIsAnisotropic = true;
  2233. }
  2234. else
  2235. {
  2236. bIsAnisotropic = (( m_nFlags & TEXTUREFLAGS_ANISOTROPIC ) != 0) && (HardwareConfig()->MaximumAnisotropicLevel() > 1);
  2237. bIsTrilinear = ( g_config.m_nForceAnisotropicLevel == 1 ) || ( ( m_nFlags & TEXTUREFLAGS_TRILINEAR ) != 0 );
  2238. }
  2239. if ( bIsAnisotropic )
  2240. {
  2241. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_ANISOTROPIC );
  2242. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_ANISOTROPIC );
  2243. }
  2244. else
  2245. {
  2246. if ( bIsTrilinear )
  2247. {
  2248. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR );
  2249. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
  2250. }
  2251. else
  2252. {
  2253. g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST );
  2254. g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR );
  2255. }
  2256. }
  2257. }
  2258. //-----------------------------------------------------------------------------
  2259. // Download bits main entry point!!
  2260. //-----------------------------------------------------------------------------
  2261. void CTexture::DownloadTexture( Rect_t *pRect, void *pSourceData, int nSourceDataSize )
  2262. {
  2263. // No downloading necessary if there's no graphics
  2264. if ( !g_pShaderDevice->IsUsingGraphics() )
  2265. return;
  2266. if ( m_nInternalFlags & ( TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE|TEXTUREFLAGSINTERNAL_TEMPEXCLUDED|TEXTUREFLAGSINTERNAL_TEMPEXCLUDE_UPDATE ) )
  2267. {
  2268. // temp exclusions are allowed to occur anytime during gameplay
  2269. // for expected stability, the ShaderAPITextureHandle_t must stay as-is
  2270. // store them off prior to their expected release, so they can be sent back down as a hint to the allocator for the expected re-allocation
  2271. // this allows the underlying d3d bits to be changed, but other system that have stored off the prior handles need not be aware
  2272. if ( m_nFrameCount > 0 )
  2273. {
  2274. m_pTempTextureHandles = new ShaderAPITextureHandle_t[m_nFrameCount];
  2275. for ( int i = 0; i != m_nFrameCount; ++i )
  2276. {
  2277. m_pTempTextureHandles[i] = m_pTextureHandles[i];
  2278. }
  2279. }
  2280. }
  2281. // We don't know the actual size of the texture at this stage...
  2282. if ( !pRect )
  2283. {
  2284. ReconstructTexture( pSourceData, nSourceDataSize );
  2285. }
  2286. else
  2287. {
  2288. ReconstructPartialTexture( pRect );
  2289. }
  2290. // Iterate over all the frames and set the appropriate wrapping + filtering state
  2291. SetFilteringAndClampingMode();
  2292. // texture bits have been updated, update the exclusion state
  2293. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE )
  2294. {
  2295. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_EXCLUDED;
  2296. }
  2297. else
  2298. {
  2299. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_EXCLUDED;
  2300. }
  2301. // texture bits have been picmipped, update the picmip state
  2302. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE )
  2303. {
  2304. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_TEMPEXCLUDED;
  2305. m_nActualDimensionLimit = m_nDesiredTempDimensionLimit;
  2306. }
  2307. else
  2308. {
  2309. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_TEMPEXCLUDED;
  2310. m_nActualDimensionLimit = m_nDesiredDimensionLimit;
  2311. }
  2312. // any possible temp exclude update is finished
  2313. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_TEMPEXCLUDE_UPDATE;
  2314. if ( m_pTempTextureHandles )
  2315. {
  2316. // check for handle stability
  2317. // the handles MUST stay the same across a ReconstructTexture(), various threaded systems have already cached/queued off the handles
  2318. for ( int i = 0; i < m_nFrameCount; i++ )
  2319. {
  2320. if ( m_pTextureHandles[i] != m_pTempTextureHandles[i] )
  2321. {
  2322. // crash will be imminent, the handles have changed and they should not have
  2323. // shaderapi will crash on next texture access because stored handles reference the freed texture handles, not the valid allocated ones
  2324. Assert( 0 );
  2325. Warning( "ERROR! - Crash Expected. DownloadTexture(): Texture Handle Difference: %d expected:0x%8.8x actual:0x%8.8x\n", i, m_pTempTextureHandles[i], m_pTextureHandles[i] );
  2326. }
  2327. }
  2328. delete[] m_pTempTextureHandles;
  2329. m_pTempTextureHandles = NULL;
  2330. }
  2331. }
  2332. //-----------------------------------------------------------------------------
  2333. // Download bits main entry point for async textures (based on CTexture::DownloadTexture)
  2334. // Very controlled environment: no procedural textures, no render target, console not supported
  2335. // The download is done is 2 parts:
  2336. // * Generating the VTF
  2337. // * Using VTF to create the shader api texture (effectively the corresponding d3d resource)
  2338. // In order to reduce spikes on the main thread (cf CMaterialSystem::ServiceAsyncTextureLoads), the flMaxTimeMs
  2339. // limit has been introduced => you can safely exit after generating the VTF and resume it at a later date
  2340. // Note that async textures are sharing the same scratch VTF therefore, if the download of an async texture
  2341. // has been interuped, it is important not to start downloading a new async texture (that would effectively invalidate
  2342. // the VTF of the other texture) - Done in CMaterialSystem::ServiceAsyncTextureLoads)
  2343. // Returns true if the download has been completed (ie interrupted after generating the VTF), false otherwise
  2344. //-----------------------------------------------------------------------------
  2345. bool CTexture::DownloadAsyncTexture( AsyncTextureContext_t *pContext, void *pSourceData, int nSourceDataSize, float flMaxTimeMs )
  2346. {
  2347. // No downloading necessary if there's no graphics
  2348. if (!g_pShaderDevice->IsUsingGraphics())
  2349. return true;
  2350. Assert( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD );
  2351. Assert( !IsGameConsole() );
  2352. Assert( !IsRenderTarget() );
  2353. Assert( !IsTempRenderTarget() );
  2354. Assert( !IsProcedural() );
  2355. if ( !pContext->m_pVTFTexture )
  2356. {
  2357. double flStartTime = Plat_FloatTime();
  2358. int oldWidth = m_nActualWidth;
  2359. int oldHeight = m_nActualHeight;
  2360. int oldDepth = m_nActualDepth;
  2361. int oldMipCount = m_nActualMipCount;
  2362. int oldFrameCount = m_nFrameCount;
  2363. pContext->m_pVTFTexture = LoadTexttureBitsFromFileOrData( pSourceData, nSourceDataSize, NULL );
  2364. if (!HasBeenAllocated() ||
  2365. m_nActualWidth != oldWidth ||
  2366. m_nActualHeight != oldHeight ||
  2367. m_nActualDepth != oldDepth ||
  2368. m_nActualMipCount != oldMipCount ||
  2369. m_nFrameCount != oldFrameCount)
  2370. {
  2371. if (HasBeenAllocated())
  2372. {
  2373. // This is necessary for the reload case, we may discover there
  2374. // are more frames of a texture animation, for example, which means
  2375. // we can't rely on having the same number of texture frames.
  2376. FreeShaderAPITextures();
  2377. }
  2378. // Create the shader api textures
  2379. if (!AllocateShaderAPITextures())
  2380. return true;
  2381. }
  2382. // Safe point to interrupt teh texture download
  2383. float flElapsedMs = (Plat_FloatTime() - flStartTime) * 1000.0f;
  2384. if (flElapsedMs > flMaxTimeMs)
  2385. {
  2386. // Running out of time - the shader api texture will be created later (most probably on the next frame)
  2387. return false;
  2388. }
  2389. }
  2390. // Blit down the texture faces, frames, and mips into the board memory
  2391. int nFirstFace, nFaceCount;
  2392. GetDownloadFaceCount( nFirstFace, nFaceCount );
  2393. WriteDataToShaderAPITexture( m_nFrameCount, nFaceCount, nFirstFace, m_nActualMipCount, pContext->m_pVTFTexture, m_ImageFormat );
  2394. // Iterate over all the frames and set the appropriate wrapping + filtering state
  2395. SetFilteringAndClampingMode();
  2396. pContext->m_pVTFTexture = NULL;
  2397. return true;
  2398. }
  2399. void CTexture::Download( Rect_t *pRect, int nAdditionalCreationFlags /* = 0 */ )
  2400. {
  2401. if ( nAdditionalCreationFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD )
  2402. {
  2403. m_nFlags |= nAdditionalCreationFlags;
  2404. if ( ScheduleAsyncDownload() )
  2405. {
  2406. return;
  2407. }
  2408. else
  2409. {
  2410. // failed to find file so remove async download flag
  2411. m_nFlags &= ~TEXTUREFLAGS_ASYNC_DOWNLOAD;
  2412. // and intentionally fall through to normal Download() which will use the error texture
  2413. }
  2414. }
  2415. if ( g_pShaderAPI->CanDownloadTextures() ) // Only download the bits if we can...
  2416. {
  2417. MaterialLock_t hLock = MaterialSystem()->Lock();
  2418. m_nFlags |= nAdditionalCreationFlags; // Path to let stdshaders drive settings like sRGB-ness at creation time
  2419. DownloadTexture( pRect );
  2420. MaterialSystem()->Unlock( hLock );
  2421. }
  2422. }
  2423. #ifdef _PS3
  2424. void CTexture::Ps3gcmRawBufferAlias( char const *pRTName )
  2425. {
  2426. ComputeActualSize( true );
  2427. m_nActualDimensionLimit = m_nDesiredDimensionLimit;
  2428. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_ALLOCATED;
  2429. extern ShaderAPITextureHandle_t Ps3gcmGetArtificialTextureHandle( int iHandle );
  2430. if ( !Q_strcmp( pRTName, "^PS3^BACKBUFFER" ) )
  2431. m_pTextureHandles[0] = Ps3gcmGetArtificialTextureHandle( PS3GCM_ARTIFICIAL_TEXTURE_HANDLE_INDEX_BACKBUFFER );
  2432. else if ( !Q_strcmp( pRTName, "^PS3^DEPTHBUFFER" ) )
  2433. m_pTextureHandles[0] = Ps3gcmGetArtificialTextureHandle( PS3GCM_ARTIFICIAL_TEXTURE_HANDLE_INDEX_DEPTHBUFFER );
  2434. else
  2435. Error( "<vitaliy> Unexpected raw buffer alias: %s!\n", pRTName );
  2436. }
  2437. #endif
  2438. void CTexture::Bind( Sampler_t sampler, TextureBindFlags_t nBindFlags )
  2439. {
  2440. Bind( sampler, nBindFlags, 0 );
  2441. }
  2442. //-----------------------------------------------------------------------------
  2443. // Binds a particular texture
  2444. //-----------------------------------------------------------------------------
  2445. void CTexture::Bind( Sampler_t sampler1, TextureBindFlags_t nBindFlags, int nFrame, Sampler_t sampler2 /* = -1 */ )
  2446. {
  2447. if ( g_pShaderDevice->IsUsingGraphics() )
  2448. {
  2449. if ( nFrame < 0 || nFrame >= m_nFrameCount )
  2450. {
  2451. // FIXME: Use the well-known 'error' id instead of frame 0
  2452. nFrame = 0;
  2453. // Assert(0);
  2454. }
  2455. // Make sure we've actually allocated the texture handle
  2456. if ( HasBeenAllocated() )
  2457. {
  2458. g_pShaderAPI->BindTexture( sampler1, nBindFlags, m_pTextureHandles[nFrame] );
  2459. }
  2460. else
  2461. {
  2462. Warning( "Trying to bind texture %s, but texture handles are not valid. Binding a white texture!", GetName() );
  2463. g_pShaderAPI->BindStandardTexture( sampler1, nBindFlags, TEXTURE_WHITE );
  2464. }
  2465. }
  2466. }
  2467. void CTexture::BindVertexTexture( VertexTextureSampler_t sampler, int nFrame )
  2468. {
  2469. if ( g_pShaderDevice->IsUsingGraphics() )
  2470. {
  2471. if ( nFrame < 0 || nFrame >= m_nFrameCount )
  2472. {
  2473. // FIXME: Use the well-known 'error' id instead of frame 0
  2474. nFrame = 0;
  2475. // Assert(0);
  2476. }
  2477. // Make sure we've actually allocated the texture
  2478. Assert( HasBeenAllocated() );
  2479. g_pShaderAPI->BindVertexTexture( sampler, m_pTextureHandles[nFrame] );
  2480. }
  2481. }
  2482. //-----------------------------------------------------------------------------
  2483. // Set this texture as a render target
  2484. //-----------------------------------------------------------------------------
  2485. bool CTexture::SetRenderTarget( int nRenderTargetID )
  2486. {
  2487. return SetRenderTarget( nRenderTargetID, NULL );
  2488. }
  2489. //-----------------------------------------------------------------------------
  2490. // Set this texture as a render target
  2491. // Optionally bind pDepthTexture as depth buffer
  2492. //-----------------------------------------------------------------------------
  2493. bool CTexture::SetRenderTarget( int nRenderTargetID, ITexture *pDepthTexture )
  2494. {
  2495. if ( ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) == 0 )
  2496. return false;
  2497. // Make sure we've actually allocated the texture handles
  2498. Assert( HasBeenAllocated() );
  2499. ShaderAPITextureHandle_t textureHandle;
  2500. if ( !IsX360() )
  2501. {
  2502. textureHandle = m_pTextureHandles[0];
  2503. }
  2504. else
  2505. {
  2506. Assert( m_nFrameCount > 1 );
  2507. textureHandle = m_pTextureHandles[m_nFrameCount-1];
  2508. }
  2509. ShaderAPITextureHandle_t depthTextureHandle = (ShaderAPITextureHandle_t)SHADER_RENDERTARGET_DEPTHBUFFER;
  2510. if ( m_nFlags & TEXTUREFLAGS_DEPTHRENDERTARGET )
  2511. {
  2512. Assert( m_nFrameCount >= 2 );
  2513. depthTextureHandle = m_pTextureHandles[1];
  2514. }
  2515. else if ( m_nFlags & TEXTUREFLAGS_NODEPTHBUFFER )
  2516. {
  2517. // GR - render target without depth buffer
  2518. depthTextureHandle = (ShaderAPITextureHandle_t)SHADER_RENDERTARGET_NONE;
  2519. }
  2520. if ( pDepthTexture)
  2521. {
  2522. depthTextureHandle = static_cast<ITextureInternal *>(pDepthTexture)->GetTextureHandle(0);
  2523. }
  2524. g_pShaderAPI->SetRenderTargetEx( nRenderTargetID, textureHandle, depthTextureHandle );
  2525. return true;
  2526. }
  2527. //-----------------------------------------------------------------------------
  2528. // Reference counting
  2529. //-----------------------------------------------------------------------------
  2530. void CTexture::IncrementReferenceCount( void )
  2531. {
  2532. ++m_nRefCount;
  2533. }
  2534. void CTexture::DecrementReferenceCount( void )
  2535. {
  2536. --m_nRefCount;
  2537. /* FIXME: Probably have to remove this from the texture manager too..?
  2538. if (IsProcedural() && (m_nRefCount < 0))
  2539. delete this;
  2540. */
  2541. }
  2542. int CTexture::GetReferenceCount() const
  2543. {
  2544. return m_nRefCount;
  2545. }
  2546. //-----------------------------------------------------------------------------
  2547. // Various accessor methods
  2548. //-----------------------------------------------------------------------------
  2549. const char* CTexture::GetName( ) const
  2550. {
  2551. return m_Name.String();
  2552. }
  2553. const char* CTexture::GetTextureGroupName( ) const
  2554. {
  2555. return m_TextureGroupName.String();
  2556. }
  2557. void CTexture::SetName( const char* pName )
  2558. {
  2559. // normalize and convert to a symbol
  2560. char szCleanName[MAX_PATH];
  2561. m_Name = NormalizeTextureName( pName, szCleanName, sizeof( szCleanName ) );
  2562. #ifdef _DEBUG
  2563. if ( m_pDebugName )
  2564. {
  2565. delete [] m_pDebugName;
  2566. }
  2567. int nLen = V_strlen( szCleanName ) + 1;
  2568. m_pDebugName = new char[nLen];
  2569. V_memcpy( m_pDebugName, szCleanName, nLen );
  2570. #endif
  2571. }
  2572. ImageFormat CTexture::GetImageFormat() const
  2573. {
  2574. return m_ImageFormat;
  2575. }
  2576. int CTexture::GetMappingWidth() const
  2577. {
  2578. return m_nMappingWidth;
  2579. }
  2580. int CTexture::GetMappingHeight() const
  2581. {
  2582. return m_nMappingHeight;
  2583. }
  2584. int CTexture::GetMappingDepth() const
  2585. {
  2586. return m_nMappingDepth;
  2587. }
  2588. int CTexture::GetActualWidth() const
  2589. {
  2590. return m_nActualWidth;
  2591. }
  2592. int CTexture::GetActualHeight() const
  2593. {
  2594. return m_nActualHeight;
  2595. }
  2596. int CTexture::GetActualDepth() const
  2597. {
  2598. return m_nActualDepth;
  2599. }
  2600. int CTexture::GetNumAnimationFrames() const
  2601. {
  2602. return m_nFrameCount;
  2603. }
  2604. void CTexture::GetReflectivity( Vector& reflectivity )
  2605. {
  2606. Precache();
  2607. VectorCopy( m_vecReflectivity, reflectivity );
  2608. }
  2609. //-----------------------------------------------------------------------------
  2610. // Little helper polling methods
  2611. //-----------------------------------------------------------------------------
  2612. bool CTexture::IsTranslucent() const
  2613. {
  2614. return ( m_nFlags & (TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA) ) != 0;
  2615. }
  2616. bool CTexture::IsNormalMap( void ) const
  2617. {
  2618. return ( ( m_nFlags & TEXTUREFLAGS_NORMAL ) != 0 );
  2619. }
  2620. bool CTexture::IsCubeMap( void ) const
  2621. {
  2622. return ( ( m_nFlags & TEXTUREFLAGS_ENVMAP ) != 0 );
  2623. }
  2624. bool CTexture::IsRenderTarget( void ) const
  2625. {
  2626. return ( ( m_nFlags & TEXTUREFLAGS_RENDERTARGET ) != 0 );
  2627. }
  2628. bool CTexture::IsTempRenderTarget( void ) const
  2629. {
  2630. return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET ) != 0 );
  2631. }
  2632. bool CTexture::IsProcedural() const
  2633. {
  2634. return ( (m_nFlags & TEXTUREFLAGS_PROCEDURAL) != 0 );
  2635. }
  2636. bool CTexture::IsMipmapped() const
  2637. {
  2638. return ( (m_nFlags & TEXTUREFLAGS_NOMIP) == 0 );
  2639. }
  2640. unsigned int CTexture::GetFlags() const
  2641. {
  2642. return m_nFlags;
  2643. }
  2644. void CTexture::ForceLODOverride( int iNumLodsOverrideUpOrDown )
  2645. {
  2646. if ( IsGameConsole() )
  2647. {
  2648. // not supporting
  2649. Assert( 0 );
  2650. return;
  2651. }
  2652. TextureLodOverride::OverrideInfo oi( iNumLodsOverrideUpOrDown, iNumLodsOverrideUpOrDown );
  2653. TextureLodOverride::Add( GetName(), oi );
  2654. Download( NULL );
  2655. }
  2656. void CTexture::ForceExcludeOverride( int iExcludeOverride )
  2657. {
  2658. if ( IsGameConsole() )
  2659. {
  2660. Assert( 0 );
  2661. return;
  2662. }
  2663. TextureLodExclude::Add( GetName(), iExcludeOverride );
  2664. Download( NULL );
  2665. }
  2666. bool CTexture::IsError() const
  2667. {
  2668. return ( (m_nInternalFlags & TEXTUREFLAGSINTERNAL_ERROR) != 0 );
  2669. }
  2670. bool CTexture::IsDefaultPool() const
  2671. {
  2672. return ( ( m_nFlags & TEXTUREFLAGS_DEFAULT_POOL ) != 0 );
  2673. }
  2674. bool CTexture::HasBeenAllocated() const
  2675. {
  2676. return ( (m_nInternalFlags & TEXTUREFLAGSINTERNAL_ALLOCATED) != 0 );
  2677. }
  2678. bool CTexture::IsVolumeTexture() const
  2679. {
  2680. return (m_nMappingDepth > 1);
  2681. }
  2682. //-----------------------------------------------------------------------------
  2683. // Sets the filtering + clamping modes on the texture
  2684. //-----------------------------------------------------------------------------
  2685. void CTexture::SetFilteringAndClampingMode()
  2686. {
  2687. if( !HasBeenAllocated() )
  2688. return;
  2689. int nCount = m_nFrameCount;
  2690. if ( IsX360() && IsRenderTarget() )
  2691. {
  2692. // 360 render targets have a reserved surface
  2693. nCount--;
  2694. }
  2695. for ( int iFrame = 0; iFrame < nCount; ++iFrame )
  2696. {
  2697. Modify( iFrame ); // Indicate we're changing state with respect to a particular frame
  2698. SetWrapState(); // Send the appropriate wrap/clamping modes to the shaderapi.
  2699. SetFilterState(); // Set the filtering mode for the texture after downloading it.
  2700. // NOTE: Apparently, the filter state cannot be set until after download
  2701. }
  2702. }
  2703. //-----------------------------------------------------------------------------
  2704. // Loads up the non-fallback information about the texture
  2705. //-----------------------------------------------------------------------------
  2706. void CTexture::Precache()
  2707. {
  2708. int nHackExtraFlags = 0;
  2709. // We only have to do something in the case of a file texture
  2710. if ( IsRenderTarget() || IsProcedural() )
  2711. return;
  2712. if ( HasBeenAllocated() )
  2713. return;
  2714. // Blow off env_cubemap too...
  2715. if ( !Q_strnicmp( m_Name.String(), "env_cubemap", 12 ))
  2716. return;
  2717. if ( IsGameConsole() && m_nFlags )
  2718. {
  2719. // 360 can be assured that precaching has already been done
  2720. return;
  2721. }
  2722. IVTFTexture *pVTFTexture = GetScratchVTFTexture();
  2723. // The texture name doubles as the relative file name
  2724. // It's assumed to have already been set by this point
  2725. // Compute the cache name
  2726. char pCacheFileName[MATERIAL_MAX_PATH];
  2727. Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "materials/%s" TEXTURE_FNAME_EXTENSION, m_Name.String() );
  2728. #if defined( _GAMECONSOLE )
  2729. // generate native texture
  2730. pVTFTexture->UpdateOrCreate( pCacheFileName );
  2731. #endif
  2732. int nVersion = -1;
  2733. if ( IsPC() )
  2734. nVersion = VTF_MAJOR_VERSION;
  2735. else if ( IsX360() )
  2736. nVersion = VTF_X360_MAJOR_VERSION;
  2737. else if ( IsPS3() )
  2738. nVersion = VTF_PS3_MAJOR_VERSION;
  2739. int nHeaderSize = VTFFileHeaderSize( nVersion );
  2740. unsigned char *pMem = (unsigned char *)stackalloc( nHeaderSize );
  2741. CUtlBuffer buf( pMem, nHeaderSize );
  2742. if ( !g_pFullFileSystem->ReadFile( pCacheFileName, NULL, buf, nHeaderSize ) )
  2743. {
  2744. goto precacheFailed;
  2745. }
  2746. // Unserialize the header only
  2747. #if !defined( _GAMECONSOLE )
  2748. if ( !pVTFTexture->Unserialize( buf, true ) )
  2749. #else
  2750. if ( !pVTFTexture->UnserializeFromBuffer( buf, true, true, true, 0 ) )
  2751. #endif
  2752. {
  2753. Warning( "Error reading material \"%s\"\n", pCacheFileName );
  2754. goto precacheFailed;
  2755. }
  2756. // FIXME: Hack for L4D
  2757. if ( !Q_strnicmp( pCacheFileName, "materials/graffiti/", 19 ) )
  2758. {
  2759. nHackExtraFlags = TEXTUREFLAGS_NOLOD;
  2760. }
  2761. // NOTE: Don't set the image format in case graphics are active
  2762. VectorCopy( pVTFTexture->Reflectivity(), m_vecReflectivity );
  2763. m_nMappingWidth = pVTFTexture->Width();
  2764. m_nMappingHeight = pVTFTexture->Height();
  2765. m_nMappingDepth = pVTFTexture->Depth();
  2766. m_nFlags = pVTFTexture->Flags() | nHackExtraFlags;
  2767. m_nFrameCount = pVTFTexture->FrameCount();
  2768. return;
  2769. precacheFailed:
  2770. m_vecReflectivity.Init( 0, 0, 0 );
  2771. m_nMappingWidth = 32;
  2772. m_nMappingHeight = 32;
  2773. m_nMappingDepth = 1;
  2774. m_nFlags = TEXTUREFLAGS_NOMIP;
  2775. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_ERROR;
  2776. m_nFrameCount = 1;
  2777. }
  2778. //-----------------------------------------------------------------------------
  2779. // Loads the low-res image from the texture
  2780. //-----------------------------------------------------------------------------
  2781. void CTexture::LoadLowResTexture( IVTFTexture *pTexture )
  2782. {
  2783. #if !defined( _GAMECONSOLE )
  2784. delete [] m_pLowResImage;
  2785. m_pLowResImage = NULL;
  2786. #endif
  2787. if ( pTexture->LowResWidth() == 0 || pTexture->LowResHeight() == 0 )
  2788. {
  2789. m_LowResImageWidth = m_LowResImageHeight = 0;
  2790. return;
  2791. }
  2792. m_LowResImageWidth = pTexture->LowResWidth();
  2793. m_LowResImageHeight = pTexture->LowResHeight();
  2794. #if !defined( _GAMECONSOLE )
  2795. m_pLowResImage = new unsigned char[m_LowResImageWidth * m_LowResImageHeight * 3];
  2796. #ifdef _DEBUG
  2797. bool retVal =
  2798. #endif
  2799. ImageLoader::ConvertImageFormat( pTexture->LowResImageData(), pTexture->LowResFormat(),
  2800. m_pLowResImage, IMAGE_FORMAT_RGB888, m_LowResImageWidth, m_LowResImageHeight );
  2801. #ifdef _DEBUG
  2802. Assert( retVal );
  2803. #endif
  2804. #else
  2805. *(unsigned int*)m_LowResImageSample = *(unsigned int*)pTexture->LowResImageSample();
  2806. #endif
  2807. }
  2808. void *CTexture::GetResourceData( uint32 eDataType, size_t *pnumBytes ) const
  2809. {
  2810. for ( DataChunk const *pDataChunk = m_arrDataChunks.Base(),
  2811. *pDataChunkEnd = pDataChunk + m_arrDataChunks.Count();
  2812. pDataChunk < pDataChunkEnd; ++pDataChunk )
  2813. {
  2814. if ( ( pDataChunk->m_eType & ~RSRCF_MASK ) == eDataType )
  2815. {
  2816. if ( ( pDataChunk->m_eType & RSRCF_HAS_NO_DATA_CHUNK ) == 0 )
  2817. {
  2818. if ( pnumBytes)
  2819. *pnumBytes = pDataChunk->m_numBytes;
  2820. return pDataChunk->m_pvData;
  2821. }
  2822. else
  2823. {
  2824. if ( pnumBytes )
  2825. *pnumBytes = sizeof( pDataChunk->m_numBytes );
  2826. return ( void *)( &pDataChunk->m_numBytes );
  2827. }
  2828. }
  2829. }
  2830. if ( pnumBytes )
  2831. pnumBytes = 0;
  2832. return NULL;
  2833. }
  2834. void CTexture::FreeResourceData()
  2835. {
  2836. // Clean up the resources data
  2837. for ( DataChunk const *pDataChunk = m_arrDataChunks.Base(),
  2838. *pDataChunkEnd = pDataChunk + m_arrDataChunks.Count();
  2839. pDataChunk < pDataChunkEnd; ++pDataChunk )
  2840. {
  2841. pDataChunk->Deallocate();
  2842. }
  2843. m_arrDataChunks.RemoveAll();
  2844. }
  2845. void CTexture::LoadResourceData( IVTFTexture *pVTFTexture )
  2846. {
  2847. // purge any prior resource data
  2848. FreeResourceData();
  2849. // Load the resources
  2850. if ( unsigned int uiRsrcCount = pVTFTexture->GetResourceTypes( NULL, 0 ) )
  2851. {
  2852. uint32 *arrRsrcTypes = ( uint32 * )stackalloc( uiRsrcCount * sizeof( unsigned int ) );
  2853. pVTFTexture->GetResourceTypes( arrRsrcTypes, uiRsrcCount );
  2854. m_arrDataChunks.EnsureCapacity( uiRsrcCount );
  2855. for ( uint32 *arrRsrcTypesEnd = arrRsrcTypes + uiRsrcCount;
  2856. arrRsrcTypes < arrRsrcTypesEnd; ++arrRsrcTypes )
  2857. {
  2858. switch ( *arrRsrcTypes )
  2859. {
  2860. case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
  2861. case VTF_LEGACY_RSRC_IMAGE:
  2862. // These stock types use specific load routines
  2863. continue;
  2864. default:
  2865. {
  2866. DataChunk dc;
  2867. dc.m_eType = *arrRsrcTypes;
  2868. dc.m_eType &= ~RSRCF_MASK;
  2869. size_t numBytes;
  2870. if ( void *pvData = pVTFTexture->GetResourceData( dc.m_eType, &numBytes ) )
  2871. {
  2872. Assert( numBytes >= sizeof( uint32 ) );
  2873. if ( numBytes == sizeof( dc.m_numBytes ) )
  2874. {
  2875. dc.m_eType |= RSRCF_HAS_NO_DATA_CHUNK;
  2876. dc.m_pvData = NULL;
  2877. memcpy( &dc.m_numBytes, pvData, numBytes );
  2878. }
  2879. else
  2880. {
  2881. dc.Allocate( numBytes );
  2882. memcpy( dc.m_pvData, pvData, numBytes );
  2883. }
  2884. m_arrDataChunks.AddToTail( dc );
  2885. }
  2886. }
  2887. }
  2888. }
  2889. }
  2890. }
  2891. #pragma pack(1)
  2892. struct DXTColBlock
  2893. {
  2894. unsigned short col0;
  2895. unsigned short col1;
  2896. // no bit fields - use bytes
  2897. unsigned char row[4];
  2898. };
  2899. struct DXTAlphaBlock3BitLinear
  2900. {
  2901. unsigned char alpha0;
  2902. unsigned char alpha1;
  2903. unsigned char stuff[6];
  2904. };
  2905. #pragma pack()
  2906. static void FillCompressedTextureWithSingleColor( int red, int green, int blue, int alpha, unsigned char *pImageData,
  2907. int width, int height, int depth, ImageFormat imageFormat )
  2908. {
  2909. Assert( ( width < 4 ) || !( width % 4 ) );
  2910. Assert( ( height < 4 ) || !( height % 4 ) );
  2911. Assert( ( depth < 4 ) || !( depth % 4 ) );
  2912. if ( width < 4 && width > 0 )
  2913. {
  2914. width = 4;
  2915. }
  2916. if ( height < 4 && height > 0 )
  2917. {
  2918. height = 4;
  2919. }
  2920. if ( depth < 4 && depth > 1 )
  2921. {
  2922. depth = 4;
  2923. }
  2924. int numBlocks = ( width * height ) >> 4;
  2925. numBlocks *= depth;
  2926. DXTColBlock colorBlock;
  2927. memset( &colorBlock, 0, sizeof( colorBlock ) );
  2928. ( ( BGR565_t * )&( colorBlock.col0 ) )->Set( red, green, blue );
  2929. ( ( BGR565_t * )&( colorBlock.col1 ) )->Set( red, green, blue );
  2930. switch( imageFormat )
  2931. {
  2932. case IMAGE_FORMAT_DXT1:
  2933. case IMAGE_FORMAT_ATI1N: // Invalid block data, but correct memory footprint
  2934. {
  2935. int i;
  2936. for( i = 0; i < numBlocks; i++ )
  2937. {
  2938. memcpy( pImageData + i * 8, &colorBlock, sizeof( colorBlock ) );
  2939. }
  2940. }
  2941. break;
  2942. case IMAGE_FORMAT_DXT5:
  2943. case IMAGE_FORMAT_ATI2N:
  2944. {
  2945. int i;
  2946. for( i = 0; i < numBlocks; i++ )
  2947. {
  2948. // memset( pImageData + i * 16, 0, 16 );
  2949. memcpy( pImageData + i * 16 + 8, &colorBlock, sizeof( colorBlock ) );
  2950. // memset( pImageData + i * 16 + 8, 0xffff, 8 ); // alpha block
  2951. }
  2952. }
  2953. break;
  2954. default:
  2955. Assert( 0 );
  2956. break;
  2957. }
  2958. }
  2959. //-----------------------------------------------------------------------------
  2960. // Generate a gray texture
  2961. //-----------------------------------------------------------------------------
  2962. void CTexture::GenerateGrayTexture( IVTFTexture *pTexture )
  2963. {
  2964. if( pTexture->FaceCount() > 1 )
  2965. return;
  2966. if( pTexture->IsCubeMap() )
  2967. return;
  2968. switch( pTexture->Format() )
  2969. {
  2970. // These are formats that we don't bother with
  2971. case IMAGE_FORMAT_RGBA16161616F:
  2972. case IMAGE_FORMAT_R32F:
  2973. case IMAGE_FORMAT_RGB323232F:
  2974. case IMAGE_FORMAT_RGBA32323232F:
  2975. case IMAGE_FORMAT_UV88:
  2976. break;
  2977. default:
  2978. for (int iFrame = 0; iFrame < pTexture->FrameCount(); ++iFrame )
  2979. {
  2980. for (int iFace = 0; iFace < pTexture->FaceCount(); ++iFace )
  2981. {
  2982. for (int iMip = 0; iMip < pTexture->MipCount(); ++iMip )
  2983. {
  2984. int green = 128;
  2985. int red = 128;
  2986. int blue = 128;
  2987. int nWidth, nHeight, nDepth;
  2988. pTexture->ComputeMipLevelDimensions( iMip, &nWidth, &nHeight, &nDepth );
  2989. if( pTexture->Format() == IMAGE_FORMAT_DXT1 || pTexture->Format() == IMAGE_FORMAT_DXT5 ||
  2990. pTexture->Format() == IMAGE_FORMAT_ATI1N || pTexture->Format() == IMAGE_FORMAT_ATI2N )
  2991. {
  2992. unsigned char *pImageData = pTexture->ImageData( iFrame, iFace, iMip, 0, 0, 0 );
  2993. FillCompressedTextureWithSingleColor( red, green, blue, 255, pImageData, nWidth, nHeight, nDepth, pTexture->Format() );
  2994. }
  2995. else
  2996. {
  2997. for ( int z = 0; z < nDepth; ++z )
  2998. {
  2999. CPixelWriter pixelWriter;
  3000. pixelWriter.SetPixelMemory( pTexture->Format(),
  3001. pTexture->ImageData( iFrame, iFace, iMip, 0, 0, z ), pTexture->RowSizeInBytes( iMip ) );
  3002. for (int y = 0; y < nHeight; ++y)
  3003. {
  3004. pixelWriter.Seek( 0, y );
  3005. for (int x = 0; x < nWidth; ++x)
  3006. {
  3007. pixelWriter.WritePixel( red, green, blue, 255 );
  3008. }
  3009. }
  3010. }
  3011. }
  3012. }
  3013. }
  3014. }
  3015. break;
  3016. }
  3017. }
  3018. //-----------------------------------------------------------------------------
  3019. // Generate a texture that shows the various mip levels
  3020. //-----------------------------------------------------------------------------
  3021. void CTexture::GenerateShowMipLevelsTextures( IVTFTexture *pTexture )
  3022. {
  3023. if( pTexture->FaceCount() > 1 )
  3024. return;
  3025. switch( pTexture->Format() )
  3026. {
  3027. // These are formats that we don't bother with for generating mip level textures.
  3028. case IMAGE_FORMAT_RGBA16161616F:
  3029. case IMAGE_FORMAT_R32F:
  3030. case IMAGE_FORMAT_RGB323232F:
  3031. case IMAGE_FORMAT_RGBA32323232F:
  3032. case IMAGE_FORMAT_UV88:
  3033. break;
  3034. default:
  3035. for (int iFrame = 0; iFrame < pTexture->FrameCount(); ++iFrame )
  3036. {
  3037. for (int iFace = 0; iFace < pTexture->FaceCount(); ++iFace )
  3038. {
  3039. for (int iMip = 0; iMip < pTexture->MipCount(); ++iMip )
  3040. {
  3041. int green = ( ( iMip + 1 ) & 1 ) ? 255 : 0;
  3042. int red = ( ( iMip + 1 ) & 2 ) ? 255 : 0;
  3043. int blue = ( ( iMip + 1 ) & 4 ) ? 255 : 0;
  3044. int nWidth, nHeight, nDepth;
  3045. pTexture->ComputeMipLevelDimensions( iMip, &nWidth, &nHeight, &nDepth );
  3046. if( pTexture->Format() == IMAGE_FORMAT_DXT1 || pTexture->Format() == IMAGE_FORMAT_DXT5 ||
  3047. pTexture->Format() == IMAGE_FORMAT_ATI1N || pTexture->Format() == IMAGE_FORMAT_ATI2N )
  3048. {
  3049. unsigned char *pImageData = pTexture->ImageData( iFrame, iFace, iMip, 0, 0, 0 );
  3050. int alpha = 255;
  3051. FillCompressedTextureWithSingleColor( red, green, blue, alpha, pImageData, nWidth, nHeight, nDepth, pTexture->Format() );
  3052. }
  3053. else
  3054. {
  3055. for ( int z = 0; z < nDepth; ++z )
  3056. {
  3057. CPixelWriter pixelWriter;
  3058. pixelWriter.SetPixelMemory( pTexture->Format(),
  3059. pTexture->ImageData( iFrame, iFace, iMip, 0, 0, z ), pTexture->RowSizeInBytes( iMip ) );
  3060. for (int y = 0; y < nHeight; ++y)
  3061. {
  3062. pixelWriter.Seek( 0, y );
  3063. for (int x = 0; x < nWidth; ++x)
  3064. {
  3065. pixelWriter.WritePixel( red, green, blue, 255 );
  3066. }
  3067. }
  3068. }
  3069. }
  3070. }
  3071. }
  3072. }
  3073. break;
  3074. }
  3075. }
  3076. //-----------------------------------------------------------------------------
  3077. // Generate a texture that shows the various mip levels
  3078. //-----------------------------------------------------------------------------
  3079. void CTexture::CopyLowResImageToTexture( IVTFTexture *pTexture )
  3080. {
  3081. int nFlags = pTexture->Flags();
  3082. nFlags |= TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_POINTSAMPLE;
  3083. nFlags &= ~(TEXTUREFLAGS_TRILINEAR | TEXTUREFLAGS_ANISOTROPIC | TEXTUREFLAGS_HINT_DXT5);
  3084. nFlags &= ~(TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_ENVMAP);
  3085. nFlags &= ~(TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA);
  3086. Assert( pTexture->FrameCount() == 1 );
  3087. Init( pTexture->Width(), pTexture->Height(), 1, IMAGE_FORMAT_BGR888, nFlags, 1 );
  3088. pTexture->Init( m_LowResImageWidth, m_LowResImageHeight, 1, IMAGE_FORMAT_BGR888, nFlags, 1 );
  3089. // Don't bother computing the actual size; it's actually equal to the low-res size
  3090. // With only one mip level
  3091. m_nActualWidth = m_LowResImageWidth;
  3092. m_nActualHeight = m_LowResImageHeight;
  3093. m_nActualDepth = 1;
  3094. m_nActualMipCount = 1;
  3095. // Copy the row-res image into the VTF Texture
  3096. CPixelWriter pixelWriter;
  3097. pixelWriter.SetPixelMemory( pTexture->Format(),
  3098. pTexture->ImageData( 0, 0, 0 ), pTexture->RowSizeInBytes( 0 ) );
  3099. #if !defined( _GAMECONSOLE )
  3100. unsigned char *pLowResImage = m_pLowResImage;
  3101. #else
  3102. unsigned char *pLowResImage = m_LowResImageSample;
  3103. #endif
  3104. for ( int y = 0; y < m_LowResImageHeight; ++y )
  3105. {
  3106. pixelWriter.Seek( 0, y );
  3107. for ( int x = 0; x < m_LowResImageWidth; ++x )
  3108. {
  3109. int red = pLowResImage[0];
  3110. int green = pLowResImage[1];
  3111. int blue = pLowResImage[2];
  3112. pLowResImage += 3;
  3113. pixelWriter.WritePixel( red, green, blue, 255 );
  3114. }
  3115. }
  3116. }
  3117. //-----------------------------------------------------------------------------
  3118. // Sets up debugging texture bits, if appropriate
  3119. //-----------------------------------------------------------------------------
  3120. bool CTexture::SetupDebuggingTextures( IVTFTexture *pVTFTexture )
  3121. {
  3122. if ( IsGameConsole() )
  3123. {
  3124. // not supporting
  3125. return false;
  3126. }
  3127. if ( pVTFTexture->Flags() & TEXTUREFLAGS_NODEBUGOVERRIDE )
  3128. return false;
  3129. if ( g_config.bDrawGray )
  3130. {
  3131. GenerateGrayTexture( pVTFTexture );
  3132. return true;
  3133. }
  3134. if ( g_config.nShowMipLevels )
  3135. {
  3136. // mat_showmiplevels 1 means don't do normal maps
  3137. if ( ( g_config.nShowMipLevels == 1 ) && ( pVTFTexture->Flags() & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_SSBUMP ) ) )
  3138. return false;
  3139. // mat_showmiplevels 2 means don't do base textures
  3140. if ( ( g_config.nShowMipLevels == 2 ) && !( pVTFTexture->Flags() & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_SSBUMP ) ) )
  3141. return false;
  3142. // This mode shows the mip levels as different colors
  3143. GenerateShowMipLevelsTextures( pVTFTexture );
  3144. return true;
  3145. }
  3146. else if ( g_config.bShowLowResImage && pVTFTexture->FrameCount() == 1 &&
  3147. pVTFTexture->FaceCount() == 1 && ((pVTFTexture->Flags() & TEXTUREFLAGS_NORMAL) == 0) &&
  3148. m_LowResImageWidth != 0 && m_LowResImageHeight != 0 )
  3149. {
  3150. // This mode just uses the low res texture
  3151. CopyLowResImageToTexture( pVTFTexture );
  3152. return true;
  3153. }
  3154. return false;
  3155. }
  3156. //-----------------------------------------------------------------------------
  3157. // Converts the texture to the actual format
  3158. // Returns true if conversion applied, false otherwise
  3159. //-----------------------------------------------------------------------------
  3160. bool CTexture::ConvertToActualFormat( IVTFTexture *pVTFTexture )
  3161. {
  3162. if ( !g_pShaderDevice->IsUsingGraphics() )
  3163. return false;
  3164. bool bConverted = false;
  3165. ImageFormat fmt = m_ImageFormat;
  3166. ImageFormat dstFormat = ComputeActualFormat( pVTFTexture->Format() );
  3167. #ifdef PLATFORM_OSX
  3168. if ( IsVolumeTexture() && ImageLoader::IsCompressed( dstFormat ) )
  3169. {
  3170. // OSX does not support compressed 3d textures
  3171. dstFormat = IMAGE_FORMAT_RGBA8888;
  3172. }
  3173. #endif
  3174. if ( fmt != dstFormat )
  3175. {
  3176. Assert( !IsGameConsole() );
  3177. pVTFTexture->ConvertImageFormat( dstFormat, false );
  3178. m_ImageFormat = dstFormat;
  3179. bConverted = true;
  3180. }
  3181. #ifndef _PS3
  3182. // No reason to do this conversion on PS3
  3183. else if ( HardwareConfig()->GetHDRType() == HDR_TYPE_INTEGER &&
  3184. fmt == dstFormat && dstFormat == IMAGE_FORMAT_RGBA16161616F )
  3185. {
  3186. // This is to force at most the precision of int16 for fp16 texture when running the integer path.
  3187. pVTFTexture->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616, false );
  3188. pVTFTexture->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
  3189. bConverted = true;
  3190. }
  3191. #endif // !_PS3
  3192. return bConverted;
  3193. }
  3194. void CTexture::GetFilename( char *pOut, int maxLen ) const
  3195. {
  3196. const char *pName = m_Name.String();
  3197. bool bIsUNCName = ( pName[0] == '/' && pName[1] == '/' && pName[2] != '/' );
  3198. if ( !bIsUNCName )
  3199. {
  3200. Q_snprintf( pOut, maxLen,
  3201. "materials/%s" TEXTURE_FNAME_EXTENSION, pName );
  3202. }
  3203. else
  3204. {
  3205. Q_snprintf( pOut, maxLen, "%s" TEXTURE_FNAME_EXTENSION, pName );
  3206. }
  3207. }
  3208. void CTexture::ReloadFilesInList( IFileList *pFilesToReload )
  3209. {
  3210. if ( IsProcedural() || IsRenderTarget() )
  3211. return;
  3212. char filename[MAX_PATH];
  3213. GetFilename( filename, sizeof( filename ) );
  3214. if ( pFilesToReload->IsFileInList( filename ) )
  3215. {
  3216. Download();
  3217. }
  3218. }
  3219. //-----------------------------------------------------------------------------
  3220. // Loads the texture bits from a file or data.
  3221. //-----------------------------------------------------------------------------
  3222. IVTFTexture *CTexture::LoadTexttureBitsFromFileOrData( void *pSourceData, int nSourceDataSize, char **pResolvedFilename )
  3223. {
  3224. char pCacheFileName[MATERIAL_MAX_PATH] = { 0 };
  3225. const char *pName;
  3226. if (m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE)
  3227. {
  3228. #if !defined( _CERT )
  3229. // excluded texture should not be visible, want these to be found during testing
  3230. // use the green checkerboard
  3231. pName = "dev/dev_exclude_error";
  3232. #else
  3233. // for shipping (in case it happens) better to use the version meant for momentary rendering
  3234. pName = "dev/dev_temp_exclude";
  3235. #endif
  3236. }
  3237. else if ((m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE) && m_nDesiredTempDimensionLimit <= 0)
  3238. {
  3239. pName = "dev/dev_temp_exclude";
  3240. }
  3241. else
  3242. {
  3243. pName = m_Name.String();
  3244. }
  3245. bool bIsUNCName = (pName[0] == '/' && pName[1] == '/' && pName[2] != '/');
  3246. if (!bIsUNCName)
  3247. {
  3248. Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "materials/%s" TEXTURE_FNAME_EXTENSION, pName );
  3249. }
  3250. else
  3251. {
  3252. Q_snprintf( pCacheFileName, sizeof( pCacheFileName ), "%s" TEXTURE_FNAME_EXTENSION, pName );
  3253. }
  3254. if (!pSourceData || !nSourceDataSize)
  3255. {
  3256. // Get the data from disk...
  3257. // NOTE: Reloading the texture bits can cause the texture size, frames, format, pretty much *anything* can change.
  3258. return LoadTextureBitsFromFile( pCacheFileName, pResolvedFilename );
  3259. }
  3260. else
  3261. {
  3262. // use the data provided
  3263. return LoadTextureBitsFromData( pCacheFileName, pSourceData, nSourceDataSize );
  3264. }
  3265. }
  3266. //-----------------------------------------------------------------------------
  3267. // Loads the texture bits from a file.
  3268. //-----------------------------------------------------------------------------
  3269. IVTFTexture *CTexture::LoadTextureBitsFromFile( char *pCacheFileName, char **ppResolvedFilename )
  3270. {
  3271. int nHeaderSize;
  3272. int nFileSize;
  3273. IVTFTexture *pVTFTexture = ( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD ) ? GetScratchVTFAsyncTexture() : GetScratchVTFTexture();
  3274. bool bIsCombinedImage = ( pCacheFileName[ 0 ] == '!' ) && g_pMDLCache != NULL;
  3275. CUtlBuffer buf;
  3276. FileHandle_t fileHandle = FILESYSTEM_INVALID_HANDLE;
  3277. int nBytesOptimalRead; // GCC needs this extra newline due to goto
  3278. int nBytesRead; // GCC needs this extra newline due to goto
  3279. if ( bIsCombinedImage )
  3280. {
  3281. int nSize;
  3282. void *pBuffer = g_pMDLCache->GetCombinedInternalAsset( COMBINED_ASSET_TEXTURE, pCacheFileName, &nSize );
  3283. // buf will not own the memory and thus will not try to dealloc it
  3284. buf.SetExternalBuffer( pBuffer, nSize, nSize );
  3285. }
  3286. else
  3287. {
  3288. while ( fileHandle == FILESYSTEM_INVALID_HANDLE ) // run until found a file or out of rules
  3289. {
  3290. #if defined( _GAMECONSOLE )
  3291. // generate native texture
  3292. pVTFTexture->UpdateOrCreate( pCacheFileName );
  3293. #endif
  3294. fileHandle = g_pFullFileSystem->OpenEx( pCacheFileName, "rb", 0, MaterialSystem()->GetForcedTextureLoadPathID(), ppResolvedFilename );
  3295. if ( fileHandle == FILESYSTEM_INVALID_HANDLE )
  3296. {
  3297. // try any fallbacks.
  3298. char *pHdrExt = Q_stristr( pCacheFileName, ".hdr" TEXTURE_FNAME_EXTENSION );
  3299. if ( pHdrExt )
  3300. {
  3301. //DevWarning( "A custom HDR cubemap \"%s\": cannot be found on disk.\n"
  3302. // "This really should have a HDR version, trying a fall back to a non-HDR version.\n", pCacheFileName );
  3303. strcpy( pHdrExt, TEXTURE_FNAME_EXTENSION );
  3304. }
  3305. else
  3306. {
  3307. // no more fallbacks
  3308. break;
  3309. }
  3310. }
  3311. }
  3312. if ( fileHandle == FILESYSTEM_INVALID_HANDLE )
  3313. {
  3314. if ( !StringHasPrefix( m_Name.String(), "env_cubemap" ) )
  3315. {
  3316. if ( IsOSX() )
  3317. {
  3318. printf("\n ##### CTexture::LoadTextureBitsFromFile couldn't find %s",pCacheFileName );
  3319. }
  3320. DevWarning( "\"%s\": can't be found on disk\n", pCacheFileName );
  3321. }
  3322. return HandleFileLoadFailedTexture( pVTFTexture );
  3323. }
  3324. int nVersion = -1;
  3325. if ( IsPC() )
  3326. nVersion = VTF_MAJOR_VERSION;
  3327. else if ( IsX360() )
  3328. nVersion = VTF_X360_MAJOR_VERSION;
  3329. else if ( IsPS3() )
  3330. nVersion = VTF_PS3_MAJOR_VERSION;
  3331. nHeaderSize = VTFFileHeaderSize( nVersion );
  3332. // restrict read to the header only!
  3333. // header provides info to avoid reading the entire file
  3334. nBytesOptimalRead = GetOptimalReadBuffer( fileHandle, nHeaderSize, buf );
  3335. nBytesRead = g_pFullFileSystem->ReadEx( buf.Base(), nBytesOptimalRead, Min( nHeaderSize, (int)g_pFullFileSystem->Size(fileHandle) ), fileHandle ); // only read as much as the file has
  3336. nBytesRead = nHeaderSize = ((VTFFileBaseHeader_t *)buf.Base())->headerSize;
  3337. g_pFullFileSystem->Seek( fileHandle, nHeaderSize, FILESYSTEM_SEEK_HEAD );
  3338. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  3339. }
  3340. // Unserialize the header only
  3341. // need the header first to determine remainder of data
  3342. #if !defined( _GAMECONSOLE )
  3343. if ( !pVTFTexture->Unserialize( buf, true ) )
  3344. #else
  3345. if ( !pVTFTexture->UnserializeFromBuffer( buf, true, true, true, 0 ) )
  3346. #endif
  3347. {
  3348. Warning( "Error reading texture header \"%s\"\n", pCacheFileName );
  3349. g_pFullFileSystem->Close( fileHandle );
  3350. return HandleFileLoadFailedTexture( pVTFTexture );
  3351. }
  3352. // FIXME: Hack for L4D
  3353. int nHackExtraFlags = 0;
  3354. if ( !Q_strnicmp( pCacheFileName, "materials/graffiti/", 19 ) )
  3355. {
  3356. nHackExtraFlags = TEXTUREFLAGS_NOLOD;
  3357. }
  3358. // OSX hackery
  3359. if ( m_nFlags & TEXTUREFLAGS_SRGB )
  3360. {
  3361. nHackExtraFlags |= TEXTUREFLAGS_SRGB;
  3362. }
  3363. // Set from stdshaders cpp code
  3364. if ( m_nFlags & TEXTUREFLAGS_ANISOTROPIC )
  3365. {
  3366. nHackExtraFlags |= TEXTUREFLAGS_ANISOTROPIC;
  3367. }
  3368. // Seek the reading back to the front of the buffer
  3369. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  3370. // Initialize the texture class with vtf header data before operations
  3371. Init(
  3372. #if !defined( _GAMECONSOLE )
  3373. pVTFTexture->Width(),
  3374. pVTFTexture->Height(),
  3375. pVTFTexture->Depth(),
  3376. #else
  3377. // 360 texture might be pre-picmipped, setup as it's original dimensions
  3378. // so picmipping logic calculates correctly, and then fixup
  3379. pVTFTexture->MappingWidth(),
  3380. pVTFTexture->MappingHeight(),
  3381. pVTFTexture->MappingDepth(),
  3382. #endif
  3383. pVTFTexture->Format(),
  3384. pVTFTexture->Flags() | nHackExtraFlags,
  3385. pVTFTexture->FrameCount() );
  3386. VectorCopy( pVTFTexture->Reflectivity(), m_vecReflectivity );
  3387. #if defined( _GAMECONSOLE )
  3388. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_QUEUEDLOAD;
  3389. if ( !g_pQueuedLoader->IsMapLoading() || ( m_nFlags & ( TEXTUREFLAGS_PROCEDURAL|TEXTUREFLAGS_RENDERTARGET|TEXTUREFLAGS_DEPTHRENDERTARGET ) ) )
  3390. {
  3391. // explicitly disabled or not appropriate for texture type
  3392. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_QUEUEDLOAD;
  3393. }
  3394. else
  3395. {
  3396. if ( pVTFTexture->FileSize( true, 0 ) >= pVTFTexture->FileSize( false, 0 ) )
  3397. {
  3398. // texture is a dwarf, entirely in preload, loads normally
  3399. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_QUEUEDLOAD;
  3400. }
  3401. }
  3402. #endif
  3403. // Compute the actual texture dimensions
  3404. int nMipSkipCount = ComputeActualSize( false, pVTFTexture );
  3405. #if defined( _GAMECONSOLE )
  3406. bool bQueuedLoad = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD ) != 0;
  3407. nMipSkipCount -= pVTFTexture->MipSkipCount();
  3408. if ( nMipSkipCount < 0 || ( nMipSkipCount >= pVTFTexture->MipCount() ) )
  3409. {
  3410. // the 360 texture was already pre-picmipped or can't be picmipped
  3411. // clamp to the available dimensions
  3412. m_nActualWidth = pVTFTexture->Width();
  3413. m_nActualHeight = pVTFTexture->Height();
  3414. m_nActualDepth = pVTFTexture->Depth();
  3415. m_nActualMipCount = ComputeActualMipCount();
  3416. nMipSkipCount = 0;
  3417. }
  3418. if ( IsX360() && g_config.skipMipLevels == 0 && m_nActualMipCount > 1 && m_nFrameCount == 1 && !( m_nFlags & ( TEXTUREFLAGS_PROCEDURAL|TEXTUREFLAGS_RENDERTARGET|TEXTUREFLAGS_DEPTHRENDERTARGET ) ) )
  3419. {
  3420. // this file based texture is a good candidate for cacheing
  3421. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_CACHEABLE;
  3422. }
  3423. if ( nMipSkipCount )
  3424. {
  3425. // track which textures had their dimensions forcefully reduced
  3426. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_REDUCED;
  3427. }
  3428. #endif
  3429. m_nMipSkipCount = nMipSkipCount;
  3430. #if !defined( _GAMECONSOLE )
  3431. // Determine how much of the file to read in
  3432. nFileSize = pVTFTexture->FileSize( nMipSkipCount );
  3433. #else
  3434. // A queued loading texture just gets the preload section
  3435. // and does NOT unserialize the texture bits here
  3436. nFileSize = pVTFTexture->FileSize( bQueuedLoad, nMipSkipCount );
  3437. #endif
  3438. // Read only the portion of the file that we care about
  3439. g_pFullFileSystem->Seek( fileHandle, 0, FILESYSTEM_SEEK_HEAD );
  3440. nBytesOptimalRead = GetOptimalReadBuffer( fileHandle, nFileSize, buf );
  3441. nBytesRead = g_pFullFileSystem->ReadEx( buf.Base(), nBytesOptimalRead, nFileSize, fileHandle );
  3442. g_pFullFileSystem->Close( fileHandle );
  3443. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  3444. // NOTE: Skipping mip levels here will cause the size to be changed...
  3445. #if !defined( _GAMECONSOLE )
  3446. if ( !pVTFTexture->Unserialize( buf, false, nMipSkipCount ) )
  3447. #else
  3448. if ( !pVTFTexture->UnserializeFromBuffer( buf, true, false, bQueuedLoad, nMipSkipCount ) )
  3449. #endif
  3450. {
  3451. Warning( "Error reading material data \"%s\"\n", pCacheFileName );
  3452. return HandleFileLoadFailedTexture( pVTFTexture );
  3453. }
  3454. // Build the low-res texture
  3455. LoadLowResTexture( pVTFTexture );
  3456. // load resources
  3457. LoadResourceData( pVTFTexture );
  3458. // Try to set up debugging textures, if we're in a debugging mode
  3459. if ( !IsProcedural() && !IsGameConsole() )
  3460. {
  3461. SetupDebuggingTextures( pVTFTexture );
  3462. }
  3463. if ( ConvertToActualFormat( pVTFTexture ) )
  3464. {
  3465. if ( IsGameConsole() )
  3466. {
  3467. // 360 vtf are baked in final formats, no format conversion can or should have occurred
  3468. // otherwise track offender and ensure files are baked correctly
  3469. Error( "\"%s\" not in native format\n", pCacheFileName );
  3470. }
  3471. }
  3472. return pVTFTexture;
  3473. }
  3474. //-----------------------------------------------------------------------------
  3475. // Loads the texture bits from provided data.
  3476. //-----------------------------------------------------------------------------
  3477. IVTFTexture *CTexture::LoadTextureBitsFromData( char *pCacheFileName, void *pSourceData, int nSourceDataSize )
  3478. {
  3479. IVTFTexture *pVTFTexture = ( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD ) ? GetScratchVTFAsyncTexture() : GetScratchVTFTexture();
  3480. CUtlBuffer buf;
  3481. buf.SetExternalBuffer( pSourceData, nSourceDataSize, nSourceDataSize, CUtlBuffer::READ_ONLY );
  3482. int nVersion = -1;
  3483. if ( IsPC() )
  3484. nVersion = VTF_MAJOR_VERSION;
  3485. else if ( IsX360() )
  3486. nVersion = VTF_X360_MAJOR_VERSION;
  3487. else if ( IsPS3() )
  3488. nVersion = VTF_PS3_MAJOR_VERSION;
  3489. int nHeaderSize = VTFFileHeaderSize( nVersion );
  3490. if ( nSourceDataSize < nHeaderSize )
  3491. {
  3492. Warning( "Error reading texture header \"%s\"\n", pCacheFileName );
  3493. return HandleFileLoadFailedTexture( pVTFTexture );
  3494. }
  3495. // Unserialize the header only
  3496. // need the header first to determine remainder of data
  3497. #if !defined( _GAMECONSOLE )
  3498. if ( !pVTFTexture->Unserialize( buf, true ) )
  3499. #else
  3500. if ( !pVTFTexture->UnserializeFromBuffer( buf, true, true, true, 0 ) )
  3501. #endif
  3502. {
  3503. Warning( "Error reading texture header \"%s\"\n", pCacheFileName );
  3504. return HandleFileLoadFailedTexture( pVTFTexture );
  3505. }
  3506. // FIXME: Hack for L4D
  3507. int nHackExtraFlags = 0;
  3508. if ( !Q_strnicmp( pCacheFileName, "materials/graffiti/", 19 ) )
  3509. {
  3510. nHackExtraFlags = TEXTUREFLAGS_NOLOD;
  3511. }
  3512. // OSX hackery
  3513. if ( m_nFlags & TEXTUREFLAGS_SRGB )
  3514. {
  3515. nHackExtraFlags |= TEXTUREFLAGS_SRGB;
  3516. }
  3517. // Set from stdshaders cpp code
  3518. if ( m_nFlags & TEXTUREFLAGS_ANISOTROPIC )
  3519. {
  3520. nHackExtraFlags |= TEXTUREFLAGS_ANISOTROPIC;
  3521. }
  3522. if ( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD )
  3523. {
  3524. nHackExtraFlags |= TEXTUREFLAGS_ASYNC_DOWNLOAD;
  3525. }
  3526. // Seek the reading back to the front of the buffer
  3527. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  3528. // Initialize the texture class with vtf header data before operations
  3529. Init(
  3530. #if !defined( _GAMECONSOLE )
  3531. pVTFTexture->Width(),
  3532. pVTFTexture->Height(),
  3533. pVTFTexture->Depth(),
  3534. #else
  3535. // 360 texture might be pre-picmipped, setup as it's original dimensions
  3536. // so picmipping logic calculates correctly, and then fixup
  3537. pVTFTexture->MappingWidth(),
  3538. pVTFTexture->MappingHeight(),
  3539. pVTFTexture->MappingDepth(),
  3540. #endif
  3541. pVTFTexture->Format(),
  3542. pVTFTexture->Flags() | nHackExtraFlags,
  3543. pVTFTexture->FrameCount() );
  3544. VectorCopy( pVTFTexture->Reflectivity(), m_vecReflectivity );
  3545. // Compute the actual texture dimensions
  3546. int nMipSkipCount = ComputeActualSize( false, pVTFTexture );
  3547. #if defined( _GAMECONSOLE )
  3548. bool bQueuedLoad = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD ) != 0;
  3549. nMipSkipCount -= pVTFTexture->MipSkipCount();
  3550. if ( nMipSkipCount < 0 || ( nMipSkipCount >= pVTFTexture->MipCount() ) )
  3551. {
  3552. // the 360 texture was already pre-picmipped or can't be picmipped
  3553. // clamp to the available dimensions
  3554. m_nActualWidth = pVTFTexture->Width();
  3555. m_nActualHeight = pVTFTexture->Height();
  3556. m_nActualDepth = pVTFTexture->Depth();
  3557. m_nActualMipCount = ComputeActualMipCount();
  3558. nMipSkipCount = 0;
  3559. }
  3560. if ( IsX360() && g_config.skipMipLevels == 0 && m_nActualMipCount > 1 && m_nFrameCount == 1 && !( m_nFlags & ( TEXTUREFLAGS_PROCEDURAL|TEXTUREFLAGS_RENDERTARGET|TEXTUREFLAGS_DEPTHRENDERTARGET ) ) )
  3561. {
  3562. // this file based texture is a good candidate for cacheing
  3563. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_CACHEABLE;
  3564. }
  3565. if ( nMipSkipCount )
  3566. {
  3567. // track which textures had their dimensions forcefully reduced
  3568. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_REDUCED;
  3569. }
  3570. #endif
  3571. m_nMipSkipCount = nMipSkipCount;
  3572. // NOTE: Skipping mip levels here will cause the size to be changed...
  3573. #if !defined( _GAMECONSOLE )
  3574. if ( !pVTFTexture->Unserialize( buf, false, nMipSkipCount ) )
  3575. #else
  3576. if ( !pVTFTexture->UnserializeFromBuffer( buf, true, false, bQueuedLoad, nMipSkipCount ) )
  3577. #endif
  3578. {
  3579. Warning( "Error reading texture data \"%s\"\n", pCacheFileName );
  3580. return HandleFileLoadFailedTexture( pVTFTexture );
  3581. }
  3582. // Build the low-res texture
  3583. LoadLowResTexture( pVTFTexture );
  3584. // Load the resources
  3585. LoadResourceData( pVTFTexture );
  3586. // Try to set up debugging textures, if we're in a debugging mode
  3587. if ( !IsProcedural() && !IsGameConsole() )
  3588. {
  3589. SetupDebuggingTextures( pVTFTexture );
  3590. }
  3591. if ( ConvertToActualFormat( pVTFTexture ) )
  3592. {
  3593. if ( IsGameConsole() )
  3594. {
  3595. // 360 vtf are baked in final formats, no format conversion can or should have occurred
  3596. // otherwise track offender and ensure files are baked correctly
  3597. Error( "\"%s\" not in native format\n", pCacheFileName );
  3598. }
  3599. }
  3600. return pVTFTexture;
  3601. }
  3602. IVTFTexture *CTexture::HandleFileLoadFailedTexture( IVTFTexture *pVTFTexture )
  3603. {
  3604. // create the error texture
  3605. #if defined( _GAMECONSOLE )
  3606. // reset botched vtf, ensure checkerboard error texture is created now and maintains bgra8888 format
  3607. pVTFTexture->ReleaseImageMemory();
  3608. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_QUEUEDLOAD;
  3609. m_nFlags |= TEXTUREFLAGS_EIGHTBITALPHA;
  3610. #endif
  3611. // This will make a checkerboard texture to indicate failure
  3612. pVTFTexture->Init( 32, 32, 1, IMAGE_FORMAT_BGRA8888, m_nFlags, 1 );
  3613. Init( pVTFTexture->Width(), pVTFTexture->Height(), pVTFTexture->Depth(), pVTFTexture->Format(),
  3614. pVTFTexture->Flags(), pVTFTexture->FrameCount() );
  3615. m_vecReflectivity.Init( 0.5f, 0.5f, 0.5f );
  3616. // NOTE: For mat_picmip to work, we must use the same size (32x32)
  3617. // Which should work since every card can handle textures of that size
  3618. m_nActualWidth = pVTFTexture->Width();
  3619. m_nActualHeight = pVTFTexture->Height();
  3620. m_nActualMipCount = 1;
  3621. // generate the checkerboard
  3622. TextureManager()->GenerateErrorTexture( this, pVTFTexture );
  3623. ConvertToActualFormat( pVTFTexture );
  3624. // Deactivate procedural texture...
  3625. m_nFlags &= ~TEXTUREFLAGS_PROCEDURAL;
  3626. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_ERROR;
  3627. return pVTFTexture;
  3628. }
  3629. //-----------------------------------------------------------------------------
  3630. // Computes subrect for a particular miplevel
  3631. //-----------------------------------------------------------------------------
  3632. void CTexture::ComputeMipLevelSubRect( const Rect_t* pSrcRect, int nMipLevel, Rect_t *pSubRect )
  3633. {
  3634. if (nMipLevel == 0)
  3635. {
  3636. *pSubRect = *pSrcRect;
  3637. return;
  3638. }
  3639. float flInvShrink = 1.0f / (float)(1 << nMipLevel);
  3640. pSubRect->x = pSrcRect->x * flInvShrink;
  3641. pSubRect->y = pSrcRect->y * flInvShrink;
  3642. pSubRect->width = (int)ceil( (pSrcRect->x + pSrcRect->width) * flInvShrink ) - pSubRect->x;
  3643. pSubRect->height = (int)ceil( (pSrcRect->y + pSrcRect->height) * flInvShrink ) - pSubRect->y;
  3644. }
  3645. //-----------------------------------------------------------------------------
  3646. // Computes the face count + first face
  3647. //-----------------------------------------------------------------------------
  3648. void CTexture::GetDownloadFaceCount( int &nFirstFace, int &nFaceCount )
  3649. {
  3650. nFaceCount = 1;
  3651. nFirstFace = 0;
  3652. if ( IsCubeMap() )
  3653. {
  3654. nFaceCount = CUBEMAP_FACE_COUNT;
  3655. }
  3656. }
  3657. //-----------------------------------------------------------------------------
  3658. // Fixup a queue loaded texture with the delayed hi-res data
  3659. //-----------------------------------------------------------------------------
  3660. void CTexture::FixupTexture( const void *pData, int nSize, LoaderError_t loaderError )
  3661. {
  3662. if ( loaderError != LOADERERROR_NONE )
  3663. {
  3664. // mark as invalid
  3665. nSize = 0;
  3666. }
  3667. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_QUEUEDLOAD;
  3668. // Make sure we've actually allocated the texture handles
  3669. Assert( HasBeenAllocated() );
  3670. #if defined( _GAMECONSOLE )
  3671. // hand off the hires data down to the shaderapi to upload directly
  3672. // Purposely bypassing downloading through material system, which is non-reentrant
  3673. // for that operation, to avoid mutexing.
  3674. // NOTE: Strange refcount work here to keep it threadsafe
  3675. int nRefCount = m_nRefCount;
  3676. int nRefCountOld = nRefCount;
  3677. g_pShaderAPI->PostQueuedTexture(
  3678. pData,
  3679. nSize,
  3680. m_pTextureHandles,
  3681. m_nFrameCount,
  3682. m_nActualWidth,
  3683. m_nActualHeight,
  3684. m_nActualDepth,
  3685. m_nActualMipCount,
  3686. &nRefCount );
  3687. int nDelta = nRefCount - nRefCountOld;
  3688. m_nRefCount += nDelta;
  3689. #endif
  3690. }
  3691. static void QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  3692. {
  3693. reinterpret_cast< CTexture * >( pContext )->FixupTexture( pData, nSize, loaderError );
  3694. }
  3695. //-----------------------------------------------------------------------------
  3696. // Generates the procedural bits
  3697. //-----------------------------------------------------------------------------
  3698. IVTFTexture *CTexture::ReconstructPartialProceduralBits( const Rect_t *pRect, Rect_t *pActualRect )
  3699. {
  3700. // Figure out the actual size for this texture based on the current mode
  3701. ComputeActualSize();
  3702. // Figure out how many mip levels we're skipping...
  3703. int nSizeFactor = 1;
  3704. int nWidth = GetActualWidth();
  3705. if ( nWidth != 0 )
  3706. {
  3707. nSizeFactor = GetMappingWidth() / nWidth;
  3708. }
  3709. int nMipSkipCount = 0;
  3710. while (nSizeFactor > 1)
  3711. {
  3712. nSizeFactor >>= 1;
  3713. ++nMipSkipCount;
  3714. }
  3715. // Determine a rectangle appropriate for the actual size...
  3716. // It must bound all partially-covered pixels..
  3717. ComputeMipLevelSubRect( pRect, nMipSkipCount, pActualRect );
  3718. if ( IsGameConsole() && !IsDebug() && !m_pTextureRegenerator )
  3719. {
  3720. // no checkerboards in 360 release
  3721. return NULL;
  3722. }
  3723. bool bUsePreallocatedScratchTexture = m_pTextureRegenerator && m_pTextureRegenerator->HasPreallocatedScratchTexture();
  3724. // Create the texture
  3725. IVTFTexture *pVTFTexture = bUsePreallocatedScratchTexture ? m_pTextureRegenerator->GetPreallocatedScratchTexture() : GetScratchVTFTexture();
  3726. // Initialize the texture
  3727. pVTFTexture->Init( m_nActualWidth, m_nActualHeight, m_nActualDepth,
  3728. ComputeActualFormat( m_ImageFormat ), m_nFlags, m_nFrameCount );
  3729. // Generate the bits from the installed procedural regenerator
  3730. if ( m_pTextureRegenerator )
  3731. {
  3732. m_pTextureRegenerator->RegenerateTextureBits( this, pVTFTexture, pActualRect );
  3733. }
  3734. else
  3735. {
  3736. // In this case, we don't have one, so just use a checkerboard...
  3737. TextureManager()->GenerateErrorTexture( this, pVTFTexture );
  3738. }
  3739. return pVTFTexture;
  3740. }
  3741. //-----------------------------------------------------------------------------
  3742. // Regenerates the bits of a texture within a particular rectangle
  3743. //-----------------------------------------------------------------------------
  3744. void CTexture::ReconstructPartialTexture( const Rect_t *pRect )
  3745. {
  3746. // FIXME: for now, only procedural textures can handle sub-rect specification.
  3747. Assert( IsProcedural() );
  3748. // Also, we need procedural textures that have only a single copy!!
  3749. // Otherwise this partial upload will not occur on all copies
  3750. Assert( m_nFlags & TEXTUREFLAGS_SINGLECOPY );
  3751. Rect_t vtfRect;
  3752. IVTFTexture *pVTFTexture = ReconstructPartialProceduralBits( pRect, &vtfRect );
  3753. // FIXME: for now, depth textures do not work with this.
  3754. Assert( pVTFTexture->Depth() == 1 );
  3755. // Make sure we've allocated the API textures
  3756. if ( !HasBeenAllocated() )
  3757. {
  3758. if ( !AllocateShaderAPITextures() )
  3759. return;
  3760. }
  3761. if ( IsGameConsole() && !pVTFTexture )
  3762. {
  3763. // 360 inhibited procedural generation
  3764. return;
  3765. }
  3766. int nFaceCount, nFirstFace;
  3767. GetDownloadFaceCount( nFirstFace, nFaceCount );
  3768. // Blit down portions of the various VTF frames into the board memory
  3769. int nStride;
  3770. Rect_t mipRect;
  3771. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  3772. {
  3773. Modify( iFrame );
  3774. for ( int iFace = 0; iFace < nFaceCount; ++iFace )
  3775. {
  3776. for ( int iMip = 0; iMip < m_nActualMipCount; ++iMip )
  3777. {
  3778. pVTFTexture->ComputeMipLevelSubRect( &vtfRect, iMip, &mipRect );
  3779. nStride = pVTFTexture->RowSizeInBytes( iMip );
  3780. unsigned char *pBits = pVTFTexture->ImageData( iFrame, iFace + nFirstFace, iMip, mipRect.x, mipRect.y, 0 );
  3781. g_pShaderAPI->TexSubImage2D(
  3782. iMip,
  3783. iFace,
  3784. mipRect.x,
  3785. mipRect.y,
  3786. 0,
  3787. mipRect.width,
  3788. mipRect.height,
  3789. pVTFTexture->Format(),
  3790. nStride,
  3791. #if defined( _GAMECONSOLE )
  3792. pVTFTexture->IsPreTiled(),
  3793. #else
  3794. false,
  3795. #endif
  3796. pBits );
  3797. }
  3798. }
  3799. }
  3800. #if defined( _GAMECONSOLE )
  3801. if ( IsProcedural() && m_pTextureRegenerator && m_pTextureRegenerator->HasPreallocatedScratchTexture() )
  3802. {
  3803. // nothing to free; we used the pre-allocated scratch texture
  3804. }
  3805. else
  3806. {
  3807. // hint the vtf system to release memory associated with its load
  3808. pVTFTexture->ReleaseImageMemory();
  3809. }
  3810. #endif
  3811. }
  3812. //-----------------------------------------------------------------------------
  3813. // Generates the procedural bits
  3814. //-----------------------------------------------------------------------------
  3815. IVTFTexture *CTexture::ReconstructProceduralBits()
  3816. {
  3817. // Figure out the actual size for this texture based on the current mode
  3818. ComputeActualSize();
  3819. if ( IsGameConsole() && !IsDebug() && !m_pTextureRegenerator )
  3820. {
  3821. // no checkerboards in 360 release
  3822. return NULL;
  3823. }
  3824. bool bUsePreallocatedScratchTexture = m_pTextureRegenerator && m_pTextureRegenerator->HasPreallocatedScratchTexture();
  3825. // Create the texture
  3826. IVTFTexture *pVTFTexture = bUsePreallocatedScratchTexture ? m_pTextureRegenerator->GetPreallocatedScratchTexture() : GetScratchVTFTexture();
  3827. // Initialize the texture
  3828. pVTFTexture->Init( m_nActualWidth, m_nActualHeight, m_nActualDepth,
  3829. ComputeActualFormat( m_ImageFormat ), m_nFlags, m_nFrameCount );
  3830. // Generate the bits from the installed procedural regenerator
  3831. if ( m_pTextureRegenerator )
  3832. {
  3833. Rect_t rect;
  3834. rect.x = 0; rect.y = 0;
  3835. rect.width = m_nActualWidth;
  3836. rect.height = m_nActualHeight;
  3837. m_pTextureRegenerator->RegenerateTextureBits( this, pVTFTexture, &rect );
  3838. }
  3839. else if ( !ImageLoader::IsFloatFormat( m_ImageFormat ) && !ImageLoader::IsRuntimeCompressed( m_ImageFormat ) )
  3840. {
  3841. // In this case, we don't have one, so just use a checkerboard...
  3842. TextureManager()->GenerateErrorTexture( this, pVTFTexture );
  3843. }
  3844. return pVTFTexture;
  3845. }
  3846. void CTexture::WriteDataToShaderAPITexture( int nFrameCount, int nFaceCount, int nFirstFace, int nMipCount, IVTFTexture *pVTFTexture, ImageFormat fmt )
  3847. {
  3848. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  3849. {
  3850. Modify( iFrame );
  3851. for ( int iFace = 0; iFace < nFaceCount; ++iFace )
  3852. {
  3853. for ( int iMip = 0; iMip < nMipCount; ++iMip )
  3854. {
  3855. unsigned char *pBits;
  3856. int nWidth, nHeight, nDepth;
  3857. pVTFTexture->ComputeMipLevelDimensions( iMip, &nWidth, &nHeight, &nDepth );
  3858. for ( int z = 0; z < nDepth; ++z )
  3859. {
  3860. pBits = pVTFTexture->ImageData( iFrame, iFace + nFirstFace, iMip, 0, 0, z );
  3861. g_pShaderAPI->TexImage2D( iMip, iFace, fmt, z, nWidth, nHeight, pVTFTexture->Format(), false, pBits );
  3862. }
  3863. }
  3864. }
  3865. }
  3866. }
  3867. bool CTexture::IsDepthTextureFormat( ImageFormat fmt )
  3868. {
  3869. return ( ( m_ImageFormat == IMAGE_FORMAT_D16_SHADOW ) ||
  3870. ( m_ImageFormat == IMAGE_FORMAT_D24X8_SHADOW ) ||
  3871. ( m_ImageFormat == IMAGE_FORMAT_D24S8 ) );
  3872. }
  3873. //-----------------------------------------------------------------------------
  3874. // Sets or updates the texture bits
  3875. //-----------------------------------------------------------------------------
  3876. void CTexture::ReconstructTexture( void *pSourceData, int nSourceDataSize )
  3877. {
  3878. int oldWidth = m_nActualWidth;
  3879. int oldHeight = m_nActualHeight;
  3880. int oldDepth = m_nActualDepth;
  3881. int oldMipCount = m_nActualMipCount;
  3882. int oldFrameCount = m_nFrameCount;
  3883. // FIXME: Should RenderTargets be a special case of Procedural?
  3884. char *pResolvedFilename = NULL;
  3885. IVTFTexture *pVTFTexture = NULL;
  3886. if ( IsProcedural() )
  3887. {
  3888. // This will call the installed texture bit regeneration interface
  3889. pVTFTexture = ReconstructProceduralBits();
  3890. }
  3891. else if ( IsRenderTarget() )
  3892. {
  3893. // Compute the actual size + format based on the current mode
  3894. ComputeActualSize( true );
  3895. }
  3896. else
  3897. {
  3898. pVTFTexture = LoadTexttureBitsFromFileOrData( pSourceData, nSourceDataSize, &pResolvedFilename );
  3899. }
  3900. if ( !HasBeenAllocated() ||
  3901. m_nActualWidth != oldWidth ||
  3902. m_nActualHeight != oldHeight ||
  3903. m_nActualDepth != oldDepth ||
  3904. m_nActualMipCount != oldMipCount ||
  3905. m_nFrameCount != oldFrameCount )
  3906. {
  3907. if ( HasBeenAllocated() )
  3908. {
  3909. // This is necessary for the reload case, we may discover there
  3910. // are more frames of a texture animation, for example, which means
  3911. // we can't rely on having the same number of texture frames.
  3912. FreeShaderAPITextures();
  3913. }
  3914. // Create the shader api textures, except temp render targets on 360.
  3915. if ( !( IsX360() && IsTempRenderTarget() ) )
  3916. {
  3917. if ( !AllocateShaderAPITextures() )
  3918. return;
  3919. }
  3920. }
  3921. // Render Targets just need to be cleared, they have no upload
  3922. if ( IsRenderTarget() )
  3923. {
  3924. // Clear the render target to opaque black
  3925. #if !defined( _GAMECONSOLE )
  3926. // Only clear if we're not a depth-stencil texture
  3927. if ( !IsDepthTextureFormat( m_ImageFormat ) )
  3928. {
  3929. CMatRenderContextPtr pRenderContext( MaterialSystem() );
  3930. ITexture *pThisTexture = GetEmbeddedTexture( 0 );
  3931. pRenderContext->PushRenderTargetAndViewport( pThisTexture ); // Push this texture on the stack
  3932. g_pShaderAPI->ClearColor4ub( 0, 0, 0, 0xFF ); // Set the clear color to opaque black
  3933. g_pShaderAPI->ClearBuffers( true, false, false, m_nActualWidth, m_nActualHeight ); // Clear the target
  3934. pRenderContext->PopRenderTargetAndViewport(); // Pop back to previous target
  3935. }
  3936. #else
  3937. // 360 may not have RT surface during init time
  3938. // avoid complex conditionalizing, just cpu clear it, which always works
  3939. ClearTexture( 0, 0, 0, 0xFF );
  3940. #endif
  3941. // no upload
  3942. return;
  3943. }
  3944. if ( IsGameConsole() && IsProcedural() && !pVTFTexture )
  3945. {
  3946. // 360 explicitly inhibited this texture's procedural generation, so no upload needed
  3947. return;
  3948. }
  3949. // Blit down the texture faces, frames, and mips into the board memory
  3950. int nFirstFace, nFaceCount;
  3951. GetDownloadFaceCount( nFirstFace, nFaceCount );
  3952. if ( IsPC() )
  3953. {
  3954. WriteDataToShaderAPITexture( m_nFrameCount, nFaceCount, nFirstFace, m_nActualMipCount, pVTFTexture, m_ImageFormat );
  3955. }
  3956. #if defined( _GAMECONSOLE )
  3957. bool bDoUpload = true;
  3958. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD )
  3959. {
  3960. // the vtf didn't load any d3d bits, the hires bits will arrive before gameplay
  3961. bDoUpload = false;
  3962. }
  3963. if ( bDoUpload )
  3964. {
  3965. for ( int iFrame = 0; iFrame < m_nFrameCount; ++iFrame )
  3966. {
  3967. Modify( iFrame );
  3968. for ( int iFace = 0; iFace < nFaceCount; ++iFace )
  3969. {
  3970. for ( int iMip = 0; iMip < m_nActualMipCount; ++iMip )
  3971. {
  3972. unsigned char *pBits;
  3973. int nWidth, nHeight, nDepth;
  3974. pVTFTexture->ComputeMipLevelDimensions( iMip, &nWidth, &nHeight, &nDepth );
  3975. #ifdef _PS3
  3976. // PS3 textures are pre-swizzled at tool time
  3977. pBits = pVTFTexture->ImageData( iFrame, iFace + nFirstFace, iMip, 0, 0, 0 );
  3978. g_pShaderAPI->TexImage2D( iMip, iFace, m_ImageFormat, nDepth > 1 ? nDepth : 0, nWidth, nHeight,
  3979. pVTFTexture->Format(), false, pBits );
  3980. #else // _PS3
  3981. pBits = pVTFTexture->ImageData( iFrame, iFace + nFirstFace, iMip, 0, 0, 0 );
  3982. g_pShaderAPI->TexImage2D( iMip, iFace, m_ImageFormat, 0, nWidth, nHeight,
  3983. pVTFTexture->Format(), pVTFTexture->IsPreTiled(), pBits );
  3984. #endif // !_PS3
  3985. }
  3986. }
  3987. }
  3988. }
  3989. #ifdef _X360
  3990. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_CACHEABLE )
  3991. {
  3992. // Make sure we've actually allocated the texture handles
  3993. Assert( HasBeenAllocated() );
  3994. // a cacheing texture needs to know how to get its bits back
  3995. g_pShaderAPI->SetCacheableTextureParams( m_pTextureHandles, m_nFrameCount, pResolvedFilename, m_nMipSkipCount );
  3996. }
  3997. #endif // _X360
  3998. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD )
  3999. {
  4000. // the empty hires version was setup
  4001. // the hires d3d bits will be delivered before gameplay (or render)
  4002. LoaderPriority_t priority = LOADERPRIORITY_BEFOREPLAY;
  4003. // add the job
  4004. LoaderJob_t loaderJob;
  4005. loaderJob.m_pFilename = pResolvedFilename;
  4006. loaderJob.m_pCallback = QueuedLoaderCallback;
  4007. loaderJob.m_pContext = (void *)this;
  4008. loaderJob.m_Priority = priority;
  4009. g_pQueuedLoader->AddJob( &loaderJob );
  4010. }
  4011. if ( IsProcedural() && m_pTextureRegenerator && m_pTextureRegenerator->HasPreallocatedScratchTexture() )
  4012. {
  4013. // nothing to free; we used the pre-allocated scratch texture
  4014. }
  4015. else
  4016. {
  4017. // hint the vtf system to release memory associated with its load
  4018. pVTFTexture->ReleaseImageMemory();
  4019. }
  4020. #endif // _GAMECONSOLE
  4021. delete [] pResolvedFilename;
  4022. // the 360 does not persist a large buffer
  4023. // the pc can afford to persist a large buffer
  4024. FreeOptimalReadBuffer( IsGameConsole() ? 32*1024 : 6*1024*1024 );
  4025. }
  4026. // Get the shaderapi texture handle associated w/ a particular frame
  4027. ShaderAPITextureHandle_t CTexture::GetTextureHandle( int nFrame, int nTextureChannel )
  4028. {
  4029. if ( nFrame < 0 )
  4030. {
  4031. nFrame = 0;
  4032. Warning( "CTexture::GetTextureHandle(): nFrame is < 0!\n" );
  4033. }
  4034. if ( nFrame >= m_nFrameCount )
  4035. {
  4036. // 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.
  4037. Assert( nFrame < m_nFrameCount );
  4038. return INVALID_SHADERAPI_TEXTURE_HANDLE;
  4039. }
  4040. Assert( nTextureChannel < 2 );
  4041. // Make sure we've actually allocated the texture handles
  4042. Assert( HasBeenAllocated() );
  4043. if ( m_pTextureHandles == NULL || !HasBeenAllocated() )
  4044. {
  4045. return INVALID_SHADERAPI_TEXTURE_HANDLE;
  4046. }
  4047. return m_pTextureHandles[nFrame];
  4048. }
  4049. void CTexture::GetLowResColorSample( float s, float t, float *color ) const
  4050. {
  4051. if ( m_LowResImageWidth <= 0 || m_LowResImageHeight <= 0 )
  4052. {
  4053. // Warning( "Programming error: GetLowResColorSample \"%s\": %dx%d\n", m_pName, ( int )m_LowResImageWidth, ( int )m_LowResImageHeight );
  4054. return;
  4055. }
  4056. #if !defined( _GAMECONSOLE )
  4057. // force s and t into [0,1)
  4058. if ( s < 0.0f )
  4059. {
  4060. s = ( 1.0f - ( float )( int )s ) + s;
  4061. }
  4062. if ( t < 0.0f )
  4063. {
  4064. t = ( 1.0f - ( float )( int )t ) + t;
  4065. }
  4066. s = s - ( float )( int )s;
  4067. t = t - ( float )( int )t;
  4068. s *= m_LowResImageWidth;
  4069. t *= m_LowResImageHeight;
  4070. int wholeS, wholeT;
  4071. wholeS = ( int )s;
  4072. wholeT = ( int )t;
  4073. float fracS, fracT;
  4074. fracS = s - ( float )( int )s;
  4075. fracT = t - ( float )( int )t;
  4076. // filter twice in the s dimension.
  4077. float sColor[2][3];
  4078. int wholeSPlusOne = ( wholeS + 1 ) % m_LowResImageWidth;
  4079. int wholeTPlusOne = ( wholeT + 1 ) % m_LowResImageHeight;
  4080. sColor[0][0] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeT * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  4081. sColor[0][1] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeT * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  4082. sColor[0][2] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeT * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  4083. sColor[0][0] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  4084. sColor[0][1] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  4085. sColor[0][2] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeT * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  4086. sColor[1][0] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  4087. sColor[1][1] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  4088. sColor[1][2] = ( 1.0f - fracS ) * ( m_pLowResImage[( wholeS + wholeTPlusOne * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  4089. sColor[1][0] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 0] * ( 1.0f / 255.0f ) );
  4090. sColor[1][1] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 1] * ( 1.0f / 255.0f ) );
  4091. sColor[1][2] += fracS * ( m_pLowResImage[( wholeSPlusOne + wholeTPlusOne * m_LowResImageWidth ) * 3 + 2] * ( 1.0f / 255.0f ) );
  4092. color[0] = sColor[0][0] * ( 1.0f - fracT ) + sColor[1][0] * fracT;
  4093. color[1] = sColor[0][1] * ( 1.0f - fracT ) + sColor[1][1] * fracT;
  4094. color[2] = sColor[0][2] * ( 1.0f - fracT ) + sColor[1][2] * fracT;
  4095. #else
  4096. color[0] = (float)m_LowResImageSample[0] * 1.0f/255.0f;
  4097. color[1] = (float)m_LowResImageSample[1] * 1.0f/255.0f;
  4098. color[2] = (float)m_LowResImageSample[2] * 1.0f/255.0f;
  4099. #endif
  4100. }
  4101. int CTexture::GetApproximateVidMemBytes( void ) const
  4102. {
  4103. ImageFormat format = GetImageFormat();
  4104. int width = GetActualWidth();
  4105. int height = GetActualHeight();
  4106. int depth = GetActualDepth();
  4107. int numFrames = GetNumAnimationFrames();
  4108. bool isMipmapped = IsMipmapped();
  4109. return numFrames * ImageLoader::GetMemRequired( width, height, depth, format, isMipmapped );
  4110. }
  4111. void CTexture::CopyFrameBufferToMe( int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect )
  4112. {
  4113. Assert( m_pTextureHandles && m_nFrameCount >= 1 );
  4114. if ( IsX360() &&
  4115. ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET ) &&
  4116. !HasBeenAllocated() )
  4117. {
  4118. //need to create the texture bits now
  4119. //to avoid creating the texture bits previously, we simply skipped this step
  4120. if ( !AllocateShaderAPITextures() )
  4121. return;
  4122. }
  4123. if ( m_pTextureHandles && m_nFrameCount >= 1 )
  4124. {
  4125. g_pShaderAPI->CopyRenderTargetToTextureEx( m_pTextureHandles[0], nRenderTargetID, pSrcRect, pDstRect );
  4126. }
  4127. }
  4128. void CTexture::CopyMeToFrameBuffer( int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect )
  4129. {
  4130. Assert( m_pTextureHandles && m_nFrameCount >= 1 );
  4131. if ( IsX360() &&
  4132. ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPRENDERTARGET ) &&
  4133. !HasBeenAllocated() )
  4134. {
  4135. //need to create the texture bits now
  4136. //to avoid creating the texture bits previously, we simply skipped this step
  4137. if ( !AllocateShaderAPITextures() )
  4138. return;
  4139. }
  4140. if ( m_pTextureHandles && m_nFrameCount >= 1 )
  4141. {
  4142. g_pShaderAPI->CopyTextureToRenderTargetEx( nRenderTargetID, m_pTextureHandles[0], pSrcRect, pDstRect );
  4143. }
  4144. }
  4145. ITexture *CTexture::GetEmbeddedTexture( int nIndex )
  4146. {
  4147. return ( nIndex == 0 ) ? this : NULL;
  4148. }
  4149. //-----------------------------------------------------------------------------
  4150. // Helper method to initialize texture bits in desired state.
  4151. //-----------------------------------------------------------------------------
  4152. #if defined( _GAMECONSOLE )
  4153. bool CTexture::ClearTexture( int r, int g, int b, int a )
  4154. {
  4155. Assert( IsProcedural() || IsRenderTarget() );
  4156. if( !HasBeenAllocated() )
  4157. return false;
  4158. if ( m_ImageFormat == IMAGE_FORMAT_D16 || m_ImageFormat == IMAGE_FORMAT_D24S8 || m_ImageFormat == IMAGE_FORMAT_D24FS8 || m_ImageFormat == IMAGE_FORMAT_R32F )
  4159. {
  4160. // not supporting non-rgba textures
  4161. return true;
  4162. }
  4163. CPixelWriter writer;
  4164. g_pShaderAPI->ModifyTexture( m_pTextureHandles[0] );
  4165. if ( !g_pShaderAPI->TexLock( 0, 0, 0, 0, m_nActualWidth, m_nActualHeight, writer ) )
  4166. return false;
  4167. writer.Seek( 0, 0 );
  4168. for ( int j = 0; j < m_nActualHeight; ++j )
  4169. {
  4170. for ( int k = 0; k < m_nActualWidth; ++k )
  4171. {
  4172. writer.WritePixel( r, g, b, a );
  4173. }
  4174. }
  4175. g_pShaderAPI->TexUnlock();
  4176. return true;
  4177. }
  4178. #endif
  4179. #if defined( _X360 )
  4180. bool CTexture::CreateRenderTargetSurface( int width, int height, ImageFormat format, bool bSameAsTexture, RTMultiSampleCount360_t multiSampleCount )
  4181. {
  4182. Assert( IsRenderTarget() && m_nFrameCount > 1 );
  4183. if ( bSameAsTexture )
  4184. {
  4185. // use RT texture configuration
  4186. width = m_nActualWidth;
  4187. height = m_nActualHeight;
  4188. format = m_ImageFormat;
  4189. }
  4190. // RT surface is expected at end of array
  4191. m_pTextureHandles[m_nFrameCount-1] = g_pShaderAPI->CreateRenderTargetSurface( width, height, format, multiSampleCount, GetName(), TEXTURE_GROUP_RENDER_TARGET_SURFACE );
  4192. return ( m_pTextureHandles[m_nFrameCount-1] != INVALID_SHADERAPI_TEXTURE_HANDLE );
  4193. }
  4194. #endif
  4195. void CTexture::DeleteIfUnreferenced()
  4196. {
  4197. if ( m_nRefCount > 0 )
  4198. return;
  4199. TextureManager()->RemoveTexture( this );
  4200. }
  4201. //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
  4202. void CTexture::SwapContents( ITexture *pOther )
  4203. {
  4204. if( (pOther == NULL) || (pOther == this) )
  4205. return;
  4206. ICallQueue *pCallQueue = materials->GetRenderContext()->GetCallQueue();
  4207. if ( pCallQueue )
  4208. {
  4209. pCallQueue->QueueCall( this, &CTexture::SwapContents, pOther );
  4210. return;
  4211. }
  4212. AssertMsg( dynamic_cast<CTexture *>(pOther) != NULL, "Texture swapping broken" );
  4213. CTexture *pOtherAsCTexture = (CTexture *)pOther;
  4214. CTexture *pTemp = (CTexture *)stackalloc( sizeof( CTexture ) );
  4215. //swap everything
  4216. memcpy( (void *)pTemp, (void *)this, sizeof( CTexture ) );
  4217. memcpy( (void *)this, (void *)pOtherAsCTexture, sizeof( CTexture ) );
  4218. memcpy( (void *)pOtherAsCTexture, (void *)pTemp, sizeof( CTexture ) );
  4219. //we have the other's name, give it back
  4220. memcpy( &pOtherAsCTexture->m_Name, &m_Name, sizeof( m_Name ) );
  4221. //pTemp still has our name
  4222. memcpy( &m_Name, &pTemp->m_Name, sizeof( m_Name ) );
  4223. }
  4224. void CTexture::MarkAsPreloaded( bool bSet )
  4225. {
  4226. if ( bSet )
  4227. {
  4228. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_PRELOADED;
  4229. }
  4230. else
  4231. {
  4232. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_PRELOADED;
  4233. }
  4234. }
  4235. bool CTexture::IsPreloaded() const
  4236. {
  4237. return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_PRELOADED ) != 0 );
  4238. }
  4239. void CTexture::MarkAsExcluded( bool bSet, int nDimensionsLimit, bool bMarkAsTrumpedExclude )
  4240. {
  4241. if ( bSet )
  4242. {
  4243. // exclusion trumps picmipping
  4244. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE;
  4245. // unique exclusion state to identify maximum exclusion
  4246. m_nDesiredDimensionLimit = -1;
  4247. }
  4248. else
  4249. {
  4250. // not excluding, but can optionally picmip
  4251. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE;
  4252. m_nDesiredDimensionLimit = nDimensionsLimit;
  4253. }
  4254. if ( !bSet && nDimensionsLimit > 0 && bMarkAsTrumpedExclude )
  4255. {
  4256. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_FORCED_TO_EXCLUDE;
  4257. }
  4258. }
  4259. bool CTexture::IsTempExcluded() const
  4260. {
  4261. return ( ( m_nInternalFlags & ( TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE | TEXTUREFLAGSINTERNAL_TEMPEXCLUDED ) ) != 0 );
  4262. }
  4263. bool CTexture::CanBeTempExcluded() const
  4264. {
  4265. return ( m_nRefCount == 1 &&
  4266. m_nFrameCount == 1 &&
  4267. !IsError() &&
  4268. !IsRenderTarget() &&
  4269. !IsProcedural() &&
  4270. !IsCubeMap() );
  4271. }
  4272. bool CTexture::MarkAsTempExcluded( bool bSet, int nExcludedDimensionLimit )
  4273. {
  4274. if ( !CanBeTempExcluded() )
  4275. {
  4276. // not possible to temp exclude these
  4277. return false;
  4278. }
  4279. if ( bSet )
  4280. {
  4281. // temp exclusion can drive the texture to a smaller footprint
  4282. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE;
  4283. // unique exclusion state to identify exclusion
  4284. m_nDesiredTempDimensionLimit = nExcludedDimensionLimit > 0 ? nExcludedDimensionLimit : -1;
  4285. }
  4286. else
  4287. {
  4288. // no longer temp excluding, default to expected normal exclusion limit
  4289. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE;
  4290. m_nDesiredTempDimensionLimit = m_nDesiredDimensionLimit;
  4291. }
  4292. // temp excludes need to be tracked from normal excludes
  4293. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_TEMPEXCLUDE_UPDATE;
  4294. return true;
  4295. }
  4296. bool CTexture::UpdateExcludedState()
  4297. {
  4298. bool bRequiresDownload = false;
  4299. // temp excludes
  4300. bool bDesiredTempExclude = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE ) != 0;
  4301. bool bActualTempExclude = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPEXCLUDED ) != 0;
  4302. if ( bDesiredTempExclude || bActualTempExclude )
  4303. {
  4304. if ( m_nActualDimensionLimit != m_nDesiredTempDimensionLimit )
  4305. {
  4306. bRequiresDownload = true;
  4307. }
  4308. else
  4309. {
  4310. // temp excludes trump any normal exclude, and normal excludes are ignored until the temp state is cleared
  4311. return false;
  4312. }
  4313. }
  4314. // normal excludes
  4315. if ( m_nActualDimensionLimit != m_nDesiredDimensionLimit )
  4316. {
  4317. bRequiresDownload = true;
  4318. }
  4319. if ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_QUEUEDLOAD )
  4320. {
  4321. // already scheduled by the queued loader, the QL wins
  4322. // a fixup will occur later once the QL finishes
  4323. return false;
  4324. }
  4325. if ( bRequiresDownload )
  4326. {
  4327. if ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPEXCLUDE_UPDATE ) && g_MaterialSystem.IsLevelLoadingComplete() && mat_exclude_async_update.GetBool() )
  4328. {
  4329. // temp excludes are async downloaded ONLY in the middle of gameplay, otherwise they do the normal sync download
  4330. // the async download path is !!!ONLY!!! wired for highly constrained temp exclusions, not for general purpose texture downloading
  4331. ScheduleExcludeAsyncDownload();
  4332. }
  4333. else
  4334. {
  4335. // force the texture to re-download, causes the texture bits to match its desired exclusion state
  4336. Download();
  4337. }
  4338. }
  4339. return bRequiresDownload;
  4340. }
  4341. bool CTexture::IsForceExcluded() const
  4342. {
  4343. return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_FORCED_TO_EXCLUDE ) != 0 );
  4344. }
  4345. bool CTexture::ClearForceExclusion()
  4346. {
  4347. if ( !IsForceExcluded() )
  4348. return false;
  4349. // clear the forced exclusion state
  4350. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_FORCED_TO_EXCLUDE;
  4351. m_nDesiredDimensionLimit = 0;
  4352. return UpdateExcludedState();
  4353. }
  4354. bool CTexture::IsAsyncDone() const
  4355. {
  4356. // we only check for async completion on textures that were async downloaded
  4357. // this function gets called on textures that might be loaded normally sometimes, so it needs to handle that case
  4358. if ( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD )
  4359. {
  4360. return ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_ASYNC_DONE ) != 0 );
  4361. }
  4362. return true;
  4363. }
  4364. static void IOAsyncCallbackTexture( const FileAsyncRequest_t &asyncRequest, int numReadBytes, FSAsyncStatus_t asyncStatus )
  4365. {
  4366. // interpret the async error
  4367. AsyncTextureLoadError_t loaderError;
  4368. switch ( asyncStatus )
  4369. {
  4370. case FSASYNC_OK:
  4371. loaderError = ASYNCTEXTURE_LOADERROR_NONE;
  4372. break;
  4373. case FSASYNC_ERR_FILEOPEN:
  4374. loaderError = ASYNCTEXTURE_LOADERROR_FILEOPEN;
  4375. break;
  4376. default:
  4377. loaderError = ASYNCTEXTRUE_LOADERROR_READING;
  4378. }
  4379. g_MaterialSystem.OnAsyncTextureDataComplete( (AsyncTextureContext_t *)asyncRequest.pContext, asyncRequest.pData, numReadBytes, loaderError );
  4380. }
  4381. bool CTexture::ScheduleAsyncDownload()
  4382. {
  4383. if ( !( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD ) )
  4384. {
  4385. // only textures flagged of Async Download can be Async downloaded
  4386. Assert( 0 );
  4387. return false;
  4388. }
  4389. if ( m_hAsyncControl )
  4390. {
  4391. // already scheduled
  4392. // this atomically marks when it can be rescheduled
  4393. return false;
  4394. }
  4395. const char *pName;
  4396. pName = m_Name.String();
  4397. char cacheFileName[MATERIAL_MAX_PATH];
  4398. // resolve the relative texture filename to its absolute path
  4399. bool bIsUNCName = ( pName[0] == '/' && pName[1] == '/' && pName[2] != '/' );
  4400. if ( !bIsUNCName )
  4401. {
  4402. Q_snprintf( cacheFileName, sizeof( cacheFileName ), "materials/%s" TEXTURE_FNAME_EXTENSION, pName );
  4403. }
  4404. else
  4405. {
  4406. Q_snprintf( cacheFileName, sizeof( cacheFileName ), "%s" TEXTURE_FNAME_EXTENSION, pName );
  4407. }
  4408. bool bExists = g_pFullFileSystem->FileExists( cacheFileName, MaterialSystem()->GetForcedTextureLoadPathID() );
  4409. if ( !bExists )
  4410. {
  4411. // unexpected failure, file should have existed and was pre-qualified
  4412. // this texture cannot be async downloaded
  4413. return false;
  4414. }
  4415. pName = cacheFileName;
  4416. m_ResolvedFileName = cacheFileName;
  4417. // send down a context that identifies the texture state
  4418. // a texture can only have one outstanding async download operation in flight
  4419. AsyncTextureContext_t *pContext = new AsyncTextureContext_t;
  4420. pContext->m_pTexture = this;
  4421. pContext->m_nInternalFlags = m_nInternalFlags;
  4422. pContext->m_nDesiredTempDimensionLimit = m_nActualDimensionLimit;
  4423. pContext->m_nActualDimensionLimit = m_nActualDimensionLimit;
  4424. pContext->m_pVTFTexture = NULL;
  4425. // schedule the async using what should be the absolute path to the file
  4426. FileAsyncRequest_t asyncRequest;
  4427. asyncRequest.pszFilename = pName;
  4428. asyncRequest.priority = -1;
  4429. asyncRequest.flags = FSASYNC_FLAGS_ALLOCNOFREE;
  4430. asyncRequest.pContext = (void *)pContext;
  4431. asyncRequest.pfnCallback = IOAsyncCallbackTexture;
  4432. g_pFullFileSystem->AsyncRead( asyncRequest, &m_hAsyncControl );
  4433. return true;
  4434. }
  4435. bool CTexture::ScheduleExcludeAsyncDownload()
  4436. {
  4437. if ( !( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPEXCLUDE_UPDATE ) )
  4438. {
  4439. // NOOOOO!!!!! This cannot be used for any textures except highly constrained temp excludes that have been properly qualified.
  4440. // It cannot be re-purposed into pushing textures in.
  4441. Assert( 0 );
  4442. return false;
  4443. }
  4444. if ( m_hAsyncControl )
  4445. {
  4446. // already scheduled
  4447. // this atomically marks when it can be rescheduled
  4448. // the data delivery will adhere to the current desired exclude state (that may change before the data arrives multiple times)
  4449. return false;
  4450. }
  4451. // want to use the prior resolved absolute filename, this causes an i/o penalty hitch once the first time the filesystem search path resolves
  4452. // each additional operation avoids the SP penalty
  4453. const char *pName;
  4454. bool bResolved = false;
  4455. bool bExcluding = false;
  4456. if ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDEXCLUDE ) ||
  4457. ( ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE ) && m_nDesiredTempDimensionLimit <= 0 ) )
  4458. {
  4459. bExcluding = true;
  4460. if ( m_ExcludedResolvedFileName.IsValid() )
  4461. {
  4462. pName = m_ExcludedResolvedFileName.String();
  4463. bResolved = true;
  4464. }
  4465. else
  4466. {
  4467. // The replacement texture for an excluded resource is solid black.
  4468. // The better thing would be to have unique 8x8 representations.
  4469. // Can cheap out on this because a temp excluded resource (weapon) is not supposed to be rendering
  4470. // for any more than a few frames before it's restored data appears.
  4471. pName = "dev/dev_temp_exclude";
  4472. }
  4473. }
  4474. else
  4475. {
  4476. if ( m_ResolvedFileName.IsValid() )
  4477. {
  4478. pName = m_ResolvedFileName.String();
  4479. bResolved = true;
  4480. }
  4481. else
  4482. {
  4483. pName = m_Name.String();
  4484. }
  4485. }
  4486. char fullPath[MAX_PATH];
  4487. char cacheFileName[MATERIAL_MAX_PATH];
  4488. if ( !bResolved )
  4489. {
  4490. // resolve the relative texture filename to its absolute path
  4491. bool bIsUNCName = ( pName[0] == '/' && pName[1] == '/' && pName[2] != '/' );
  4492. if ( !bIsUNCName )
  4493. {
  4494. Q_snprintf( cacheFileName, sizeof( cacheFileName ), "materials/%s" TEXTURE_FNAME_EXTENSION, pName );
  4495. }
  4496. else
  4497. {
  4498. Q_snprintf( cacheFileName, sizeof( cacheFileName ), "%s" TEXTURE_FNAME_EXTENSION, pName );
  4499. }
  4500. // all 360 files are expected to be in zip, no need to search outside of zip
  4501. g_pFullFileSystem->RelativePathToFullPath( cacheFileName, MaterialSystem()->GetForcedTextureLoadPathID(), fullPath, sizeof( fullPath ), ( IsX360() ? FILTER_CULLNONPACK : FILTER_NONE ) );
  4502. bool bExists = V_IsAbsolutePath( fullPath );
  4503. if ( !bExists )
  4504. {
  4505. // unexpected failure, file should have existed and was pre-qualified
  4506. // this texture cannot be async downloaded
  4507. return false;
  4508. }
  4509. pName = fullPath;
  4510. if ( bExcluding )
  4511. {
  4512. m_ExcludedResolvedFileName = fullPath;
  4513. }
  4514. else
  4515. {
  4516. m_ResolvedFileName = fullPath;
  4517. }
  4518. }
  4519. // send down a context that identifies the texture state
  4520. // the latent async delivery will need to match the state that may have changed
  4521. // a texture can only have one outstanding async download operation in flight
  4522. // although the texture state may thrash, the actual i/o will not
  4523. AsyncTextureContext_t *pContext = new AsyncTextureContext_t;
  4524. pContext->m_pTexture = this;
  4525. pContext->m_nInternalFlags = m_nInternalFlags;
  4526. pContext->m_nDesiredTempDimensionLimit = m_nDesiredTempDimensionLimit;
  4527. pContext->m_nActualDimensionLimit = m_nActualDimensionLimit;
  4528. pContext->m_pVTFTexture = NULL;
  4529. // schedule the async using what should be the absolute path to the file
  4530. FileAsyncRequest_t asyncRequest;
  4531. asyncRequest.pszFilename = pName;
  4532. asyncRequest.priority = -1;
  4533. asyncRequest.flags = FSASYNC_FLAGS_ALLOCNOFREE;
  4534. asyncRequest.pContext = (void *)pContext;
  4535. asyncRequest.pfnCallback = IOAsyncCallbackTexture;
  4536. g_pFullFileSystem->AsyncRead( asyncRequest, &m_hAsyncControl );
  4537. return true;
  4538. }
  4539. //-----------------------------------------------------------------------------
  4540. // Note for async texture:
  4541. // -----------------------
  4542. // The download is done is 2 parts:
  4543. // * Generating the VTF
  4544. // * Using VTF to create the shader api texture (effectively the corresponding d3d resource)
  4545. // In order to reduce spikes on the main thread (cf CMaterialSystem::ServiceAsyncTextureLoads), the flMaxTimeMs
  4546. // limit has been introduced => you can safely exit after generating the VTF and resume it at a later date
  4547. //-----------------------------------------------------------------------------
  4548. bool CTexture::FinishAsyncDownload( AsyncTextureContext_t *pContext, void *pData, int nNumReadBytes, bool bAbort, float flMaxTimeMs )
  4549. {
  4550. // The temp exclusions/restores are expected/desgined to be serviced at the only safe interval at the end of the frame
  4551. // and end of any current QMS jobs before QMS queues and start on it's next frame. Texture downloading is not thead safe
  4552. // and does not need to be made so. Instead, while the texture access pattens are quiescent and stable, the download
  4553. // (expected to be few in number) is deferred to this safe interval.
  4554. Assert( ThreadInMainThread() );
  4555. // For non-exclude async downloads, never abort
  4556. if ( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD )
  4557. {
  4558. bAbort = false;
  4559. }
  4560. // aborting just discards the data
  4561. // whatever state the texture bits were in, they stay that way
  4562. bool bDownloadCompleted = true;
  4563. if ( !bAbort && g_pShaderAPI->CanDownloadTextures() )
  4564. {
  4565. // the delayed async nature of this download may have invalidated the original/expected state at the moment of queuing
  4566. // prevent an update of the texture to the wrong state
  4567. // the temp exclusion monitor is responsible for rescheduling
  4568. if ( ( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD ) || // general async download (non-exclude)
  4569. ( ( ( pContext->m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE ) == ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE ) ) &&
  4570. ( pContext->m_nDesiredTempDimensionLimit == m_nDesiredTempDimensionLimit ) ) )
  4571. {
  4572. // the download will put the texture in the expected state
  4573. MaterialLock_t hLock = MaterialSystem()->Lock();
  4574. if ( m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD )
  4575. {
  4576. bDownloadCompleted = DownloadAsyncTexture( pContext, pData, nNumReadBytes, flMaxTimeMs );
  4577. }
  4578. else
  4579. {
  4580. DownloadTexture( NULL, pData, nNumReadBytes );
  4581. }
  4582. MaterialSystem()->Unlock( hLock );
  4583. }
  4584. else
  4585. {
  4586. // the texture wants to be in a different state than this download can achieve
  4587. bool bDesiredTempExclude = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_SHOULDTEMPEXCLUDE ) != 0;
  4588. bool bActualTempExclude = ( m_nInternalFlags & TEXTUREFLAGSINTERNAL_TEMPEXCLUDED ) != 0;
  4589. if ( bDesiredTempExclude == bActualTempExclude && m_nDesiredTempDimensionLimit == m_nActualDimensionLimit )
  4590. {
  4591. // the current desired temp exclude state now matches the actual
  4592. // the discarded download does not need to happen
  4593. m_nInternalFlags &= ~TEXTUREFLAGSINTERNAL_TEMPEXCLUDE_UPDATE;
  4594. }
  4595. }
  4596. }
  4597. if ( bDownloadCompleted )
  4598. {
  4599. // ownership of the data is expected to have been handed off
  4600. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  4601. g_pFullFileSystem->AsyncRelease( m_hAsyncControl );
  4602. // texture can be rescheduled
  4603. m_hAsyncControl = NULL;
  4604. delete pContext;
  4605. if (m_nFlags & TEXTUREFLAGS_ASYNC_DOWNLOAD)
  4606. {
  4607. m_nInternalFlags |= TEXTUREFLAGSINTERNAL_ASYNC_DONE; // signal we're done
  4608. m_nFlags &= ~TEXTUREFLAGS_ASYNC_DOWNLOAD; // no longer want this flag
  4609. }
  4610. }
  4611. return bDownloadCompleted;
  4612. }
  4613. //////////////////////////////////////////////////////////////////////////
  4614. //
  4615. // Saving all the texture LOD modifications to content
  4616. //
  4617. //////////////////////////////////////////////////////////////////////////
  4618. #ifdef IS_WINDOWS_PC
  4619. static bool SetBufferValue( char *chTxtFileBuffer, char const *szLookupKey, char const *szNewValue )
  4620. {
  4621. bool bResult = false;
  4622. size_t lenTmp = strlen( szNewValue );
  4623. size_t nTxtFileBufferLen = strlen( chTxtFileBuffer );
  4624. for ( char *pch = chTxtFileBuffer;
  4625. ( NULL != ( pch = strstr( pch, szLookupKey ) ) );
  4626. ++ pch )
  4627. {
  4628. char *val = pch + strlen( szLookupKey );
  4629. if ( !V_isspace( *val ) )
  4630. continue;
  4631. else
  4632. ++ val;
  4633. char *pValStart = val;
  4634. // Okay, here comes the value
  4635. while ( *val && V_isspace( *val ) )
  4636. ++ val;
  4637. while ( *val && !V_isspace( *val ) )
  4638. ++ val;
  4639. char *pValEnd = val; // Okay, here ends the value
  4640. memmove( pValStart + lenTmp, pValEnd, chTxtFileBuffer + nTxtFileBufferLen + 1 - pValEnd );
  4641. memcpy( pValStart, szNewValue, lenTmp );
  4642. nTxtFileBufferLen += ( lenTmp - ( pValEnd - pValStart ) );
  4643. bResult = true;
  4644. }
  4645. if ( !bResult )
  4646. {
  4647. char *pchAdd = chTxtFileBuffer + nTxtFileBufferLen;
  4648. strcpy( pchAdd + strlen( pchAdd ), "\n" );
  4649. strcpy( pchAdd + strlen( pchAdd ), szLookupKey );
  4650. strcpy( pchAdd + strlen( pchAdd ), " " );
  4651. strcpy( pchAdd + strlen( pchAdd ), szNewValue );
  4652. strcpy( pchAdd + strlen( pchAdd ), "\n" );
  4653. bResult = true;
  4654. }
  4655. return bResult;
  4656. }
  4657. // Replaces the first occurrence of "szFindData" with "szNewData"
  4658. // Returns the remaining buffer past the replaced data or NULL if
  4659. // no replacement occurred.
  4660. static char * BufferReplace( char *buf, char const *szFindData, char const *szNewData )
  4661. {
  4662. size_t len = strlen( buf ), lFind = strlen( szFindData ), lNew = strlen( szNewData );
  4663. if ( char *pBegin = strstr( buf, szFindData ) )
  4664. {
  4665. memmove( pBegin + lNew, pBegin + lFind, buf + len - ( pBegin + lFind ) );
  4666. memmove( pBegin, szNewData, lNew );
  4667. return pBegin + lNew;
  4668. }
  4669. return NULL;
  4670. }
  4671. class CP4Requirement
  4672. {
  4673. public:
  4674. CP4Requirement();
  4675. ~CP4Requirement();
  4676. protected:
  4677. bool m_bLoadedModule;
  4678. CSysModule *m_pP4Module;
  4679. };
  4680. CP4Requirement::CP4Requirement() :
  4681. m_bLoadedModule( false ),
  4682. m_pP4Module( NULL )
  4683. {
  4684. if ( p4 )
  4685. return;
  4686. // load the p4 lib
  4687. m_pP4Module = Sys_LoadModule( "p4lib" );
  4688. m_bLoadedModule = true;
  4689. if ( m_pP4Module )
  4690. {
  4691. CreateInterfaceFn factory = Sys_GetFactory( m_pP4Module );
  4692. if ( factory )
  4693. {
  4694. p4 = ( IP4 * )factory( P4_INTERFACE_VERSION, NULL );
  4695. if ( p4 )
  4696. {
  4697. extern CreateInterfaceFn g_fnMatSystemConnectCreateInterface;
  4698. p4->Connect( g_fnMatSystemConnectCreateInterface );
  4699. p4->Init();
  4700. }
  4701. }
  4702. }
  4703. if ( !p4 )
  4704. {
  4705. Warning( "Can't load p4lib.dll\n" );
  4706. }
  4707. }
  4708. CP4Requirement::~CP4Requirement()
  4709. {
  4710. if ( m_bLoadedModule && m_pP4Module )
  4711. {
  4712. if ( p4 )
  4713. {
  4714. p4->Shutdown();
  4715. p4->Disconnect();
  4716. }
  4717. Sys_UnloadModule( m_pP4Module );
  4718. m_pP4Module = NULL;
  4719. p4 = NULL;
  4720. }
  4721. }
  4722. 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." );
  4723. 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 )
  4724. {
  4725. using namespace TextureLodOverride;
  4726. if ( args.ArgC() != 2 )
  4727. goto usage;
  4728. char const *szCmd = args.Arg( 1 );
  4729. Msg( "mat_texture_list_txlod_sync %s...\n", szCmd );
  4730. if ( !stricmp( szCmd, "reset" ) )
  4731. {
  4732. for ( int k = 0; k < s_OverrideMap.GetNumStrings(); ++ k )
  4733. {
  4734. char const *szTx = s_OverrideMap.String( k );
  4735. s_OverrideMap[ k ] = OverrideInfo(); // Reset the override info
  4736. // Force the texture LOD override to get re-processed
  4737. if ( ITexture *pTx = materials->FindTexture( szTx, "" ) )
  4738. pTx->ForceLODOverride( 0 );
  4739. else
  4740. Warning( " mat_texture_list_txlod_sync reset - texture '%s' no longer found.\n", szTx );
  4741. }
  4742. s_OverrideMap.Purge();
  4743. Msg("mat_texture_list_txlod_sync reset : completed.\n");
  4744. return;
  4745. }
  4746. else if ( !stricmp( szCmd, "save" ) )
  4747. {
  4748. CP4Requirement p4req;
  4749. if ( !p4 )
  4750. g_p4factory->SetDummyMode( true );
  4751. for ( int k = 0; k < s_OverrideMap.GetNumStrings(); ++ k )
  4752. {
  4753. char const *szTx = s_OverrideMap.String( k );
  4754. OverrideInfo oi = s_OverrideMap[ k ];
  4755. ITexture *pTx = materials->FindTexture( szTx, "" );
  4756. if ( !oi.x || !oi.y )
  4757. continue;
  4758. if ( !pTx )
  4759. {
  4760. Warning( " mat_texture_list_txlod_sync save - texture '%s' no longer found.\n", szTx );
  4761. continue;
  4762. }
  4763. int iMaxWidth = pTx->GetActualWidth(), iMaxHeight = pTx->GetActualHeight();
  4764. // Save maxwidth and maxheight
  4765. char chMaxWidth[20], chMaxHeight[20];
  4766. sprintf( chMaxWidth, "%d", iMaxWidth ), sprintf( chMaxHeight, "%d", iMaxHeight );
  4767. // We have the texture and path to its content
  4768. char chResolveName[ MAX_PATH ] = {0}, chResolveNameArg[ MAX_PATH ] = {0};
  4769. Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s" TEXTURE_FNAME_EXTENSION, szTx );
  4770. char *szTextureContentPath;
  4771. if ( !mat_texture_list_content_path.GetString()[0] )
  4772. {
  4773. szTextureContentPath = const_cast< char * >( g_pFullFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 ) );
  4774. if ( !szTextureContentPath )
  4775. {
  4776. Warning( " mat_texture_list_txlod_sync save - texture '%s' is not loaded from file system.\n", szTx );
  4777. continue;
  4778. }
  4779. if ( !BufferReplace( szTextureContentPath, "\\game\\", "\\content\\" ) ||
  4780. !BufferReplace( szTextureContentPath, "\\materials\\", "\\materialsrc\\" ) )
  4781. {
  4782. Warning( " mat_texture_list_txlod_sync save - texture '%s' cannot be mapped to content directory.\n", szTx );
  4783. continue;
  4784. }
  4785. }
  4786. else
  4787. {
  4788. V_strncpy( chResolveName, mat_texture_list_content_path.GetString(), MAX_PATH );
  4789. V_strncat( chResolveName, "/", MAX_PATH );
  4790. V_strncat( chResolveName, szTx, MAX_PATH );
  4791. V_strncat( chResolveName, TEXTURE_FNAME_EXTENSION, MAX_PATH );
  4792. szTextureContentPath = chResolveName;
  4793. }
  4794. // Figure out what kind of source content is there:
  4795. // 1. look for TGA - if found, get the txt file (if txt file missing, create one)
  4796. // 2. otherwise look for PSD - affecting psdinfo
  4797. // 3. else error
  4798. char *pExtPut = szTextureContentPath + strlen( szTextureContentPath ) - strlen( TEXTURE_FNAME_EXTENSION ); // compensating the TEXTURE_FNAME_EXTENSION(.vtf) extension
  4799. // 1.tga
  4800. sprintf( pExtPut, ".tga" );
  4801. if ( g_pFullFileSystem->FileExists( szTextureContentPath ) )
  4802. {
  4803. // Have tga - pump in the txt file
  4804. sprintf( pExtPut, ".txt" );
  4805. CUtlBuffer bufTxtFileBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  4806. g_pFullFileSystem->ReadFile( szTextureContentPath, 0, bufTxtFileBuffer );
  4807. for ( int k = 0; k < 1024; ++ k ) bufTxtFileBuffer.PutChar( 0 );
  4808. // Now fix maxwidth/maxheight settings
  4809. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxwidth", chMaxWidth );
  4810. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxheight", chMaxHeight );
  4811. bufTxtFileBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, strlen( ( char * ) bufTxtFileBuffer.Base() ) );
  4812. // Check out or add the file
  4813. g_p4factory->SetOpenFileChangeList( "Texture LOD Autocheckout" );
  4814. CP4AutoEditFile autop4_edit( szTextureContentPath );
  4815. // Save the file contents
  4816. if ( g_pFullFileSystem->WriteFile( szTextureContentPath, 0, bufTxtFileBuffer ) )
  4817. {
  4818. Msg(" '%s' : saved.\n", szTextureContentPath );
  4819. CP4AutoAddFile autop4_add( szTextureContentPath );
  4820. }
  4821. else
  4822. {
  4823. Warning( " '%s' : failed to save - set \"maxwidth %d maxheight %d\" manually.\n",
  4824. szTextureContentPath, iMaxWidth, iMaxHeight );
  4825. }
  4826. continue;
  4827. }
  4828. // 2.psd
  4829. sprintf( pExtPut, ".psd" );
  4830. if ( g_pFullFileSystem->FileExists( szTextureContentPath ) )
  4831. {
  4832. char chCommand[MAX_PATH];
  4833. char szTxtFileName[MAX_PATH] = {0};
  4834. GetModSubdirectory( "tmp_lod_psdinfo.txt", szTxtFileName, sizeof( szTxtFileName ) );
  4835. sprintf( chCommand, "/C psdinfo \"%s\" > \"%s\"", szTextureContentPath, szTxtFileName);
  4836. ShellExecute( NULL, NULL, "cmd.exe", chCommand, NULL, SW_HIDE );
  4837. Sleep( 200 );
  4838. CUtlBuffer bufTxtFileBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  4839. g_pFullFileSystem->ReadFile( szTxtFileName, 0, bufTxtFileBuffer );
  4840. for ( int k = 0; k < 1024; ++ k ) bufTxtFileBuffer.PutChar( 0 );
  4841. // Now fix maxwidth/maxheight settings
  4842. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxwidth", chMaxWidth );
  4843. SetBufferValue( ( char * ) bufTxtFileBuffer.Base(), "maxheight", chMaxHeight );
  4844. bufTxtFileBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, strlen( ( char * ) bufTxtFileBuffer.Base() ) );
  4845. // Check out or add the file
  4846. // Save the file contents
  4847. if ( g_pFullFileSystem->WriteFile( szTxtFileName, 0, bufTxtFileBuffer ) )
  4848. {
  4849. g_p4factory->SetOpenFileChangeList( "Texture LOD Autocheckout" );
  4850. CP4AutoEditFile autop4_edit( szTextureContentPath );
  4851. sprintf( chCommand, "/C psdinfo -write \"%s\" < \"%s\"", szTextureContentPath, szTxtFileName );
  4852. Sleep( 200 );
  4853. ShellExecute( NULL, NULL, "cmd.exe", chCommand, NULL, SW_HIDE );
  4854. Sleep( 200 );
  4855. Msg(" '%s' : saved.\n", szTextureContentPath );
  4856. CP4AutoAddFile autop4_add( szTextureContentPath );
  4857. }
  4858. else
  4859. {
  4860. Warning( " '%s' : failed to save - set \"maxwidth %d maxheight %d\" manually.\n",
  4861. szTextureContentPath, iMaxWidth, iMaxHeight );
  4862. }
  4863. continue;
  4864. }
  4865. // 3. - error
  4866. sprintf( pExtPut, "" );
  4867. {
  4868. Warning( " '%s' : doesn't specify a valid TGA or PSD file!\n", szTextureContentPath );
  4869. continue;
  4870. }
  4871. }
  4872. Msg("mat_texture_list_txlod_sync save : completed.\n");
  4873. return;
  4874. }
  4875. else
  4876. goto usage;
  4877. return;
  4878. usage:
  4879. Warning(
  4880. "Usage:\n"
  4881. " mat_texture_list_txlod_sync reset - resets all run-time changes to LOD overrides;\n"
  4882. " mat_texture_list_txlod_sync save - saves all changes to material content files.\n"
  4883. );
  4884. }
  4885. ConVar mat_texture_list_exclude_editing( "mat_texture_list_exclude_editing", "0" );
  4886. CON_COMMAND_F( mat_texture_list_exclude, "'load' - loads the exclude list file, 'reset' - resets all loaded exclude information, 'save' - saves exclude list file", FCVAR_DONTRECORD )
  4887. {
  4888. using namespace TextureLodOverride;
  4889. using namespace TextureLodExclude;
  4890. if ( args.ArgC() < 2 )
  4891. goto usage;
  4892. char const *szCmd = args.Arg( 1 );
  4893. if ( !stricmp( szCmd, "load" ) )
  4894. {
  4895. if ( args.ArgC() < 3 )
  4896. goto usage;
  4897. char const *szFile = args.Arg( 2 );
  4898. Msg( "mat_texture_list_exclude loading '%s'...\n", szFile );
  4899. // Read the file buffer
  4900. CUtlInplaceBuffer bufFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
  4901. if ( !g_pFullFileSystem->ReadFile( szFile, NULL, bufFile ) )
  4902. {
  4903. Warning( "Error: failed to load exclude file '%s'!\n", szFile );
  4904. return;
  4905. }
  4906. // Process the file
  4907. while ( char *pszLine = bufFile.InplaceGetLinePtr() )
  4908. {
  4909. // Skip empty lines
  4910. if ( !*pszLine || V_isspace( *pszLine ) ||
  4911. !V_isalnum( *pszLine ) )
  4912. continue;
  4913. // If the line starts with a digit, then it's LOD override
  4914. int nLodOverride = atoi( pszLine );
  4915. while ( V_isdigit( *pszLine ) )
  4916. ++ pszLine;
  4917. while ( V_isspace( *pszLine ) )
  4918. ++ pszLine;
  4919. // Skip malformed lines
  4920. if ( !V_isalpha( *pszLine ) )
  4921. continue;
  4922. // Record the exclude element
  4923. TextureLodExclude::Add( pszLine, nLodOverride );
  4924. }
  4925. for ( int k = 0; k < s_ExcludeMap.GetNumStrings(); ++ k )
  4926. {
  4927. char const *szTx = s_ExcludeMap.String( k );
  4928. // Force the texture LOD override to get re-processed
  4929. if ( ITexture *pTx = materials->FindTexture( szTx, "" ) )
  4930. pTx->Download( NULL );
  4931. }
  4932. Msg( "mat_texture_list_exclude loaded '%s'.\n", szFile );
  4933. // Set the var to designate exclude mode
  4934. int iMode = mat_texture_list_exclude_editing.GetInt();
  4935. mat_texture_list_exclude_editing.SetValue( MAX( 0, iMode ) + 1 );
  4936. return;
  4937. }
  4938. else if ( !stricmp( szCmd, "reset" ) )
  4939. {
  4940. Msg( "mat_texture_list_exclude reset...\n" );
  4941. CUtlStringMap< int > lstReload;
  4942. for ( int k = 0; k < s_OverrideMap.GetNumStrings(); ++ k )
  4943. {
  4944. char const *szTx = s_OverrideMap.String( k );
  4945. lstReload[ szTx ] = 1;
  4946. }
  4947. s_OverrideMap.Purge();
  4948. for ( int k = 0; k < s_ExcludeMap.GetNumStrings(); ++ k )
  4949. {
  4950. char const *szTx = s_ExcludeMap.String( k );
  4951. lstReload[ szTx ] = 1;
  4952. }
  4953. s_ExcludeMap.Purge();
  4954. for ( int k = 0; k < lstReload.GetNumStrings(); ++ k )
  4955. {
  4956. char const *szTx = lstReload.String( k );
  4957. // Force the texture LOD override to get re-processed
  4958. if ( ITexture *pTx = materials->FindTexture( szTx, "" ) )
  4959. pTx->Download( NULL );
  4960. }
  4961. Msg( "mat_texture_list_exclude reset : completed.\n" );
  4962. mat_texture_list_exclude_editing.SetValue( 0 );
  4963. return;
  4964. }
  4965. else if ( !stricmp( szCmd, "save" ) )
  4966. {
  4967. if ( args.ArgC() < 3 )
  4968. goto usage;
  4969. char const *szFile = args.Arg( 2 );
  4970. Msg( "mat_texture_list_exclude saving '%s'...\n", szFile );
  4971. // Read the file buffer
  4972. CUtlInplaceBuffer bufFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
  4973. // Write the buffer file (full excludes)
  4974. for ( int k = 0; k < s_ExcludeMap.GetNumStrings(); ++ k )
  4975. {
  4976. char const *szTx = s_ExcludeMap.String( k );
  4977. int x = s_ExcludeMap[ k ];
  4978. if ( !( x == 0 ) ) continue; // first pass, skip mips
  4979. bufFile.Printf( "%s\n", szTx );
  4980. }
  4981. // empty line
  4982. bufFile.Printf( "\n" );
  4983. // Write the buffer file (mips)
  4984. for ( int k = 0; k < s_ExcludeMap.GetNumStrings(); ++ k )
  4985. {
  4986. char const *szTx = s_ExcludeMap.String( k );
  4987. int x = s_ExcludeMap[ k ];
  4988. if ( !( x > 0 ) ) continue;
  4989. bufFile.Printf( "%d %s\n", x, szTx );
  4990. }
  4991. // Save out the buffer to file
  4992. if ( !g_pFullFileSystem->WriteFile( szFile, NULL, bufFile ) )
  4993. {
  4994. Warning( "Error: failed to save exclude file '%s'!\n", szFile );
  4995. return;
  4996. }
  4997. Msg( "mat_texture_list_exclude saved '%s'.\n", szFile );
  4998. return;
  4999. }
  5000. return;
  5001. usage:
  5002. Warning(
  5003. "Usage:\n"
  5004. " mat_texture_list_exclude load excludelistfile.lst - loads exclude list file;\n"
  5005. " mat_texture_list_exclude reset - resets loaded exclude list information;\n"
  5006. " mat_texture_list_exclude save excludelistfile.lst - saves exclude list file.\n"
  5007. );
  5008. }
  5009. #endif
  5010. bool CTextureImpl_GetTextureInformation( char const *szTextureName, MaterialTextureInfo_t &info )
  5011. {
  5012. #ifdef IS_WINDOWS_PC
  5013. info.iExcludeInformation = TextureLodExclude::Get( szTextureName );
  5014. return true;
  5015. #else
  5016. return false;
  5017. #endif
  5018. }