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.

1025 lines
31 KiB

  1. //========= Copyright � 1996-2013, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Provide custom texture generation (compositing)
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "composite_texture.h"
  8. #include "vstdlib/jobthread.h"
  9. #include "materialsystem/base_visuals_data_processor.h"
  10. #include "materialsystem_global.h"
  11. #include "tier0/vprof.h"
  12. #include "keyvalues.h"
  13. #include "texturemanager.h"
  14. //#define WRITE_OUT_VTF_PRE_COMPRESS
  15. #ifdef WRITE_OUT_VTF_PRE_COMPRESS
  16. #include "filesystem.h"
  17. #endif
  18. ConVar mat_verbose_texture_gen( "mat_verbose_texture_gen", "0" );
  19. #define TEX_GEN_LOG( msg, ... ) \
  20. if ( mat_verbose_texture_gen.GetBool() ) \
  21. { \
  22. Msg( "TextureGeneration: " msg, ##__VA_ARGS__ ); \
  23. } \
  24. // NOTE: This has to be the last file included!
  25. #include "tier0/memdbgon.h"
  26. int CCompositeTexture::m_nTextureCount = 0;
  27. int s_nCompositeMaterialIndex = 0;
  28. static ConVar *s_mat_picmip = NULL;
  29. int GetMatPicMip()
  30. {
  31. if ( !s_mat_picmip )
  32. {
  33. s_mat_picmip = g_pCVar->FindVar( "mat_picmip" );
  34. }
  35. return ( s_mat_picmip && s_mat_picmip->GetInt() > 0 ) ? s_mat_picmip->GetInt() : 0;
  36. }
  37. // this should match the CompositeTextureRTSizes_t enum in composite_texture.h
  38. static SCompositeTextureRTData_t s_compositeTextureRTData[COMPOSITE_TEXTURE_RT_COUNT] =
  39. {
  40. // these should be sorted in descending size order
  41. #if !defined( PLATFORM_OSX )
  42. { "_rt_CustomMaterial2048", 2048, true, false, NULL },
  43. #endif
  44. { "_rt_CustomMaterial1024", 1024, true, false, NULL },
  45. { "_rt_CustomMaterial512", 512, true, false, NULL },
  46. { "_rt_CustomMaterial256", 256, false, false, NULL },
  47. { "_rt_CustomMaterial128", 128, false, false, NULL }
  48. };
  49. int GetRTIndex( int nSize )
  50. {
  51. int nClosestBigger = 0;
  52. for (int i = 0; i < COMPOSITE_TEXTURE_RT_COUNT; i++)
  53. {
  54. if ( s_compositeTextureRTData[i].nSize == nSize && s_compositeTextureRTData[i].bAvailable == true )
  55. {
  56. return i;
  57. }
  58. if ( s_compositeTextureRTData[i].nSize > nSize && s_compositeTextureRTData[i].bAvailable == true )
  59. {
  60. nClosestBigger = i;
  61. }
  62. }
  63. return nClosestBigger;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Inherited from ITextureRegenerator
  67. //-----------------------------------------------------------------------------
  68. // If generation is complete then this will copy the results over into the texture
  69. // pRect is ignored, this always does the whole texture
  70. void CCompositeTextureResult::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
  71. {
  72. TM_ZONE_FILTERED( TELEMETRY_LEVEL1, 200, TMZF_NONE, "%s", __FUNCTION__ );
  73. if ( m_pOwner && m_pOwner->GenerationComplete() )
  74. {
  75. TEX_GEN_LOG( "Finished generating texture %s... ", m_pOwner->GetName() );
  76. IVTFTexture *pResultVTF = m_pOwner->GetResultVTF();
  77. // we have already generated once, if we have results of an appropriate size, then copy them over, else signal regeneration
  78. if ( pResultVTF != NULL && ( pVTFTexture->Width() == ( m_pOwner->ActualSize() ) ) && ( pVTFTexture->Width() <= pResultVTF->Width() ) )
  79. {
  80. // handle the case where the destination texture is smaller than our result (can happen with mat_picmip change to values lower than we have RTs for)
  81. int nMipOffset = 0;
  82. if ( pVTFTexture->Width() < pResultVTF->Width() )
  83. {
  84. int nSourceSize = pResultVTF->Width();
  85. while ( nSourceSize > pVTFTexture->Width() )
  86. {
  87. nMipOffset++;
  88. nSourceSize >>= 1;
  89. }
  90. }
  91. // copy each mip over
  92. for (int nMip = 0; nMip < pResultVTF->MipCount(); nMip++)
  93. {
  94. unsigned char *pBuffer = pResultVTF->ImageData( 0, 0, nMip + nMipOffset );
  95. unsigned char *pImageData = pVTFTexture->ImageData( 0, 0, nMip );
  96. // this amounts to a memcpy, but deals with properly calculating the size for compressed textures
  97. ImageLoader::ConvertImageFormat( pBuffer, pResultVTF->Format(), pImageData, pVTFTexture->Format(), pResultVTF->Width() >> ( nMip + nMipOffset ), pResultVTF->Height() >> ( nMip + nMipOffset ) );
  98. }
  99. TEX_GEN_LOG( "SUCCESS.\n" );
  100. }
  101. else
  102. {
  103. TEX_GEN_LOG( "FAILURE: resulting vtf size missmatch, forcing regenerate.\n" );
  104. m_pOwner->ForceRegenerate();
  105. }
  106. }
  107. }
  108. void CCompositeTextureResult::Release()
  109. {
  110. if ( m_pTexture != NULL )
  111. {
  112. m_pTexture->SetTextureRegenerator( NULL, false );
  113. m_pTexture->DecrementReferenceCount();
  114. m_pTexture->DeleteIfUnreferenced();
  115. m_pTexture = NULL;
  116. }
  117. m_pOwner = NULL;
  118. }
  119. //
  120. // Composite Texture - used to make a custom texture using a Shader that can be used with custom materials
  121. //
  122. CCompositeTexture::CCompositeTexture( const CUtlBuffer &compareBlob, KeyValues *pCompositingMaterialKeyValues, CompositeTextureSize_t size, CompositeTextureFormat_t format, int nMaterialParamNameId, bool bSRGB, bool bIgnorePicMip )
  123. : m_size( size )
  124. , m_format( format )
  125. , m_nMaterialParamNameId( nMaterialParamNameId )
  126. , m_bSRGB( bSRGB )
  127. , m_nRegenerateStage( COMPOSITE_TEXTURE_STATE_NOT_STARTED )
  128. , m_pResultVTF( NULL )
  129. , m_ResultTexture( this )
  130. , m_bNeedsRegenerate( false )
  131. , m_bNeedsFinalize( true )
  132. , m_pScratchVTF( NULL )
  133. , m_pCustomMaterialRT( NULL )
  134. , m_pCompositingMaterial( NULL )
  135. , m_nLastFrameCount( 0 )
  136. , m_pPixelsReadEvent( NULL )
  137. , m_bIgnorePicMip( bIgnorePicMip )
  138. {
  139. for ( int i = 0; i < NUM_PRELOAD_TEXTURES; i++ )
  140. {
  141. m_pPreLoadTextures[ i ] = NULL;
  142. }
  143. m_compareBlob.CopyBuffer( compareBlob );
  144. // we need to copy this, because the passed in one was allocated outside materialsystem.dll
  145. m_pCompositingMaterialKeyValues = pCompositingMaterialKeyValues->MakeCopy();
  146. m_nActualSize = ( 1 << Size() ) >> ( m_bIgnorePicMip ? 0 : GetMatPicMip() );
  147. V_sprintf_safe( m_szTextureName, "composite_texture_%d_%d_%d_%d", m_nMaterialParamNameId, m_nActualSize, m_format, m_nTextureCount++ );
  148. }
  149. bool CCompositeTexture::Init()
  150. {
  151. int nRT = GetRTIndex( m_nActualSize );
  152. m_pCustomMaterialRT = materials->FindTexture( s_compositeTextureRTData[nRT].pName, TEXTURE_GROUP_RENDER_TARGET );
  153. if ( !m_pCustomMaterialRT->IsError() )
  154. {
  155. m_pCustomMaterialRT->AddRef();
  156. m_pScratchVTF = s_compositeTextureRTData[nRT].pScratch;
  157. }
  158. else
  159. {
  160. // release the error texture
  161. m_pCustomMaterialRT->Release();
  162. m_pCustomMaterialRT = NULL;
  163. Warning( "Error creating composite tex8ture! Could not find render target texture. \n" );
  164. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_COMPLETE;
  165. return false;
  166. }
  167. // it's possible for the result vtf to exist already in the case of regeneration, so we clean up the old one before making a new one
  168. if ( m_pResultVTF != NULL )
  169. {
  170. DestroyVTFTexture( m_pResultVTF );
  171. m_pResultVTF = NULL;
  172. }
  173. {
  174. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "CreateResultVTF" );
  175. // create the final result VTF that we'll keep around. It's compressed and used to copy over to the actual texture whenever the texture needs downloading
  176. m_pResultVTF = CreateVTFTexture();
  177. m_pResultVTF->Init( m_pScratchVTF->Width(), m_pScratchVTF->Height(), m_pScratchVTF->Depth(), ( Format() == COMPOSITE_TEXTURE_FORMAT_DXT1 ) ? IMAGE_FORMAT_DXT1_RUNTIME : IMAGE_FORMAT_DXT5_RUNTIME, TEXTUREFLAGS_ALL_MIPS, 1, m_pScratchVTF->MipCount() );
  178. }
  179. return true;
  180. }
  181. CCompositeTexture::~CCompositeTexture()
  182. {
  183. ReleasePreloadedTextures();
  184. if ( m_pCompositingMaterial != NULL )
  185. {
  186. m_pCompositingMaterial->DecrementReferenceCount();
  187. m_pCompositingMaterial->DeleteIfUnreferenced();
  188. m_pCompositingMaterial = NULL;
  189. }
  190. if ( m_pCompositingMaterialKeyValues != NULL )
  191. {
  192. m_pCompositingMaterialKeyValues->deleteThis();
  193. m_pCompositingMaterialKeyValues = NULL;
  194. }
  195. m_ResultTexture.Release();
  196. if ( m_pResultVTF != NULL )
  197. {
  198. DestroyVTFTexture(m_pResultVTF);
  199. m_pResultVTF = NULL;
  200. }
  201. m_pScratchVTF = NULL;
  202. if ( m_pCustomMaterialRT != NULL )
  203. {
  204. m_pCustomMaterialRT->Release();
  205. m_pCustomMaterialRT = NULL;
  206. }
  207. if ( m_pPixelsReadEvent != NULL )
  208. {
  209. delete m_pPixelsReadEvent;
  210. m_pPixelsReadEvent = NULL;
  211. }
  212. }
  213. void CCompositeTexture::ReleasePreloadedTextures()
  214. {
  215. for ( int i = 0; i < NUM_PRELOAD_TEXTURES; i++ )
  216. {
  217. if ( m_pPreLoadTextures[ i ] != NULL )
  218. {
  219. m_pPreLoadTextures[ i ]->DecrementReferenceCount();
  220. m_pPreLoadTextures[ i ]->DeleteIfUnreferenced();
  221. m_pPreLoadTextures[ i ] = NULL;
  222. }
  223. }
  224. }
  225. // this is called from the GenerationStep which is called from the generate thread
  226. void CCompositeTexture::GenerateComposite( void )
  227. {
  228. TM_ZONE_FILTERED( TELEMETRY_LEVEL1, 100, TMZF_NONE, "GenerateComposite" );
  229. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_NOT_STARTED )
  230. {
  231. TEX_GEN_LOG( "Loading texture for %s\n", GetName() );
  232. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_ASYNC_TEXTURE_LOAD;
  233. }
  234. else if ( m_pScratchVTF != NULL )
  235. {
  236. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_COPY_TO_VTF_COMPLETE )
  237. {
  238. // no longer need the RT
  239. if ( m_pCustomMaterialRT != NULL )
  240. {
  241. m_pCustomMaterialRT->Release();
  242. m_pCustomMaterialRT = NULL;
  243. }
  244. {
  245. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "MipGen" );
  246. m_pScratchVTF->GenerateMipmaps();
  247. }
  248. #ifdef WRITE_OUT_VTF_PRE_COMPRESS
  249. CUtlBuffer buf;
  250. m_pScratchVTF->Serialize(buf);
  251. char szTextureName[MAX_PATH];
  252. V_snprintf(szTextureName, MAX_PATH, "d:/temp/%s.vtf", m_szTextureName );
  253. FileHandle_t f = g_pFullFileSystem->Open(szTextureName, "wb", NULL);
  254. if ( f != FILESYSTEM_INVALID_HANDLE )
  255. {
  256. g_pFullFileSystem->Write( buf.Base(), buf.TellMaxPut(), f );
  257. g_pFullFileSystem->Close(f);
  258. }
  259. #endif
  260. {
  261. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "DXT Compress" );
  262. // compress and copy the Scratch VTF mip maps to the Result VTF
  263. for (int nMip = 0; nMip < m_pScratchVTF->MipCount(); nMip++)
  264. {
  265. unsigned char *pResultImageData = m_pResultVTF->ImageData( 0, 0, nMip );
  266. unsigned char *pScratchImageData = m_pScratchVTF->ImageData( 0, 0, nMip );
  267. ImageLoader::ConvertImageFormat( pScratchImageData, IMAGE_FORMAT_RGBA8888, pResultImageData, ( Format() == COMPOSITE_TEXTURE_FORMAT_DXT1 ) ? IMAGE_FORMAT_DXT1_RUNTIME : IMAGE_FORMAT_DXT5_RUNTIME, m_pResultVTF->Width() >> nMip, m_pResultVTF->Height() >> nMip );
  268. }
  269. }
  270. // done with the scratch VTF
  271. m_pScratchVTF = NULL;
  272. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_WAITING_FOR_MATERIAL_CLEANUP;
  273. }
  274. }
  275. }
  276. // This does one generation step. It's called from the generate thread
  277. void CCompositeTexture::GenerationStep( void )
  278. {
  279. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_REQUESTED_READ )
  280. {
  281. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "ReadPixelsEventWait" );
  282. // wait on ReadPixelsAsync() to signal up that it's completed
  283. if ( m_pPixelsReadEvent->Wait( 20 ) )
  284. {
  285. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_WAITING_FOR_GETRESULT;
  286. }
  287. }
  288. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_REQUESTED_GETRESULT )
  289. {
  290. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "ReadPixelsGetResultEventWait" );
  291. // wait on ReadPixelsAsyncGetResult() to signal up that it's completed
  292. if ( m_pPixelsReadEvent->Wait( 20 ) )
  293. {
  294. delete m_pPixelsReadEvent;
  295. m_pPixelsReadEvent = NULL;
  296. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_COPY_TO_VTF_COMPLETE;
  297. }
  298. }
  299. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_WAITING_FOR_ASYNC_TEXTURE_LOAD_FINISH )
  300. {
  301. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "WaitForAsyncTextureLoads" );
  302. bool bDone = true;
  303. for ( int i = 0; i < NUM_PRELOAD_TEXTURES; i++ )
  304. {
  305. if ( ( m_pPreLoadTextures[ i ] != NULL ) && !m_pPreLoadTextures[ i ]->IsAsyncDone() )
  306. {
  307. bDone = false;
  308. break;
  309. }
  310. }
  311. if ( bDone )
  312. {
  313. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_NEEDS_INIT;
  314. }
  315. }
  316. GenerateComposite();
  317. }
  318. void CCompositeTexture::ForceRegenerate( void )
  319. {
  320. // we need to recalculate m_nActualSize
  321. m_nActualSize = ( 1 << Size() ) >> ( m_bIgnorePicMip ? 0 : GetMatPicMip() );
  322. m_bNeedsRegenerate = true;
  323. }
  324. struct materialTextureParams
  325. {
  326. const char *m_pParamName;
  327. const char *m_pDefaultTexture;
  328. };
  329. void CCompositeTexture::DoAsyncTextureLoad( void )
  330. {
  331. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "DoAsyncTextureLoad" );
  332. materialTextureParams materialParams[ NUM_PRELOAD_TEXTURES ] =
  333. {
  334. // used by weapons
  335. { "$basetexture", nullptr },
  336. { "$exptexture", nullptr },
  337. { "$painttexture", nullptr },
  338. { "$maskstexture", nullptr },
  339. { "$aotexture", nullptr },
  340. { "$postexture", nullptr },
  341. { "$surfacetexture", nullptr },
  342. // used by gloves (character / custom_character shaders)
  343. { "$bumpmap", nullptr },
  344. { "$masks1", nullptr },
  345. { "$phongwarptexture", nullptr },
  346. { "$fresnelrangestexture", nullptr },
  347. { "$masks2", nullptr },
  348. { "$envmap", nullptr },
  349. { "$materialmask", nullptr },
  350. { "$ao", nullptr },
  351. { "$grunge", nullptr },
  352. { "$detail", nullptr },
  353. { "$detailnormal", nullptr },
  354. { "$pattern", nullptr },
  355. { "$noise", "models/weapons/customization/materials/noise" } // default set in custom character shader
  356. };
  357. //KeyValuesDumpAsDevMsg( m_pCompositingMaterialKeyValues, 1, 1 );
  358. // initiate async load of the textures needed for the compositing material
  359. for ( int i = 0; i < NUM_PRELOAD_TEXTURES; i++ )
  360. {
  361. const char *pszTexture = m_pCompositingMaterialKeyValues->GetString( materialParams[ i ].m_pParamName );
  362. if ( !( pszTexture && pszTexture[0] != 0 ) && materialParams[i].m_pDefaultTexture )
  363. {
  364. pszTexture = materialParams[i].m_pDefaultTexture;
  365. }
  366. if ( pszTexture && pszTexture[0] != 0 )
  367. {
  368. m_pPreLoadTextures[ i ] = TextureManager()->FindOrLoadTexture( pszTexture, TEXTURE_GROUP_COMPOSITE, TEXTUREFLAGS_ASYNC_DOWNLOAD );
  369. if ( !m_pPreLoadTextures[ i ]->IsError() )
  370. {
  371. m_pPreLoadTextures[ i ]->AddRef();
  372. }
  373. else
  374. {
  375. m_pPreLoadTextures[ i ] = NULL;
  376. DevMsg( "DoAsyncTextureLoad: Failed to preload texture: %s (%s)\n", pszTexture, materialParams[ i ].m_pParamName );
  377. }
  378. }
  379. }
  380. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_WAITING_FOR_ASYNC_TEXTURE_LOAD_FINISH;
  381. }
  382. void CCompositeTexture::CreateCompositingMaterial( void )
  383. {
  384. {
  385. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "CreateCompositingMat" );
  386. char szCompositeMaterialName[32];
  387. V_sprintf_safe( szCompositeMaterialName, "compositing_material_%d", s_nCompositeMaterialIndex++ );
  388. m_pCompositingMaterial = materials->CreateMaterial( szCompositeMaterialName, m_pCompositingMaterialKeyValues->MakeCopy() );
  389. if ( m_pCompositingMaterial && m_pCompositingMaterial->IsErrorMaterial() )
  390. {
  391. // release the error material
  392. m_pCompositingMaterial->Release();
  393. // we do not call DeleteIfUnreferenced here because it's the error material
  394. m_pCompositingMaterial = NULL;
  395. Warning( "Error creating compositing material! Marking composite complete and not rendering to RT...\n" );
  396. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_COMPLETE;
  397. return;
  398. }
  399. }
  400. {
  401. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "RefreshCompositingMat" );
  402. // this causes a precache of the material
  403. m_pCompositingMaterial->Refresh();
  404. }
  405. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_WAITING_FOR_RENDER_TO_RT;
  406. }
  407. void CCompositeTexture::RenderToRT( void )
  408. {
  409. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_WAITING_FOR_RENDER_TO_RT )
  410. {
  411. if ( materials->CanDownloadTextures() )
  412. {
  413. int resolutionX = m_pCustomMaterialRT->GetActualWidth();
  414. int resolutionY = m_pCustomMaterialRT->GetActualHeight();
  415. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "RenderToRT %d", resolutionX );
  416. //init render context and set the the custom weapon RT as the current target
  417. CMatRenderContextPtr pRenderContext( materials );
  418. pRenderContext->PushRenderTargetAndViewport( m_pCustomMaterialRT, 0, 0, resolutionX, resolutionY );
  419. //render a quad using the composite material to the custom weapon RT
  420. pRenderContext->DrawScreenSpaceRectangle( m_pCompositingMaterial, 0, 0, resolutionX, resolutionY, 0, 0, resolutionX - 1, resolutionY - 1, resolutionX, resolutionY );
  421. pRenderContext->PopRenderTargetAndViewport();
  422. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_RENDERED_TO_RT;
  423. }
  424. }
  425. }
  426. // this is just to have two frames between rendering and attempting to read back.
  427. void CCompositeTexture::AdvanceToReadRT( void )
  428. {
  429. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_RENDERED_TO_RT )
  430. {
  431. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_WAITING_FOR_READ_RT;
  432. }
  433. }
  434. void CCompositeTexture::ReadRT( void )
  435. {
  436. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_WAITING_FOR_READ_RT )
  437. {
  438. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "ReadRT" );
  439. if ( materials->CanDownloadTextures() )
  440. {
  441. int resolutionX = m_pCustomMaterialRT->GetActualWidth();
  442. int resolutionY = m_pCustomMaterialRT->GetActualHeight();
  443. unsigned char *pDestImage = m_pScratchVTF->ImageData( 0, 0, 0 );
  444. // queue up read pixels
  445. CMatRenderContextPtr pRenderContext( materials );
  446. m_pPixelsReadEvent = new CThreadEvent();
  447. pRenderContext->ReadPixelsAsync( 0, 0, resolutionX, resolutionY, pDestImage, IMAGE_FORMAT_RGBA8888, m_pCustomMaterialRT, m_pPixelsReadEvent );
  448. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_REQUESTED_READ;
  449. }
  450. }
  451. }
  452. void CCompositeTexture::GetReadRTResult( void )
  453. {
  454. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_WAITING_FOR_GETRESULT )
  455. {
  456. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "GetReadRTResult" );
  457. if ( materials->CanDownloadTextures() )
  458. {
  459. int resolutionX = m_pCustomMaterialRT->GetActualWidth();
  460. int resolutionY = m_pCustomMaterialRT->GetActualHeight();
  461. unsigned char *pDestImage = m_pScratchVTF->ImageData( 0, 0, 0 );
  462. // queue up read pixels
  463. CMatRenderContextPtr pRenderContext( materials );
  464. m_pPixelsReadEvent->Reset();
  465. pRenderContext->ReadPixelsAsyncGetResult( 0, 0, resolutionX, resolutionY, pDestImage, IMAGE_FORMAT_RGBA8888, m_pPixelsReadEvent );
  466. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_REQUESTED_GETRESULT;
  467. }
  468. }
  469. }
  470. void CCompositeTexture::CleanupCompositingMaterial( void )
  471. {
  472. if ( m_nRegenerateStage == COMPOSITE_TEXTURE_STATE_WAITING_FOR_MATERIAL_CLEANUP )
  473. {
  474. if ( m_pCompositingMaterial != NULL )
  475. {
  476. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "CleanupCompositingMat" );
  477. m_pCompositingMaterial->DecrementReferenceCount();
  478. m_pCompositingMaterial->DeleteIfUnreferenced();
  479. m_pCompositingMaterial = NULL;
  480. }
  481. ReleasePreloadedTextures(); // no longer need these
  482. m_nRegenerateStage = COMPOSITE_TEXTURE_STATE_COMPLETE;
  483. }
  484. }
  485. void CCompositeTexture::Usage( int &nTextures, int &nBackingTextures )
  486. {
  487. nTextures += ( m_ResultTexture.m_pTexture != NULL ) ? m_ResultTexture.m_pTexture->GetApproximateVidMemBytes() : 0;
  488. nBackingTextures += ( m_pResultVTF != NULL ) ? m_pResultVTF->ComputeTotalSize() : 0;
  489. }
  490. void CCompositeTexture::Finalize()
  491. {
  492. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "Finalize" );
  493. if ( m_ResultTexture.m_pTexture == NULL )
  494. {
  495. m_ResultTexture.m_pTexture = materials->CreateProceduralTexture( m_szTextureName, TEXTURE_GROUP_COMPOSITE, ( 1 << Size() ), ( 1 << Size() ),
  496. ( Format() == COMPOSITE_TEXTURE_FORMAT_DXT5 ) ? IMAGE_FORMAT_DXT5_RUNTIME : IMAGE_FORMAT_DXT1_RUNTIME,
  497. TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_ANISOTROPIC | ( ( m_bSRGB ) ? TEXTUREFLAGS_SRGB : 0 ) | TEXTUREFLAGS_SKIP_INITIAL_DOWNLOAD );
  498. m_ResultTexture.m_pTexture->SetTextureRegenerator( &m_ResultTexture );
  499. }
  500. m_ResultTexture.m_pTexture->Download();
  501. m_bNeedsFinalize = false;
  502. #if defined( DX_TO_GL_ABSTRACTION )
  503. // Free VTF, significant mem saving - after Downloand not used for anything other than CWeaponCSBase::SaveCustomMaterialsTextures()
  504. // forceregenerate ensures this is safe on loss of focus - see CCompositeTextureResult::RegenerateTextureBits()
  505. if ( m_pResultVTF != NULL )
  506. {
  507. DestroyVTFTexture( m_pResultVTF );
  508. m_pResultVTF = NULL;
  509. }
  510. #endif
  511. }
  512. bool CCompositeTexture::Compare( const SCompositeTextureInfo &textureInfo )
  513. {
  514. if (Size() == textureInfo.m_size &&
  515. Format() == textureInfo.m_format &&
  516. GetMaterialParamNameId() == textureInfo.m_nMaterialParamID &&
  517. IsSRGB() == textureInfo.m_bSRGB &&
  518. textureInfo.m_pVisualsDataProcessor->GetCompareObject()->Compare( GetVisualsDataCompareBlob() ) )
  519. {
  520. return true;
  521. }
  522. return false;
  523. }
  524. //
  525. // global weapon material generator
  526. // the game uses this to make/get a custom material for a weapon
  527. //
  528. CCompositeTextureGenerator::CCompositeTextureGenerator( void )
  529. : m_bGenerateThreadExit( false )
  530. , m_hGenerateThread( NULL )
  531. , m_pGunGrimeTexture( NULL )
  532. , m_pPaintWearTexture( NULL )
  533. {
  534. #ifndef DEDICATED
  535. m_pCompositeTextures.EnsureCapacity( 256 );
  536. m_pPendingCompositeTextures.EnsureCapacity( 256 );
  537. #endif
  538. }
  539. CCompositeTextureGenerator::~CCompositeTextureGenerator()
  540. {
  541. }
  542. // this is called at the end of each frame
  543. bool ProcessCompositeTextureGenerator( void )
  544. {
  545. return MaterialSystem()->GetCompositeTextureGenerator()->Process();
  546. }
  547. // this handles doing rendering and material refreshes for the regenerators, downloads textures and flags materials ready so they will draw,
  548. // triggers regenerations as needed, and handles swapping materials that are pending swap and ready, and cleans up materials that are no longer used
  549. bool CCompositeTextureGenerator::Process( void )
  550. {
  551. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ );
  552. bool bDidWork = false;
  553. for ( int i = m_pPendingCompositeTextures.Count() - 1; i >= 0; i-- )
  554. {
  555. CCompositeTexture *pTexture = m_pPendingCompositeTextures[ i ];
  556. if ( pTexture && !pTexture->IsReady() )
  557. {
  558. if ( pTexture->GenerationComplete() && pTexture->NeedsFinalize() )
  559. {
  560. pTexture->Finalize();
  561. // move from pending to final list
  562. m_pCompositeTextures.AddToTail( pTexture );
  563. m_pPendingCompositeTextures.Remove( i );
  564. break;
  565. }
  566. else if ( pTexture->NeedsAsyncTextureLoad() )
  567. {
  568. pTexture->DoAsyncTextureLoad();
  569. break;
  570. }
  571. else if ( pTexture->NeedsCompositingMaterial() )
  572. {
  573. pTexture->CreateCompositingMaterial();
  574. bDidWork = true;
  575. break;
  576. }
  577. else if ( pTexture->NeedsMaterialCleanup() )
  578. {
  579. pTexture->CleanupCompositingMaterial();
  580. break;
  581. }
  582. else if ( pTexture->NeedsRender() )
  583. {
  584. pTexture->RenderToRT();
  585. bDidWork = true;
  586. break;
  587. }
  588. else if ( pTexture->HasRendered() )
  589. {
  590. pTexture->AdvanceToReadRT();
  591. break;
  592. }
  593. else if ( pTexture->NeedsReadRT() )
  594. {
  595. pTexture->ReadRT();
  596. break;
  597. }
  598. else if ( pTexture->NeedsGetResult() )
  599. {
  600. pTexture->GetReadRTResult();
  601. bDidWork = true;
  602. break;
  603. }
  604. }
  605. }
  606. for ( int i = m_pCompositeTextures.Count() - 1; i >= 0; i-- )
  607. {
  608. CCompositeTexture *pTexture = m_pCompositeTextures[ i ];
  609. if ( pTexture )
  610. {
  611. // see if a material needs regeneration (it must have completed generation)
  612. if ( pTexture->GenerationComplete() && pTexture->NeedsRegenerate() )
  613. {
  614. // signal the regeneration, and set the material to not draw
  615. pTexture->Refresh();
  616. pTexture->Init();
  617. // move back to the pending list
  618. m_pPendingCompositeTextures.AddToTail( pTexture );
  619. m_pCompositeTextures.Remove( i );
  620. // add material to generation queue
  621. m_GenerateQueueMutex.Lock();
  622. m_pGenerateQueue.AddToTail( pTexture );
  623. m_GenerateQueueMutex.Unlock();
  624. }
  625. // clean up materials that are no longer used (we are the only reference)
  626. else if ( pTexture->ShouldRelease() )
  627. {
  628. pTexture->Release();
  629. m_pCompositeTextures.Remove( i );
  630. }
  631. }
  632. }
  633. return bDidWork;
  634. }
  635. bool CCompositeTextureGenerator::Init( void )
  636. {
  637. #ifndef DEDICATED
  638. for (int i = 0; i < COMPOSITE_TEXTURE_RT_COUNT; i++)
  639. {
  640. ITexture *pRT = MaterialSystem()->CreateNamedRenderTargetTextureEx2( s_compositeTextureRTData[i].pName, s_compositeTextureRTData[i].nSize, s_compositeTextureRTData[i].nSize, RT_SIZE_NO_CHANGE,
  641. IMAGE_FORMAT_RGBA8888, MATERIAL_RT_DEPTH_NONE, TEXTUREFLAGS_SINGLECOPY | ( ( s_compositeTextureRTData[i].bSRGB ) ? TEXTUREFLAGS_SRGB : 0 ) );
  642. if ( pRT->IsError() )
  643. {
  644. pRT->Release();
  645. pRT = NULL;
  646. }
  647. if ( pRT == NULL )
  648. {
  649. Warning( "Failed to create %d x %d render target for composite texturing!\n", s_compositeTextureRTData[i].nSize, s_compositeTextureRTData[i].nSize );
  650. }
  651. else
  652. {
  653. m_CompositeTextureManagerRTs[i].Init( pRT );
  654. s_compositeTextureRTData[i].bAvailable = true;
  655. s_compositeTextureRTData[i].pScratch = CreateVTFTexture();
  656. s_compositeTextureRTData[i].pScratch->Init( s_compositeTextureRTData[i].nSize, s_compositeTextureRTData[i].nSize, 1, IMAGE_FORMAT_RGBA8888, TEXTUREFLAGS_ALL_MIPS, 1 );
  657. }
  658. }
  659. // precache scratch and grime textures
  660. m_pGunGrimeTexture = TextureManager()->FindOrLoadTexture( "models/weapons/customization/shared/gun_grunge.vtf", TEXTURE_GROUP_COMPOSITE );
  661. if ( !m_pGunGrimeTexture->IsError() )
  662. {
  663. m_pGunGrimeTexture->AddRef();
  664. }
  665. else
  666. {
  667. m_pGunGrimeTexture = NULL;
  668. }
  669. m_pPaintWearTexture = TextureManager()->FindOrLoadTexture( "models/weapons/customization/shared/paint_wear.vtf", TEXTURE_GROUP_COMPOSITE );
  670. if ( !m_pPaintWearTexture->IsError() )
  671. {
  672. m_pPaintWearTexture->AddRef();
  673. }
  674. else
  675. {
  676. m_pPaintWearTexture = NULL;
  677. }
  678. MaterialSystem()->AddEndFramePriorToNextContextFunc( ::ProcessCompositeTextureGenerator );
  679. CreateGenerateThread();
  680. #endif
  681. return true;
  682. }
  683. void CCompositeTextureGenerator::Shutdown( void )
  684. {
  685. #ifndef DEDICATED
  686. DestroyGenerateThread();
  687. MaterialSystem()->RemoveEndFramePriorToNextContextFunc( ::ProcessCompositeTextureGenerator );
  688. DestroyTextures();
  689. for (int i = 0; i < COMPOSITE_TEXTURE_RT_COUNT; i++)
  690. {
  691. if ( s_compositeTextureRTData[i].bAvailable )
  692. {
  693. s_compositeTextureRTData[i].bAvailable = false;
  694. if ( s_compositeTextureRTData[i].pScratch != NULL )
  695. {
  696. DestroyVTFTexture( s_compositeTextureRTData[i].pScratch );
  697. s_compositeTextureRTData[i].pScratch = NULL;
  698. }
  699. m_CompositeTextureManagerRTs[i].Shutdown();
  700. }
  701. }
  702. if ( m_pPaintWearTexture )
  703. {
  704. m_pPaintWearTexture->DecrementReferenceCount();
  705. m_pPaintWearTexture->DeleteIfUnreferenced();
  706. m_pPaintWearTexture = NULL;
  707. }
  708. if ( m_pGunGrimeTexture )
  709. {
  710. m_pGunGrimeTexture->DecrementReferenceCount();
  711. m_pGunGrimeTexture->DeleteIfUnreferenced();
  712. m_pGunGrimeTexture = NULL;
  713. }
  714. #endif
  715. }
  716. ICompositeTexture *CCompositeTextureGenerator::GetCompositeTexture( const SCompositeTextureInfo &textureInfo, bool bIgnorePicMip, bool bAllowCreate )
  717. {
  718. return GetCompositeTexture( textureInfo.m_pVisualsDataProcessor, textureInfo.m_nMaterialParamID, textureInfo.m_size, textureInfo.m_format, textureInfo.m_bSRGB, bIgnorePicMip, bAllowCreate );
  719. }
  720. ICompositeTexture *CCompositeTextureGenerator::GetCompositeTexture( IVisualsDataProcessor *pVisualsDataProcessor, int nMaterialParamNameId, CompositeTextureSize_t size, CompositeTextureFormat_t format, bool bSRGB, bool bIgnorePicMip, bool bAllowCreate )
  721. {
  722. #if defined( DEDICATED ) || defined( DISABLE_CUSTOM_MATERIAL_GENERATION )
  723. return NULL;
  724. #endif
  725. if ( !pVisualsDataProcessor->HasCustomMaterial() )
  726. {
  727. return NULL;
  728. }
  729. CCompositeTexture *pTexture = NULL;
  730. // Look for an existing material match
  731. for ( int i = 0; i < m_pCompositeTextures.Count(); ++i )
  732. {
  733. CCompositeTexture *pCompareTexture = m_pCompositeTextures[ i ];
  734. if ( pCompareTexture->Format() == format &&
  735. pCompareTexture->Size() == size &&
  736. pCompareTexture->GetMaterialParamNameId() == nMaterialParamNameId &&
  737. pCompareTexture->IsSRGB() == bSRGB &&
  738. pVisualsDataProcessor->GetCompareObject()->Compare( pCompareTexture->GetVisualsDataCompareBlob() ) )
  739. {
  740. pTexture = m_pCompositeTextures[ i ];
  741. break;
  742. }
  743. }
  744. for ( int i = 0; i < m_pPendingCompositeTextures.Count(); ++i )
  745. {
  746. CCompositeTexture *pCompareTexture = m_pPendingCompositeTextures[ i ];
  747. if ( pCompareTexture->Format() == format &&
  748. pCompareTexture->Size() == size &&
  749. pCompareTexture->GetMaterialParamNameId() == nMaterialParamNameId &&
  750. pCompareTexture->IsSRGB() == bSRGB &&
  751. pVisualsDataProcessor->GetCompareObject()->Compare( pCompareTexture->GetVisualsDataCompareBlob() ) )
  752. {
  753. pTexture = m_pPendingCompositeTextures[ i ];
  754. break;
  755. }
  756. }
  757. if ( !pTexture && bAllowCreate )
  758. {
  759. KeyValues *pCompositeMaterialKeyValues = pVisualsDataProcessor->GenerateCompositeMaterialKeyValues( nMaterialParamNameId );
  760. if ( !pCompositeMaterialKeyValues )
  761. {
  762. return NULL;
  763. }
  764. pTexture = new CCompositeTexture( pVisualsDataProcessor->GetCompareObject()->GetCompareBlob(), pCompositeMaterialKeyValues, size, format, nMaterialParamNameId, bSRGB, bIgnorePicMip );
  765. pCompositeMaterialKeyValues->deleteThis(); // copied inside CCompositeTexture(), no longer needed
  766. if ( pTexture->Init() )
  767. {
  768. m_pPendingCompositeTextures.AddToTail( pTexture );
  769. // add texture to generation queue (handled in the generation thread)
  770. m_GenerateQueueMutex.Lock();
  771. m_pGenerateQueue.AddToHead( pTexture );
  772. m_GenerateQueueMutex.Unlock();
  773. }
  774. else
  775. {
  776. pTexture->Release();
  777. pTexture = NULL;
  778. }
  779. }
  780. // The texture may not be complete yet, but it will be completed over the next few frames via Process()
  781. return pTexture;
  782. }
  783. bool CCompositeTextureGenerator::ForceRegenerate( ICompositeTexture *pTextureInterface )
  784. {
  785. CCompositeTexture *pTexture = dynamic_cast< CCompositeTexture * >( pTextureInterface );
  786. if ( !pTexture )
  787. {
  788. return false;
  789. }
  790. int nTexture = m_pCompositeTextures.Find( pTexture );
  791. if ( nTexture != -1 )
  792. {
  793. // move back to the pending list
  794. m_pPendingCompositeTextures.AddToTail( pTexture );
  795. m_pCompositeTextures.Remove( nTexture );
  796. }
  797. else if ( m_pPendingCompositeTextures.Find( pTexture ) == -1 )
  798. {
  799. // texture isn't in either list, so we can't regenerate it
  800. return false;
  801. }
  802. pTexture->Refresh();
  803. // add texture to generation queue (handled in the generation thread)
  804. m_GenerateQueueMutex.Lock();
  805. m_pGenerateQueue.AddToTail( pTexture );
  806. m_GenerateQueueMutex.Unlock();
  807. return true;
  808. }
  809. void CCompositeTextureGenerator::DestroyTextures( void )
  810. {
  811. for ( int i = 0; i < m_pCompositeTextures.Count(); ++i )
  812. {
  813. CCompositeTexture *pTexture = m_pCompositeTextures[ i ];
  814. if ( pTexture )
  815. {
  816. pTexture->ReleaseResult();
  817. pTexture->Release();
  818. }
  819. }
  820. m_pCompositeTextures.RemoveAll();
  821. for ( int i = 0; i < m_pPendingCompositeTextures.Count(); ++i )
  822. {
  823. CCompositeTexture *pTexture = m_pPendingCompositeTextures[ i ];
  824. if ( pTexture )
  825. {
  826. pTexture->ReleaseResult();
  827. pTexture->Release();
  828. }
  829. }
  830. m_pPendingCompositeTextures.RemoveAll();
  831. }
  832. void CCompositeTextureGenerator::CreateGenerateThread()
  833. {
  834. // kick off a thread to do custom texture generation
  835. m_bGenerateThreadExit = false;
  836. m_hGenerateThread = ThreadExecuteSolo( "CompositeTextureGenerationThread", this, &CCompositeTextureGenerator::GenerateThread );
  837. }
  838. void CCompositeTextureGenerator::DestroyGenerateThread()
  839. {
  840. if ( m_hGenerateThread )
  841. {
  842. // remove all the queued materials from the generate queue
  843. m_GenerateQueueMutex.Lock();
  844. m_pGenerateQueue.RemoveAll();
  845. m_GenerateQueueMutex.Unlock();
  846. m_bGenerateThreadExit = true;
  847. ThreadJoin( m_hGenerateThread );
  848. ReleaseThreadHandle( m_hGenerateThread );
  849. m_hGenerateThread = NULL;
  850. }
  851. }
  852. void CCompositeTextureGenerator::GenerateThread()
  853. {
  854. while ( !m_bGenerateThreadExit )
  855. {
  856. CCompositeTexture *pTexture = NULL;
  857. // pull a material that needs generation off the queue (if any exist)
  858. m_GenerateQueueMutex.Lock();
  859. if ( m_pGenerateQueue.Count() > 0 )
  860. {
  861. pTexture = m_pGenerateQueue.Element( m_pGenerateQueue.Head() );
  862. m_pGenerateQueue.Remove( m_pGenerateQueue.Head() );
  863. }
  864. m_GenerateQueueMutex.Unlock();
  865. if ( pTexture )
  866. {
  867. while ( !m_bGenerateThreadExit && !pTexture->GenerationComplete() )
  868. {
  869. pTexture->GenerationStep();
  870. ThreadSleep( 1 );
  871. }
  872. // wait for the current texture to finalize (in the main thread) before going to the next one
  873. while ( !m_bGenerateThreadExit && pTexture->NeedsFinalize() && pTexture->GenerationComplete() )
  874. {
  875. ThreadSleep( 1 );
  876. }
  877. }
  878. else
  879. {
  880. ThreadSleep( 1 );
  881. }
  882. }
  883. m_GenerateQueueMutex.Lock();
  884. m_pGenerateQueue.RemoveAll();
  885. m_GenerateQueueMutex.Unlock();
  886. }