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.

358 lines
10 KiB

  1. //--------------------------------------------------------------------------------------
  2. // File: hl2stereo.h
  3. // Authors: John McDonald
  4. // Email: [email protected]
  5. //
  6. // Utility classes for stereo
  7. //
  8. // Copyright (c) 2009 NVIDIA Corporation. All rights reserved.
  9. //
  10. // NOTE: This file is provided as-is, with no warranty either expressed or implied.
  11. //--------------------------------------------------------------------------------------
  12. #pragma once
  13. #ifndef __HL2STEREO__
  14. #define __HL2STEREO__ 1
  15. #include "nvapi.h"
  16. namespace nv
  17. {
  18. namespace stereo
  19. {
  20. typedef struct _Nv_Stereo_Image_Header
  21. {
  22. unsigned int dwSignature;
  23. unsigned int dwWidth;
  24. unsigned int dwHeight;
  25. unsigned int dwBPP;
  26. unsigned int dwFlags;
  27. } NVSTEREOIMAGEHEADER, *LPNVSTEREOIMAGEHEADER;
  28. #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e //NV3D
  29. #define NVSTEREO_SWAP_EYES 0x00000001
  30. inline void PopulateTextureData( float *leftEye, float *rightEye, LPNVSTEREOIMAGEHEADER header, unsigned int width, unsigned int height, unsigned int pixelBytes, float eyeSep, float sep, float conv )
  31. {
  32. // Normally sep is in [0, 100], and we want the fractional part of 1.
  33. float finalSeparation = eyeSep * sep * 0.005f;
  34. leftEye[0] = -finalSeparation;
  35. leftEye[1] = conv;
  36. leftEye[2] = -1.0f;
  37. rightEye[0] = -leftEye[0];
  38. rightEye[1] = leftEye[1];
  39. rightEye[2] = -leftEye[2];
  40. // Fill the header
  41. header->dwSignature = NVSTEREO_IMAGE_SIGNATURE;
  42. header->dwWidth = width;
  43. header->dwHeight = height;
  44. header->dwBPP = pixelBytes * 8;
  45. header->dwFlags = 0;
  46. }
  47. // This is expensive...may take more than 1ms to return. Only call this once at startup.
  48. inline bool IsStereoEnabled()
  49. {
  50. NvU8 stereoEnabled = 0;
  51. if ( NVAPI_OK != NvAPI_Stereo_IsEnabled( &stereoEnabled ) )
  52. {
  53. // Only try to call initialize once here...doing this just in case their library always returns
  54. // one of the other error codes continually.
  55. static bool s_bFirstTime = true;
  56. if ( s_bFirstTime )
  57. {
  58. s_bFirstTime = false;
  59. NvAPI_Initialize();
  60. NvAPI_Stereo_CreateConfigurationProfileRegistryKey( NVAPI_STEREO_DX9_REGISTRY_PROFILE );
  61. if ( NVAPI_OK != NvAPI_Stereo_IsEnabled( &stereoEnabled ) )
  62. {
  63. return false;
  64. }
  65. }
  66. else
  67. {
  68. return false;
  69. }
  70. }
  71. return stereoEnabled != 0;
  72. }
  73. #ifndef NO_STEREO_D3D9
  74. // The D3D9 "Driver" for stereo updates, encapsulates the logic that is Direct3D9 specific.
  75. struct D3D9Type
  76. {
  77. typedef IDirect3DDevice9 Device;
  78. typedef IDirect3DTexture9 Texture;
  79. typedef IDirect3DSurface9 StagingResource;
  80. static const NV_STEREO_REGISTRY_PROFILE_TYPE RegistryProfileType = NVAPI_STEREO_DX9_REGISTRY_PROFILE;
  81. static const int StereoTexWidth = 8;
  82. static const int StereoTexHeight = 1;
  83. static const D3DFORMAT StereoTexFormat = D3DFMT_R32F;
  84. static const int StereoBytesPerPixel = 4;
  85. static StagingResource *CreateStagingResource( Device *pDevice, float eyeSep, float sep, float conv )
  86. {
  87. StagingResource *staging = 0;
  88. unsigned int stagingWidth = StereoTexWidth * 2;
  89. unsigned int stagingHeight = StereoTexHeight + 1;
  90. pDevice->CreateOffscreenPlainSurface( stagingWidth, stagingHeight, StereoTexFormat, D3DPOOL_SYSTEMMEM, &staging, NULL );
  91. if ( !staging )
  92. {
  93. return 0;
  94. }
  95. D3DLOCKED_RECT lr;
  96. staging->LockRect( &lr, NULL, 0 );
  97. unsigned char *sysData = ( unsigned char * ) lr.pBits;
  98. unsigned int sysMemPitch = stagingWidth * StereoBytesPerPixel;
  99. float *leftEyePtr = ( float * )sysData;
  100. float *rightEyePtr = leftEyePtr + StereoTexWidth;
  101. LPNVSTEREOIMAGEHEADER header = ( LPNVSTEREOIMAGEHEADER )( sysData + sysMemPitch );
  102. PopulateTextureData( leftEyePtr, rightEyePtr, header, stagingWidth, stagingHeight, StereoBytesPerPixel, eyeSep, sep, conv );
  103. staging->UnlockRect();
  104. return staging;
  105. }
  106. static void UpdateTextureFromStaging( Device *pDevice, Texture *tex, StagingResource *staging )
  107. {
  108. RECT stereoSrcRect;
  109. stereoSrcRect.top = 0;
  110. stereoSrcRect.bottom = StereoTexHeight;
  111. stereoSrcRect.left = 0;
  112. stereoSrcRect.right = StereoTexWidth;
  113. POINT stereoDstPoint;
  114. stereoDstPoint.x = 0;
  115. stereoDstPoint.y = 0;
  116. IDirect3DSurface9 *texSurface;
  117. tex->GetSurfaceLevel( 0, &texSurface );
  118. pDevice->UpdateSurface( staging, &stereoSrcRect, texSurface, &stereoDstPoint );
  119. texSurface->Release();
  120. }
  121. };
  122. #endif // NO_STEREO_D3D9
  123. #ifndef NO_STEREO_D3D10
  124. // The D3D10 "Driver" for stereo updates, encapsulates the logic that is Direct3D10 specific.
  125. struct D3D10Type
  126. {
  127. typedef ID3D10Device Device;
  128. typedef ID3D10Texture2D Texture;
  129. typedef ID3D10Texture2D StagingResource;
  130. static const NV_STEREO_REGISTRY_PROFILE_TYPE RegistryProfileType = NVAPI_STEREO_DX10_REGISTRY_PROFILE;
  131. static const int StereoTexWidth = 8;
  132. static const int StereoTexHeight = 1;
  133. static const DXGI_FORMAT StereoTexFormat = DXGI_FORMAT_R32_FLOAT;
  134. static const int StereoBytesPerPixel = 4;
  135. static StagingResource *CreateStagingResource( Device *pDevice, float eyeSep, float sep, float conv )
  136. {
  137. StagingResource *staging = 0;
  138. unsigned int stagingWidth = StereoTexWidth * 2;
  139. unsigned int stagingHeight = StereoTexHeight + 1;
  140. // Allocate the buffer sys mem data to write the stereo tag and stereo params
  141. D3D10_SUBRESOURCE_DATA sysData;
  142. sysData.SysMemPitch = StereoBytesPerPixel * stagingWidth;
  143. sysData.pSysMem = new unsigned char[sysData.SysMemPitch * stagingHeight];
  144. float *leftEyePtr = ( float * )sysData.pSysMem;
  145. float *rightEyePtr = leftEyePtr + StereoTexWidth;
  146. LPNVSTEREOIMAGEHEADER header = ( LPNVSTEREOIMAGEHEADER )( ( unsigned char * )sysData.pSysMem + sysData.SysMemPitch );
  147. PopulateTextureData( leftEyePtr, rightEyePtr, header, stagingWidth, stagingHeight, StereoBytesPerPixel, eyeSep, sep, conv );
  148. D3D10_TEXTURE2D_DESC desc;
  149. memset( &desc, 0, sizeof( D3D10_TEXTURE2D_DESC ) );
  150. desc.Width = stagingWidth;
  151. desc.Height = stagingHeight;
  152. desc.MipLevels = 1;
  153. desc.ArraySize = 1;
  154. desc.Format = StereoTexFormat;
  155. desc.SampleDesc.Count = 1;
  156. desc.Usage = D3D10_USAGE_DEFAULT;
  157. desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
  158. desc.CPUAccessFlags = 0;
  159. desc.MiscFlags = 0;
  160. pDevice->CreateTexture2D( &desc, &sysData, &staging );
  161. delete [] sysData.pSysMem;
  162. return staging;
  163. }
  164. static void UpdateTextureFromStaging( Device *pDevice, Texture *tex, StagingResource *staging )
  165. {
  166. D3D10_BOX stereoSrcBox;
  167. stereoSrcBox.front = 0;
  168. stereoSrcBox.back = 1;
  169. stereoSrcBox.top = 0;
  170. stereoSrcBox.bottom = StereoTexHeight;
  171. stereoSrcBox.left = 0;
  172. stereoSrcBox.right = StereoTexWidth;
  173. pDevice->CopySubresourceRegion( tex, 0, 0, 0, 0, staging, 0, &stereoSrcBox );
  174. }
  175. };
  176. #endif // NO_STEREO_D3D10
  177. // The HL2 Stereo class, which can work for either D3D9 or D3D10, depending on which type it's specialized for
  178. // Note that both types can live side-by-side in two seperate instances as well.
  179. // Also note that there are convenient typedefs below the class definition.
  180. template < class D3DType >
  181. class HL2Stereo
  182. {
  183. public:
  184. typedef typename D3DType Parms;
  185. typedef typename D3DType::Device Device;
  186. typedef typename D3DType::Texture Texture;
  187. typedef typename D3DType::StagingResource StagingResource;
  188. HL2Stereo() :
  189. mEyeSeparation( 0 ),
  190. mSeparation( 0 ),
  191. mConvergence( 0 ),
  192. mStereoHandle( 0 ),
  193. mInitialized( false ),
  194. mActive( false ),
  195. mDeviceLost( true ) // mDeviceLost is set to true to initialize the texture with good data at app startup.
  196. {
  197. NvAPI_Initialize();
  198. NvAPI_Stereo_CreateConfigurationProfileRegistryKey( D3DType::RegistryProfileType );
  199. }
  200. ~HL2Stereo()
  201. {
  202. if ( mStereoHandle )
  203. {
  204. NvAPI_Stereo_DestroyHandle( mStereoHandle );
  205. mStereoHandle = 0;
  206. }
  207. }
  208. void Init( Device *dev )
  209. {
  210. NvAPI_Stereo_CreateHandleFromIUnknown( dev, &mStereoHandle );
  211. // Set that we've initialized regardless --we'll only try to init once.
  212. mInitialized = true;
  213. }
  214. // Not const because we will update the various values if an update is needed.
  215. bool RequiresUpdate( bool deviceLost )
  216. {
  217. bool active = IsStereoActive();
  218. bool updateRequired;
  219. float eyeSep, sep, conv;
  220. if ( active )
  221. {
  222. if ( NVAPI_OK != NvAPI_Stereo_GetEyeSeparation( mStereoHandle, &eyeSep ) )
  223. return false;
  224. if ( NVAPI_OK != NvAPI_Stereo_GetSeparation( mStereoHandle, &sep ) )
  225. return false;
  226. if ( NVAPI_OK != NvAPI_Stereo_GetConvergence( mStereoHandle, &conv ) )
  227. return false;
  228. // clamp the convergence to prevent wallhack exploit
  229. if ( conv > 31.0f )
  230. {
  231. conv = 31.0f;
  232. NvAPI_Stereo_SetConvergence( mStereoHandle, conv );
  233. DevMsg( "[NVIDIA Stereo 3D] Clamping convergence: %.2f\n", conv);
  234. }
  235. updateRequired = ( eyeSep != mEyeSeparation )
  236. || ( sep != mSeparation )
  237. || ( conv != mConvergence )
  238. || ( active != mActive );
  239. }
  240. else
  241. {
  242. eyeSep = sep = conv = 0;
  243. updateRequired = active != mActive;
  244. }
  245. // If the device was lost and is now restored, need to update the texture contents again.
  246. updateRequired = updateRequired || ( !deviceLost && mDeviceLost );
  247. mDeviceLost = deviceLost;
  248. if ( updateRequired )
  249. {
  250. //Msg( "*** NV_STEREO - UpdateRequired == true\n" );
  251. mEyeSeparation = eyeSep;
  252. mSeparation = sep;
  253. mConvergence = conv;
  254. mActive = active;
  255. return true;
  256. }
  257. return false;
  258. }
  259. bool IsStereoActive() const
  260. {
  261. NvU8 stereoActive = 0;
  262. if ( NVAPI_OK != NvAPI_Stereo_IsActivated( mStereoHandle, &stereoActive ) )
  263. {
  264. return false;
  265. }
  266. return stereoActive != 0;
  267. }
  268. void UpdateStereoTexture( Device *dev, Texture *tex, bool deviceLost )
  269. {
  270. if ( !mInitialized )
  271. {
  272. Init( dev );
  273. }
  274. if ( !RequiresUpdate( deviceLost ) )
  275. {
  276. return;
  277. }
  278. DevMsg( "[NVIDIA Stereo 3D] UpdateStereoTexture: EyeSep: %.2f, Sep: %.2f, Conv: %.2f\n", mEyeSeparation, mSeparation, mConvergence);
  279. StagingResource *staging = D3DType::CreateStagingResource( dev, mEyeSeparation, mSeparation, mConvergence );
  280. if ( staging )
  281. {
  282. D3DType::UpdateTextureFromStaging( dev, tex, staging );
  283. staging->Release();
  284. }
  285. }
  286. private:
  287. float mEyeSeparation;
  288. float mSeparation;
  289. float mConvergence;
  290. StereoHandle mStereoHandle;
  291. bool mInitialized;
  292. bool mActive;
  293. bool mDeviceLost;
  294. };
  295. #ifndef NO_STEREO_D3D9
  296. typedef HL2Stereo< D3D9Type > HL2StereoD3D9;
  297. #endif
  298. #ifndef NO_STEREO_D3D10
  299. typedef HL2Stereo< D3D10Type > HL2StereoD3D10;
  300. #endif
  301. };
  302. };
  303. #endif /* __HL2STEREO__ */