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

245 lines
7.3 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #if defined( REPLAY_ENABLED )
  8. #include "materialsystem/itexture.h"
  9. #include "materialsystem/imaterialsystem.h"
  10. #include "replay/iclientreplaycontext.h"
  11. #include "replay/ireplayscreenshotmanager.h"
  12. #include "replay_screenshot.h"
  13. #include "view.h"
  14. #include "filesystem.h"
  15. #include "view_shared.h"
  16. #include "iviewrender.h"
  17. #include "fasttimer.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. //-----------------------------------------------------------------------------
  21. extern IClientReplayContext *g_pClientReplayContext;
  22. ITexture *CReplayScreenshotTaker::m_pScreenshotTarget;
  23. //-----------------------------------------------------------------------------
  24. // Purpose:
  25. //-----------------------------------------------------------------------------
  26. CReplayScreenshotTaker::CReplayScreenshotTaker( IViewRender *pViewRender, CViewSetup &viewSetup )
  27. : m_pViewRender( pViewRender ),
  28. m_View( viewSetup )
  29. {
  30. m_pUnpaddedPixels = NULL;
  31. m_pPaddedPixels = NULL;
  32. m_pVTFPixels = NULL;
  33. m_pVTFTexture = NULL;
  34. m_pBuffer = NULL;
  35. if ( !m_pScreenshotTarget )
  36. return;
  37. m_aPaddedDims[ 0 ] = m_pScreenshotTarget->GetActualWidth();
  38. m_aPaddedDims[ 1 ] = m_pScreenshotTarget->GetActualHeight();
  39. g_pClientReplayContext->GetScreenshotManager()->GetUnpaddedScreenshotSize( m_aUnpaddedDims[ 0 ], m_aUnpaddedDims[ 1 ] );
  40. // Calculate sizes
  41. int nUnpaddedSize = 3 * m_aUnpaddedDims[ 0 ] * m_aUnpaddedDims[ 1 ];
  42. int nPaddedSize = 3 * m_aPaddedDims[ 0 ] * m_aPaddedDims[ 1 ];
  43. // Allocate for padded & unpadded pixel data
  44. m_pUnpaddedPixels = new uint8[ nUnpaddedSize ];
  45. m_pPaddedPixels = new uint8[ nPaddedSize ];
  46. // White out the entire padded image
  47. V_memset( m_pPaddedPixels, 255, nPaddedSize );
  48. // Create the VTF
  49. #ifndef _X360
  50. IVTFTexture *pVTFTexture = CreateVTFTexture();
  51. const int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_SRGB;
  52. if ( !pVTFTexture->Init( m_aPaddedDims[ 0 ], m_aPaddedDims[ 1 ], 1, IMAGE_FORMAT_RGB888, nFlags, 1, 1 ) )
  53. return;
  54. m_pVTFTexture = pVTFTexture;
  55. #else
  56. m_pVTFTexture = NULL;
  57. #endif // _X360
  58. // Allocate pixels for the output buffer
  59. int nVTFSize = 1024 + ( 3 * m_aPaddedDims[ 0 ] * m_aPaddedDims[ 1 ] );
  60. m_pVTFPixels = new uint8[ nVTFSize ];
  61. m_pBuffer = new CUtlBuffer( m_pVTFPixels, nVTFSize );
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose:
  65. //-----------------------------------------------------------------------------
  66. CReplayScreenshotTaker::~CReplayScreenshotTaker()
  67. {
  68. delete [] m_pUnpaddedPixels;
  69. delete [] m_pPaddedPixels;
  70. delete [] m_pVTFPixels;
  71. #ifndef _X360
  72. DestroyVTFTexture( m_pVTFTexture );
  73. #endif // _X360
  74. delete m_pBuffer;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose: takes a screenshot for the replay system
  78. //-----------------------------------------------------------------------------
  79. void CReplayScreenshotTaker::TakeScreenshot( WriteReplayScreenshotParams_t &params )
  80. {
  81. if ( !m_pViewRender )
  82. return;
  83. CFastTimer timer;
  84. ConVarRef replay_debug( "replay_debug" );
  85. bool bDbg = replay_debug.IsValid() && replay_debug.GetBool();
  86. int width = params.m_nWidth;
  87. int height = params.m_nHeight;
  88. CMatRenderContextPtr pRenderContext( materials );
  89. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  90. pRenderContext->PushMatrix();
  91. pRenderContext->MatrixMode( MATERIAL_VIEW );
  92. pRenderContext->PushMatrix();
  93. extern bool g_bRenderingScreenshot;
  94. g_bRenderingScreenshot = true;
  95. // Push back buffer on the stack with small viewport
  96. pRenderContext->PushRenderTargetAndViewport( m_pScreenshotTarget, 0, 0, width, height );
  97. // setup the view to render
  98. CViewSetup viewSetup = m_View;
  99. viewSetup.x = 0;
  100. viewSetup.y = 0;
  101. viewSetup.width = width;
  102. viewSetup.height = height;
  103. viewSetup.fov = ScaleFOVByWidthRatio( m_View.fov, ( (float)width / (float)height ) / ( 4.0f / 3.0f ) );
  104. viewSetup.m_bRenderToSubrectOfLargerScreen = true;
  105. // Setup view origin/angles
  106. if ( params.m_pOrigin )
  107. {
  108. viewSetup.origin = *params.m_pOrigin;
  109. }
  110. if ( params.m_pAngles )
  111. {
  112. viewSetup.angles = *params.m_pAngles;
  113. }
  114. timer.Start();
  115. // draw out the scene - don't draw the HUD or the viewmodel
  116. m_pViewRender->RenderView( viewSetup, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, 0 );
  117. timer.End();
  118. if ( bDbg ) Warning( "Screenshot RenderView(): %.4f s\n", timer.GetDuration().GetSeconds() );
  119. timer.Start();
  120. // Get Bits from the material system
  121. pRenderContext->ReadPixels( 0, 0, width, height, m_pUnpaddedPixels, IMAGE_FORMAT_RGB888 );
  122. timer.End();
  123. if ( bDbg ) Warning( "Screenshot ReadPixels(): %.4f s\n", timer.GetDuration().GetSeconds() );
  124. // Some stuff to be setup dependent on padded vs. not padded
  125. int nSrcWidth, nSrcHeight;
  126. unsigned char *pSrcImage;
  127. // Setup dimensions as needed
  128. int nPaddedWidth = m_aPaddedDims[0];
  129. int nPaddedHeight = m_aPaddedDims[1];
  130. // Allocate
  131. unsigned char *pUnpaddedImage = m_pUnpaddedPixels;
  132. unsigned char *pPaddedImage = m_pPaddedPixels;
  133. timer.Start();
  134. // Copy over each row individually
  135. for ( int nRow = 0; nRow < height; ++nRow )
  136. {
  137. unsigned char *pDst = pPaddedImage + 3 * ( nRow * nPaddedWidth );
  138. const unsigned char *pSrc = pUnpaddedImage + 3 * ( nRow * width );
  139. V_memcpy( pDst, pSrc, 3 * width );
  140. }
  141. timer.End();
  142. if ( bDbg ) Warning( "Screenshot copy image: %.4f s\n", timer.GetDuration().GetSeconds() );
  143. // Setup source data
  144. nSrcWidth = nPaddedWidth;
  145. nSrcHeight = nPaddedHeight;
  146. pSrcImage = pPaddedImage;
  147. if ( !m_pVTFTexture )
  148. return;
  149. // Copy the image data over to the VTF
  150. unsigned char *pDestBits = m_pVTFTexture->ImageData();
  151. int nDstSize = nSrcWidth * nSrcHeight * 3;
  152. V_memcpy( pDestBits, pSrcImage, nDstSize );
  153. bool bWriteResult = true;
  154. // Reset put
  155. m_pBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  156. timer.Start();
  157. // Serialize to the buffer
  158. bWriteResult = m_pVTFTexture->Serialize( *m_pBuffer );
  159. timer.End();
  160. if ( bDbg ) Warning( "Screenshot VTF->Serialize(): %.4f s\n", timer.GetDuration().GetSeconds() );
  161. if ( !bWriteResult )
  162. {
  163. Warning( "Couldn't write Replay screenshot.\n" );
  164. bWriteResult = false;
  165. return;
  166. }
  167. // async write to disk (this will take ownership of the memory)
  168. char szPathedFileName[_MAX_PATH];
  169. Q_snprintf( szPathedFileName, sizeof(szPathedFileName), "//MOD/%s", params.m_pFilename );
  170. timer.Start();
  171. filesystem->AsyncWrite( szPathedFileName, m_pBuffer->Base(), m_pBuffer->TellPut(), false );
  172. timer.End();
  173. if ( bDbg ) Warning( "Screenshot AsyncWrite(): %.4f s\n", timer.GetDuration().GetSeconds() );
  174. // restore our previous state
  175. pRenderContext->PopRenderTargetAndViewport();
  176. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  177. pRenderContext->PopMatrix();
  178. pRenderContext->MatrixMode( MATERIAL_VIEW );
  179. pRenderContext->PopMatrix();
  180. g_bRenderingScreenshot = false;
  181. }
  182. void CReplayScreenshotTaker::CreateRenderTarget( IMaterialSystem *pMaterialSystem )
  183. {
  184. m_pScreenshotTarget = pMaterialSystem->CreateNamedRenderTargetTextureEx2( "rt_ReplayScreenshot", 0, 0, RT_SIZE_REPLAY_SCREENSHOT, IMAGE_FORMAT_RGB888, MATERIAL_RT_DEPTH_SEPARATE );
  185. m_pScreenshotTarget->AddRef(); // we will leak this ref, but only at shutdown of the app, which will be cleaned up then
  186. }
  187. #endif