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

//--------------------------------------------------------------------------------------
// File: hl2stereo.h
// Authors: John McDonald
// Email: [email protected]
//
// Utility classes for stereo
//
// Copyright (c) 2009 NVIDIA Corporation. All rights reserved.
//
// NOTE: This file is provided as-is, with no warranty either expressed or implied.
//--------------------------------------------------------------------------------------
#pragma once
#ifndef __HL2STEREO__
#define __HL2STEREO__ 1
#include "nvapi.h"
namespace nv
{
namespace stereo
{
typedef struct _Nv_Stereo_Image_Header
{
unsigned int dwSignature;
unsigned int dwWidth;
unsigned int dwHeight;
unsigned int dwBPP;
unsigned int dwFlags;
} NVSTEREOIMAGEHEADER, *LPNVSTEREOIMAGEHEADER;
#define NVSTEREO_IMAGE_SIGNATURE 0x4433564e //NV3D
#define NVSTEREO_SWAP_EYES 0x00000001
inline void PopulateTextureData( float *leftEye, float *rightEye, LPNVSTEREOIMAGEHEADER header, unsigned int width, unsigned int height, unsigned int pixelBytes, float eyeSep, float sep, float conv )
{
// Normally sep is in [0, 100], and we want the fractional part of 1.
float finalSeparation = eyeSep * sep * 0.005f;
leftEye[0] = -finalSeparation;
leftEye[1] = conv;
leftEye[2] = -1.0f;
rightEye[0] = -leftEye[0];
rightEye[1] = leftEye[1];
rightEye[2] = -leftEye[2];
// Fill the header
header->dwSignature = NVSTEREO_IMAGE_SIGNATURE;
header->dwWidth = width;
header->dwHeight = height;
header->dwBPP = pixelBytes * 8;
header->dwFlags = 0;
}
// This is expensive...may take more than 1ms to return. Only call this once at startup.
inline bool IsStereoEnabled()
{
NvU8 stereoEnabled = 0;
if ( NVAPI_OK != NvAPI_Stereo_IsEnabled( &stereoEnabled ) )
{
// Only try to call initialize once here...doing this just in case their library always returns
// one of the other error codes continually.
static bool s_bFirstTime = true;
if ( s_bFirstTime )
{
s_bFirstTime = false;
NvAPI_Initialize();
NvAPI_Stereo_CreateConfigurationProfileRegistryKey( NVAPI_STEREO_DX9_REGISTRY_PROFILE );
if ( NVAPI_OK != NvAPI_Stereo_IsEnabled( &stereoEnabled ) )
{
return false;
}
}
else
{
return false;
}
}
return stereoEnabled != 0;
}
#ifndef NO_STEREO_D3D9
// The D3D9 "Driver" for stereo updates, encapsulates the logic that is Direct3D9 specific.
struct D3D9Type
{
typedef IDirect3DDevice9 Device;
typedef IDirect3DTexture9 Texture;
typedef IDirect3DSurface9 StagingResource;
static const NV_STEREO_REGISTRY_PROFILE_TYPE RegistryProfileType = NVAPI_STEREO_DX9_REGISTRY_PROFILE;
static const int StereoTexWidth = 8;
static const int StereoTexHeight = 1;
static const D3DFORMAT StereoTexFormat = D3DFMT_R32F;
static const int StereoBytesPerPixel = 4;
static StagingResource *CreateStagingResource( Device *pDevice, float eyeSep, float sep, float conv )
{
StagingResource *staging = 0;
unsigned int stagingWidth = StereoTexWidth * 2;
unsigned int stagingHeight = StereoTexHeight + 1;
pDevice->CreateOffscreenPlainSurface( stagingWidth, stagingHeight, StereoTexFormat, D3DPOOL_SYSTEMMEM, &staging, NULL );
if ( !staging )
{
return 0;
}
D3DLOCKED_RECT lr;
staging->LockRect( &lr, NULL, 0 );
unsigned char *sysData = ( unsigned char * ) lr.pBits;
unsigned int sysMemPitch = stagingWidth * StereoBytesPerPixel;
float *leftEyePtr = ( float * )sysData;
float *rightEyePtr = leftEyePtr + StereoTexWidth;
LPNVSTEREOIMAGEHEADER header = ( LPNVSTEREOIMAGEHEADER )( sysData + sysMemPitch );
PopulateTextureData( leftEyePtr, rightEyePtr, header, stagingWidth, stagingHeight, StereoBytesPerPixel, eyeSep, sep, conv );
staging->UnlockRect();
return staging;
}
static void UpdateTextureFromStaging( Device *pDevice, Texture *tex, StagingResource *staging )
{
RECT stereoSrcRect;
stereoSrcRect.top = 0;
stereoSrcRect.bottom = StereoTexHeight;
stereoSrcRect.left = 0;
stereoSrcRect.right = StereoTexWidth;
POINT stereoDstPoint;
stereoDstPoint.x = 0;
stereoDstPoint.y = 0;
IDirect3DSurface9 *texSurface;
tex->GetSurfaceLevel( 0, &texSurface );
pDevice->UpdateSurface( staging, &stereoSrcRect, texSurface, &stereoDstPoint );
texSurface->Release();
}
};
#endif // NO_STEREO_D3D9
#ifndef NO_STEREO_D3D10
// The D3D10 "Driver" for stereo updates, encapsulates the logic that is Direct3D10 specific.
struct D3D10Type
{
typedef ID3D10Device Device;
typedef ID3D10Texture2D Texture;
typedef ID3D10Texture2D StagingResource;
static const NV_STEREO_REGISTRY_PROFILE_TYPE RegistryProfileType = NVAPI_STEREO_DX10_REGISTRY_PROFILE;
static const int StereoTexWidth = 8;
static const int StereoTexHeight = 1;
static const DXGI_FORMAT StereoTexFormat = DXGI_FORMAT_R32_FLOAT;
static const int StereoBytesPerPixel = 4;
static StagingResource *CreateStagingResource( Device *pDevice, float eyeSep, float sep, float conv )
{
StagingResource *staging = 0;
unsigned int stagingWidth = StereoTexWidth * 2;
unsigned int stagingHeight = StereoTexHeight + 1;
// Allocate the buffer sys mem data to write the stereo tag and stereo params
D3D10_SUBRESOURCE_DATA sysData;
sysData.SysMemPitch = StereoBytesPerPixel * stagingWidth;
sysData.pSysMem = new unsigned char[sysData.SysMemPitch * stagingHeight];
float *leftEyePtr = ( float * )sysData.pSysMem;
float *rightEyePtr = leftEyePtr + StereoTexWidth;
LPNVSTEREOIMAGEHEADER header = ( LPNVSTEREOIMAGEHEADER )( ( unsigned char * )sysData.pSysMem + sysData.SysMemPitch );
PopulateTextureData( leftEyePtr, rightEyePtr, header, stagingWidth, stagingHeight, StereoBytesPerPixel, eyeSep, sep, conv );
D3D10_TEXTURE2D_DESC desc;
memset( &desc, 0, sizeof( D3D10_TEXTURE2D_DESC ) );
desc.Width = stagingWidth;
desc.Height = stagingHeight;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = StereoTexFormat;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
pDevice->CreateTexture2D( &desc, &sysData, &staging );
delete [] sysData.pSysMem;
return staging;
}
static void UpdateTextureFromStaging( Device *pDevice, Texture *tex, StagingResource *staging )
{
D3D10_BOX stereoSrcBox;
stereoSrcBox.front = 0;
stereoSrcBox.back = 1;
stereoSrcBox.top = 0;
stereoSrcBox.bottom = StereoTexHeight;
stereoSrcBox.left = 0;
stereoSrcBox.right = StereoTexWidth;
pDevice->CopySubresourceRegion( tex, 0, 0, 0, 0, staging, 0, &stereoSrcBox );
}
};
#endif // NO_STEREO_D3D10
// The HL2 Stereo class, which can work for either D3D9 or D3D10, depending on which type it's specialized for
// Note that both types can live side-by-side in two seperate instances as well.
// Also note that there are convenient typedefs below the class definition.
template < class D3DType >
class HL2Stereo
{
public:
typedef typename D3DType Parms;
typedef typename D3DType::Device Device;
typedef typename D3DType::Texture Texture;
typedef typename D3DType::StagingResource StagingResource;
HL2Stereo() :
mEyeSeparation( 0 ),
mSeparation( 0 ),
mConvergence( 0 ),
mStereoHandle( 0 ),
mInitialized( false ),
mActive( false ),
mDeviceLost( true ) // mDeviceLost is set to true to initialize the texture with good data at app startup.
{
NvAPI_Initialize();
NvAPI_Stereo_CreateConfigurationProfileRegistryKey( D3DType::RegistryProfileType );
}
~HL2Stereo()
{
if ( mStereoHandle )
{
NvAPI_Stereo_DestroyHandle( mStereoHandle );
mStereoHandle = 0;
}
}
void Init( Device *dev )
{
NvAPI_Stereo_CreateHandleFromIUnknown( dev, &mStereoHandle );
// Set that we've initialized regardless --we'll only try to init once.
mInitialized = true;
}
// Not const because we will update the various values if an update is needed.
bool RequiresUpdate( bool deviceLost )
{
bool active = IsStereoActive();
bool updateRequired;
float eyeSep, sep, conv;
if ( active )
{
if ( NVAPI_OK != NvAPI_Stereo_GetEyeSeparation( mStereoHandle, &eyeSep ) )
return false;
if ( NVAPI_OK != NvAPI_Stereo_GetSeparation( mStereoHandle, &sep ) )
return false;
if ( NVAPI_OK != NvAPI_Stereo_GetConvergence( mStereoHandle, &conv ) )
return false;
// clamp the convergence to prevent wallhack exploit
if ( conv > 31.0f )
{
conv = 31.0f;
NvAPI_Stereo_SetConvergence( mStereoHandle, conv );
DevMsg( "[NVIDIA Stereo 3D] Clamping convergence: %.2f\n", conv);
}
updateRequired = ( eyeSep != mEyeSeparation )
|| ( sep != mSeparation )
|| ( conv != mConvergence )
|| ( active != mActive );
}
else
{
eyeSep = sep = conv = 0;
updateRequired = active != mActive;
}
// If the device was lost and is now restored, need to update the texture contents again.
updateRequired = updateRequired || ( !deviceLost && mDeviceLost );
mDeviceLost = deviceLost;
if ( updateRequired )
{
//Msg( "*** NV_STEREO - UpdateRequired == true\n" );
mEyeSeparation = eyeSep;
mSeparation = sep;
mConvergence = conv;
mActive = active;
return true;
}
return false;
}
bool IsStereoActive() const
{
NvU8 stereoActive = 0;
if ( NVAPI_OK != NvAPI_Stereo_IsActivated( mStereoHandle, &stereoActive ) )
{
return false;
}
return stereoActive != 0;
}
void UpdateStereoTexture( Device *dev, Texture *tex, bool deviceLost )
{
if ( !mInitialized )
{
Init( dev );
}
if ( !RequiresUpdate( deviceLost ) )
{
return;
}
DevMsg( "[NVIDIA Stereo 3D] UpdateStereoTexture: EyeSep: %.2f, Sep: %.2f, Conv: %.2f\n", mEyeSeparation, mSeparation, mConvergence);
StagingResource *staging = D3DType::CreateStagingResource( dev, mEyeSeparation, mSeparation, mConvergence );
if ( staging )
{
D3DType::UpdateTextureFromStaging( dev, tex, staging );
staging->Release();
}
}
private:
float mEyeSeparation;
float mSeparation;
float mConvergence;
StereoHandle mStereoHandle;
bool mInitialized;
bool mActive;
bool mDeviceLost;
};
#ifndef NO_STEREO_D3D9
typedef HL2Stereo< D3D9Type > HL2StereoD3D9;
#endif
#ifndef NO_STEREO_D3D10
typedef HL2Stereo< D3D10Type > HL2StereoD3D10;
#endif
};
};
#endif /* __HL2STEREO__ */