//
// Asura_Xbox360_Graphics.cpp
//
// Created by KJFL - 21 June 2005
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

/******************************************************************************/
/* Included Files                                                             */
/******************************************************************************/

#include "Core/Asura.h"

#include "Rendering/Asura_Xbox360_Graphics.h"

// Asura Platform-independent
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_Timers.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_CPUProfile.h"
#include "Debugging/Asura_DebugText.h"
#include "Debugging/Asura_Logfile.h"
#include "Debugging/Asura_ScreenShot_System.h"
#include "GUIMenu/Asura_GUIMenu_System.h"
#include "Interface/Asura_CommandConsole.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"
#include "Shadows/Asura_CSM.h"

// Asura Xbox 360
#include "Botanicals/Asura_Xbox360_Botanicals.h"
#include "Debugging/Asura_Platform_GPUProfile.h"
#include "Rendering/Asura_Xbox360_Effect_System.h"
#include "Rendering/Asura_Xbox360_Fog.h"
#include "Rendering/Asura_Xbox360_SkinRenderer.h"
#include "Rendering/Asura_Xbox360_TextureManagement.h"
#include "Rendering/Asura_Xbox360_VertexBuffer.h"

#ifdef ASURA_USE_FASTPATH_PFX
#include "SpecialFX/Pfx/Asura_PFX_System.h"
#include "SpecialFX/Asura_Platform_PFX_System_FastPath.h"
#endif

// Shader Include Files
#include "EDRAMCopy.h"
#include "StencilRestore.h"


// Misc
#include <fxl.h>
#include <xgraphics.h>
#include <xtl.h>

/******************************************************************************/
/* Constants                                                                  */
/******************************************************************************/

// Depth-bias values...
static float	g_fDecalDepthBias	= -0.0001f;
static float	g_fSkyboxDepthBias	= 103.0f;		// ART 20/07/07 - Any larger than this and we seem to wrap around the depth-buffer!

// Formats for the main surfaces

#if defined ASURA_USE_HDR
	const D3DFORMAT eCOLOUR_TILE_FORMAT   = D3DFMT_A2B10G10R10F_EDRAM;
#else
	const D3DFORMAT eCOLOUR_TILE_FORMAT   = D3DFMT_A8R8G8B8;
#endif
	const D3DFORMAT eSRT_TILE_FORMAT   = D3DFMT_A16B16G16R16_EDRAM;

const D3DFORMAT eNORMAL_TILE_FORMAT        = D3DFMT_A2R10G10B10;
const D3DFORMAT eDEPTHMTL_TILE_FORMAT      = D3DFMT_G16R16F;
const D3DFORMAT eALBEDO_TILE_FORMAT        = D3DFMT_A8R8G8B8;
const D3DFORMAT eDEPTH_BUFFER_FORMAT       = D3DFMT_D24S8;

const D3DFORMAT ePOST_TONE_MAP_FORMAT      = D3DFMT_A8R8G8B8;
const D3DFORMAT ePOST_TONE_MAP_FORMAT_SRGB = (D3DFORMAT) MAKESRGBFMT( ePOST_TONE_MAP_FORMAT );

#if defined ASURA_USE_HDR
	const D3DFORMAT eSCENE_RESOLVE_FORMAT = D3DFMT_A16B16G16R16F;
#else
	const D3DFORMAT eSCENE_RESOLVE_FORMAT = D3DFMT_A8R8G8B8;
#endif

const D3DFORMAT eFRONT_BUFFER_FORMAT = (D3DFORMAT) MAKESRGBFMT( D3DFMT_LE_A8R8G8B8 );

// Vertex data for full-screen renders
const Direct3D_VertexElement axFULL_SCREEN_VERTEX_DECLARATION[] =
{
	{ 0, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	D3DDECL_END()
};


const short asFULL_SCREEN_VERTICES[] = // Only need 3 verts since using D3DPT_RECTLIST
{
	-1,  1, // Top left
	 1, -1, // Bottom right
	-1, -1, // Bottom left
};

const Direct3D_VertexElement axCOPY_TO_EDRAM_VERTEX_DECLARATION[] =
{
	{ 0, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	{ 0, 4, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
	D3DDECL_END()
};


// Vertex data for the post-tiling stencil buffer restore

const Direct3D_VertexElement axSTENCIL_RESTORE_VERTEX_DECLARATION[] =
{
	{ 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	{ 0, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
	D3DDECL_END()
};

// Command buffer size

#if !defined ASURA_XBOX360_COMMAND_BUFFER_SIZE
	// Hardware default is 2 MB, so let's use that
	#define ASURA_XBOX360_COMMAND_BUFFER_SIZE (2 * 1024 * 1024)
#endif

/******************************************************************************/
/* Global Variables                                                           */
/******************************************************************************/

DVSVAR bool dvs_bMSAA = true;

/******************************************************************************/
/* Asura_Xbox360_Graphics Static Class Data                                   */
/******************************************************************************/

// Width & height set up in Initialise(), based on what user has chosen in the dashboard
int									Asura_Xbox360_Graphics::s_iScreenWidth					= 0;
int									Asura_Xbox360_Graphics::s_iScreenHeight					= 0;

Direct3D_Object*					Asura_Xbox360_Graphics::s_pxD3DObject					= NULL;
Direct3D_Device*					Asura_Xbox360_Graphics::s_pxD3DDevice					= NULL;

D3DXMATRIX							Asura_Xbox360_Graphics::s_xViewMatrix;
D3DXMATRIX							Asura_Xbox360_Graphics::s_xProjectionMatrix;
D3DXMATRIX							Asura_Xbox360_Graphics::s_xViewProjectionMatrix;

#if defined ASURA_XBOX360_ALLOW_STATS
bool								Asura_Xbox360_Graphics::s_bShowStats					= true;
#endif

bool								Asura_Xbox360_Graphics::s_bTiling						= false;
bool								Asura_Xbox360_Graphics::s_bTiling2						= false;
bool								Asura_Xbox360_Graphics::s_bIsCurrentlyRenderingInSkybox	= false;

Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxColourTile					= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxNormalTile					= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxAlbedoTile					= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxDepthMtlTile				= NULL;

Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxDepthTile					= NULL;

Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxSRTColourTile				= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxSRTDepthTile				= NULL;

Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxFinalRT						= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxFinalDepth					= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxHiZRestore					= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxHiZRestoreSRT				= NULL;

Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxPostToneMapRT				= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxPostToneMapLinearRT			= NULL;

Direct3D_Texture*					Asura_Xbox360_Graphics::s_pxSceneResolveTexture			= NULL;
Direct3D_Texture*					Asura_Xbox360_Graphics::s_pxFrontBuffer					= NULL;

Direct3D_Texture*					Asura_Xbox360_Graphics::s_pxNormalTexture				= NULL;
Direct3D_Texture*					Asura_Xbox360_Graphics::s_pxAlbedoTexture				= NULL;
Direct3D_Texture*					Asura_Xbox360_Graphics::s_pxDepthMtlTexture				= NULL;
Direct3D_Texture*					Asura_Xbox360_Graphics::s_pxDepthTexture				= NULL;

Direct3D_Texture					Asura_Xbox360_Graphics::s_xPostToneMapResolveTexture;
Direct3D_Texture					Asura_Xbox360_Graphics::s_xPostToneMapLinearResolveTexture;

Asura_Xbox360_Effect*				Asura_Xbox360_Graphics::s_pxEDRAMCopyEffect				= NULL;

Direct3D_VertexDeclaration*			Asura_Xbox360_Graphics::s_pxFullScreenVD				= NULL;
Direct3D_VertexDeclaration*			Asura_Xbox360_Graphics::s_pxCopyToEDRAMVD				= NULL;
Asura_Xbox360_VertexBuffer			Asura_Xbox360_Graphics::s_xFullScreenVB;

Asura_Xbox360_Effect*				Asura_Xbox360_Graphics::s_pxStencilRestoreEffect		= NULL;
Asura_Xbox360_Effect_System::Handle	Asura_Xbox360_Graphics::s_xStencilRestoreTextureHandle	= 0;
Direct3D_Texture					Asura_Xbox360_Graphics::s_xStencilRestoreTexture;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxStencilRestoreRT			= NULL;
Direct3D_Surface*					Asura_Xbox360_Graphics::s_pxStencilRestoreSRT_RT			= NULL;
Direct3D_VertexDeclaration*			Asura_Xbox360_Graphics::s_pxStencilRestoreVD			= NULL;
Asura_Xbox360_VertexBuffer			Asura_Xbox360_Graphics::s_xStencilRestoreVB;
u_int								Asura_Xbox360_Graphics::s_uNumStencilRestoreStrips		= 0;
static Asura_Xbox360_Effect_System::Handle s_xVposOffsetHandle = NULL;


float								Asura_Xbox360_Graphics::s_fViewportMaxZ					= 1.0f;
float								Asura_Xbox360_Graphics::s_fViewportMinZ					= 0.0f;

Asura_FixedArray< D3DRECT, Asura_Xbox360_Graphics::uNUM_TILES >
	Asura_Xbox360_Graphics::s_axTilingRects;

Asura_FixedArray< D3DRECT, 2 > Asura_Xbox360_Graphics::s_axTilingRectsSRT;

Asura_FixedArray< D3DSURFACE_PARAMETERS, Asura_Xbox360_Graphics::SP_NUM_SURFACE_PARAMS >
	Asura_Xbox360_Graphics::s_axSurfaceParams;

void*								Asura_Xbox360_Graphics::s_pExtraMem						= NULL;

bool								Asura_Xbox360_Graphics::s_bDeviceSuspended				= false;

float								Asura_Xbox360_Graphics::s_fGamma						= 0.5f;

/******************************************************************************/
/* Asura_Xbox360_Graphics Static Class Functions                              */
/******************************************************************************/

void Asura_Xbox360_Graphics::InitialiseSurfaceParams()
{
	// We'll always use tiles side by side

	// First predicated tiling bracket (g-buffer generation)
	{
		const u_int uTILE_WIDTH = s_iScreenWidth / uNUM_TILES;
		const u_int uTILE_HEIGHT = s_iScreenHeight;

		for( u_int u = 0; u < uNUM_TILES; ++u )
		{
			s_axTilingRects[u].x1 = uTILE_WIDTH * u;
			s_axTilingRects[u].y1 = 0;
			s_axTilingRects[u].x2 = uTILE_WIDTH * (u + 1);
			s_axTilingRects[u].y2 = uTILE_HEIGHT;
		}

		const u_int uEDRAM_TILE_WIDTH  = XGNextMultiple( uTILE_WIDTH,  GPU_TEXTURE_TILE_DIMENSION );
		const u_int uEDRAM_TILE_HEIGHT = XGNextMultiple( uTILE_HEIGHT, GPU_TEXTURE_TILE_DIMENSION );

		// Colour tile used during predicated tiling
		const u_int uCOLOUR_TILE_SIZE = XGSurfaceSize( uEDRAM_TILE_WIDTH, uEDRAM_TILE_HEIGHT,
			eCOLOUR_TILE_FORMAT, ASURA_XBOX360_MULTISAMPLE_TYPE );

		s_axSurfaceParams[ SP_COLOUR_TILE ].Base = 0;
		s_axSurfaceParams[ SP_COLOUR_TILE ].HierarchicalZBase = 0;
		s_axSurfaceParams[ SP_COLOUR_TILE ].ColorExpBias = 0;

		// World-space normal tile used during predicated tiling
		const u_int uNORMAL_TILE_SIZE = XGSurfaceSize( uEDRAM_TILE_WIDTH, uEDRAM_TILE_HEIGHT,
			eNORMAL_TILE_FORMAT, ASURA_XBOX360_MULTISAMPLE_TYPE );

		s_axSurfaceParams[ SP_NORMAL_TILE ].Base = uCOLOUR_TILE_SIZE;
		s_axSurfaceParams[ SP_NORMAL_TILE ].HierarchicalZBase = 0;
		s_axSurfaceParams[ SP_NORMAL_TILE ].ColorExpBias = 0;

		// Albedo & specular tile used during predicated tiling
		const u_int uALBEDO_TILE_SIZE = XGSurfaceSize( uEDRAM_TILE_WIDTH, uEDRAM_TILE_HEIGHT,
			eALBEDO_TILE_FORMAT, ASURA_XBOX360_MULTISAMPLE_TYPE );

		s_axSurfaceParams[ SP_ALBEDO_TILE ].Base = uCOLOUR_TILE_SIZE + uNORMAL_TILE_SIZE;
		s_axSurfaceParams[ SP_ALBEDO_TILE ].HierarchicalZBase = 0;
		s_axSurfaceParams[ SP_ALBEDO_TILE ].ColorExpBias = 0;

		// World-space depth & material tile used during predicated tiling
		const u_int uDEPTHMTL_TILE_SIZE = XGSurfaceSize( uEDRAM_TILE_WIDTH, uEDRAM_TILE_HEIGHT,
			eDEPTHMTL_TILE_FORMAT, ASURA_XBOX360_MULTISAMPLE_TYPE );

		s_axSurfaceParams[ SP_DEPTHMTL_TILE ].Base = uCOLOUR_TILE_SIZE + uNORMAL_TILE_SIZE
			+ uALBEDO_TILE_SIZE;
		s_axSurfaceParams[ SP_DEPTHMTL_TILE ].HierarchicalZBase = 0;
		s_axSurfaceParams[ SP_DEPTHMTL_TILE ].ColorExpBias = 0;

		// Depth-stencil tile used during predicated tiling
		s_axSurfaceParams[ SP_DEPTH_TILE ].Base = uCOLOUR_TILE_SIZE + uNORMAL_TILE_SIZE
			+ uALBEDO_TILE_SIZE + uDEPTHMTL_TILE_SIZE;
		s_axSurfaceParams[ SP_DEPTH_TILE ].HierarchicalZBase = 0;
		s_axSurfaceParams[ SP_DEPTH_TILE ].ColorExpBias = 0;
	}

	u_int uEndSRTTiles;

	// Second predicated tiling bracket (shadows making use of destination alpha)
	{
		const u_int uTILE_WIDTH = s_iScreenWidth / 2;
		const u_int uTILE_HEIGHT = s_iScreenHeight;

		for( u_int u = 0; u < 2; ++u )
		{
			s_axTilingRectsSRT[u].x1 = uTILE_WIDTH * u;
			s_axTilingRectsSRT[u].y1 = 0;
			s_axTilingRectsSRT[u].x2 = uTILE_WIDTH * (u + 1);
			s_axTilingRectsSRT[u].y2 = uTILE_HEIGHT;
		}

		const u_int uEDRAM_TILE_WIDTH  = XGNextMultiple( uTILE_WIDTH,  GPU_TEXTURE_TILE_DIMENSION );
		const u_int uEDRAM_TILE_HEIGHT = XGNextMultiple( uTILE_HEIGHT, GPU_TEXTURE_TILE_DIMENSION );

		// Colour tile used during 2nd predicated tiling
 		const u_int uSRT_TILE_SIZE = XGSurfaceSize( uEDRAM_TILE_WIDTH, uEDRAM_TILE_HEIGHT,
			eSRT_TILE_FORMAT, D3DMULTISAMPLE_NONE );

 		s_axSurfaceParams[ SP_SRT_COLOUR_TILE ].Base = 0;
 		s_axSurfaceParams[ SP_SRT_COLOUR_TILE ].HierarchicalZBase = 0;
 		s_axSurfaceParams[ SP_SRT_COLOUR_TILE ].ColorExpBias = 0;
	 
 		// Depth tile used during 2nd predicated tiling
		// Full-screen final render target
		const u_int uSRT_DEPTH_TILE_SIZE = XGSurfaceSize( uEDRAM_TILE_WIDTH, uEDRAM_TILE_HEIGHT,
			eDEPTH_BUFFER_FORMAT, D3DMULTISAMPLE_NONE );
	 
 		s_axSurfaceParams[ SP_SRT_DEPTH_TILE ].Base = uSRT_TILE_SIZE;
 		s_axSurfaceParams[ SP_SRT_DEPTH_TILE ].HierarchicalZBase = 0;
 		s_axSurfaceParams[ SP_SRT_DEPTH_TILE ].ColorExpBias = 0;

		uEndSRTTiles = uSRT_TILE_SIZE + uSRT_DEPTH_TILE_SIZE;
	}

	// Full-screen final render target
	const u_int uFINAL_RT_SIZE = XGSurfaceSize( s_iScreenWidth, s_iScreenHeight,
		eCOLOUR_TILE_FORMAT, D3DMULTISAMPLE_NONE );

	s_axSurfaceParams[ SP_FINAL_RT ].Base = 0;  // This can go back at the start of EDRAM
	s_axSurfaceParams[ SP_FINAL_RT ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_FINAL_RT ].ColorExpBias = 0;

	// Full-screen final depth-stencil buffer
	const u_int uFINAL_DEPTH_BUFFER_SIZE = XGSurfaceSize( s_iScreenWidth, s_iScreenHeight,
		eDEPTH_BUFFER_FORMAT, D3DMULTISAMPLE_NONE );

	const u_int uFINAL_DEPTH_BUFFER_SIZE_HIZ = XGHierarchicalZSize( s_iScreenWidth, s_iScreenHeight,
		D3DMULTISAMPLE_NONE );

	s_axSurfaceParams[ SP_FINAL_DEPTH ].Base = uFINAL_RT_SIZE;
	s_axSurfaceParams[ SP_FINAL_DEPTH ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_FINAL_DEPTH ].ColorExpBias = 0;

	// Shadow creation render target
	const u_int uSHADOW_RENDER_TARGET_SIZE = XGSurfaceSize( ASURA_MAX_SHADOW_TEXTURE_SIZE,
		ASURA_MAX_SHADOW_TEXTURE_SIZE, D3DFMT_R32F, D3DMULTISAMPLE_NONE );

	s_axSurfaceParams[ SP_SHADOW_RT ].Base = uEndSRTTiles;
	s_axSurfaceParams[ SP_SHADOW_RT ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_SHADOW_RT ].ColorExpBias = 0;

	// Shadow creation depth buffer
	s_axSurfaceParams[ SP_SHADOW_DEPTH ].Base = uEndSRTTiles + uSHADOW_RENDER_TARGET_SIZE;
	s_axSurfaceParams[ SP_SHADOW_DEPTH ].HierarchicalZBase = uFINAL_DEPTH_BUFFER_SIZE_HIZ;
	s_axSurfaceParams[ SP_SHADOW_DEPTH ].ColorExpBias = 0;

	// Bloom FSFX buffer
	s_axSurfaceParams[ SP_BLOOM ].Base = uFINAL_RT_SIZE + uFINAL_DEPTH_BUFFER_SIZE;
	s_axSurfaceParams[ SP_BLOOM ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_BLOOM ].ColorExpBias = 0;

	// Advanced bloom FSFX buffer
	// We just use the same EDRAM area as the z-buffer, as the z-buffer is no longer needed when we get
	// to the FSFX rendering (We still have the deferred depth-normal buffer which can be used by
	// further FSFX passes)
	//  [2/14/2008 AS] Moved it to be the same as the shadow buffers as depth (well actually stencil) 
	// is needed for AVP Alien vision modes
	s_axSurfaceParams[ SP_ADV_BLOOM ].Base = uFINAL_RT_SIZE + uFINAL_DEPTH_BUFFER_SIZE;
	s_axSurfaceParams[ SP_ADV_BLOOM ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_ADV_BLOOM ].ColorExpBias = 0;

	// HDR working RTs can go back at the beginning again
	s_axSurfaceParams[ SP_HDR ].Base = 0;
	s_axSurfaceParams[ SP_HDR ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_HDR ].ColorExpBias = 0;

	// Same for the cascaded shadow maps
	s_axSurfaceParams[ SP_CSM ].Base = 0;
	s_axSurfaceParams[ SP_CSM ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_CSM ].ColorExpBias = 5 ; // Convert [-1,1] output into [-32,32]

	// Depth target is used first, then resolved out
	s_axSurfaceParams[ SP_CSM_DEPTH ].Base = 0;
	s_axSurfaceParams[ SP_CSM_DEPTH ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_CSM_DEPTH ].ColorExpBias = 0;

	// Same as for the SP_ADV_BLOOM
	s_axSurfaceParams[ SP_SSAO_DEPTH ].Base = uFINAL_RT_SIZE + uFINAL_DEPTH_BUFFER_SIZE;
	s_axSurfaceParams[ SP_SSAO_DEPTH ].HierarchicalZBase = 0;
	s_axSurfaceParams[ SP_SSAO_DEPTH ].ColorExpBias = 0;
}

static CRITICAL_SECTION g_xDeviceCritSec;

bool Asura_Xbox360_Graphics::AcquireThreadOwnership()
{
	EnterCriticalSection(&g_xDeviceCritSec);
	Direct3D_Device* pxDevice = GetDevice();
	if(pxDevice)
	{
		if (pxDevice->QueryThreadOwnership() == GetCurrentThreadId())
		{
			LeaveCriticalSection(&g_xDeviceCritSec);
			return false;
		}
		pxDevice->AcquireThreadOwnership();
	}
	else
	{
		return false;
	}
	return true;
}

void Asura_Xbox360_Graphics::ReleaseThreadOwnership()
{
	GetDevice()->ReleaseThreadOwnership();
	LeaveCriticalSection(&g_xDeviceCritSec);
}


bool Asura_Xbox360_Graphics::HasThreadOwnership()
{
	return (GetDevice()->QueryThreadOwnership() == GetCurrentThreadId());
}


void Asura_Xbox360_Graphics::ExclusiveThreadOwnership()
{
	EnterCriticalSection(&g_xDeviceCritSec);
}


void Asura_Xbox360_Graphics::ReleaseExclusiveThreadOwnership()
{
	LeaveCriticalSection(&g_xDeviceCritSec);
}


void Asura_Xbox360_Graphics::Initialise()
{

#if defined( ASURA_SUPPORT_COMMAND_CONSOLE )
#if defined ASURA_XBOX360_ALLOW_STATS
	Asura_CommandConsole::AddVar( L"Debug.RenderStats", &s_bShowStats, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif

	Asura_CommandConsole::AddCmd( L"Render.UseNullHardware", UseNullHardware,
								  Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif // #if defined( ASURA_SUPPORT_COMMAND_CONSOLE )

#if defined ASURA_DEBUG_VARIABLE_SYSTEM
	Asura_CommandConsole::AddVar( L"Render.MSAA", &dvs_bMSAA,
		Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif

	{
		XVIDEO_MODE xVideoMode;
		XGetVideoMode( &xVideoMode );

#if !defined ASURA_NO_DEBUG_OUTPUT
		const char* const szVideoStandard
			= xVideoMode.VideoStandard == XC_VIDEO_STANDARD_NTSC_M ? "NTSC-M"
			: xVideoMode.VideoStandard == XC_VIDEO_STANDARD_NTSC_J ? "NTSC-J"
			: xVideoMode.VideoStandard == XC_VIDEO_STANDARD_PAL_I  ? "PAL"
			: "Unknown video standard";

		const char* const szInterlaced = (xVideoMode.fIsInterlaced ? "yes" : "no");
		const char* const szWidescreen = (xVideoMode.fIsWideScreen ? "yes" : "no");
		const char* const szHighDef    = (xVideoMode.fIsHiDef      ? "yes" : "no");
#endif

		Asura_OutputToDebugger_Retail(( "\nCurrent video mode:\n%ux%u @ %.2f Hz\n%s\nInterlaced:\t%s\nWidescreen:\t%s\nHigh-def:\t%s\n\n",
			xVideoMode.dwDisplayWidth, xVideoMode.dwDisplayHeight, xVideoMode.RefreshRate,
			szVideoStandard, szInterlaced, szWidescreen, szHighDef ));

		// Use 1280x720 for widescreen or high-def (the latter covers cases like VGA 1024x768),
		// and 960x720 for everything else
		if( xVideoMode.fIsWideScreen || xVideoMode.fIsHiDef )
		{
			s_iScreenWidth = 1280;
			Asura_Camera::SetPhysicalScreenAspectRatio( 16.0f / 9.0f );
		}
		else
		{
			// NY - 23 May 2008 - special case for PAL50
			// could possibly be generalised out, but 576 is the only res we know we need to do this for, so keeping it simple until we know otherwise
			if (xVideoMode.dwDisplayHeight == 576)
			{
				Asura_Camera::SetPhysicalScreenAspectRatio( 4.0f / 3.0f * 576.0f / 480.0f );
			}
			else
			{
				Asura_Camera::SetPhysicalScreenAspectRatio( 4.0f / 3.0f );
			}
			s_iScreenWidth = 960;

			// In non-widescreen modes, the frame buffer occupies less memory. Consume the extra
			// free RAM now, so our usage is consistent in all modes

			const u_int uExtraPixels = (1280 - 960) * 720;

			const u_int uExtraBytes =
				  uExtraPixels * XGBytesPerPixelFromFormat( eSCENE_RESOLVE_FORMAT )
				+ uExtraPixels * XGBytesPerPixelFromFormat( eFRONT_BUFFER_FORMAT )
				+ uExtraPixels * XGBytesPerPixelFromFormat( eNORMAL_TILE_FORMAT )
				+ uExtraPixels * XGBytesPerPixelFromFormat( eALBEDO_TILE_FORMAT )
				+ uExtraPixels * XGBytesPerPixelFromFormat( eDEPTHMTL_TILE_FORMAT )
				+ uExtraPixels * XGBytesPerPixelFromFormat( eDEPTH_BUFFER_FORMAT )

				// Radial blur
				+ uExtraPixels * XGBytesPerPixelFromFormat( D3DFMT_A8R8G8B8 )

				// Advanced bloom (two lots of quarter the size)
				+ (uExtraPixels / 4) * XGBytesPerPixelFromFormat( D3DFMT_A8R8G8B8 ) * 2;

			s_pExtraMem = Asura_Xbox360_MemoryManagement::AllocatePhysical(Asura_Xbox360_MemoryManagement::MT_READ_ONLY, uExtraBytes, XALLOC_PHYSICAL_ALIGNMENT_DEFAULT);
			Asura_Assert( s_pExtraMem, "Failed to allocate extra graphics memory." );
		}

		s_iScreenHeight = 720;
	}

	InitialiseSurfaceParams();

	s_pxD3DObject = Direct3DCreate9( D3D_SDK_VERSION );

	InitializeCriticalSection(&g_xDeviceCritSec);
	{
		D3DPRESENT_PARAMETERS xParams;
		Asura_Mem::Set( &xParams, 0, sizeof( xParams ) );
		GetDefaultParams(&xParams);
		
		const DWORD uFlags = D3DCREATE_BUFFER_2_FRAMES;	

		s_pxD3DObject->CreateDevice( 0, D3DDEVTYPE_HAL, NULL, uFlags, &xParams, &s_pxD3DDevice );
	}

	// Create front buffer, scene resolve texture and predicated tiling textures. If we weren't
	// doing any post-processing, we'd need to double-buffer the front buffer; but the additional
	// rendering makes that ping-ponging unnecessary
	{
		s_pxD3DDevice->CreateTexture( s_iScreenWidth, s_iScreenHeight, 1, 0, eSCENE_RESOLVE_FORMAT, D3DPOOL_DEFAULT, &s_pxSceneResolveTexture, NULL );
		s_pxD3DDevice->CreateTexture( s_iScreenWidth, s_iScreenHeight, 1, 0, eFRONT_BUFFER_FORMAT,  D3DPOOL_DEFAULT, &s_pxFrontBuffer,         NULL );

		s_pxD3DDevice->CreateTexture( s_iScreenWidth, s_iScreenHeight, 1, 0, eNORMAL_TILE_FORMAT,   D3DPOOL_DEFAULT, &s_pxNormalTexture,       NULL );
		s_pxD3DDevice->CreateTexture( s_iScreenWidth, s_iScreenHeight, 1, 0, eALBEDO_TILE_FORMAT,   D3DPOOL_DEFAULT, &s_pxAlbedoTexture,       NULL );
		s_pxD3DDevice->CreateTexture( s_iScreenWidth, s_iScreenHeight, 1, 0, eDEPTHMTL_TILE_FORMAT, D3DPOOL_DEFAULT, &s_pxDepthMtlTexture,     NULL );
		s_pxD3DDevice->CreateTexture( s_iScreenWidth, s_iScreenHeight, 1, 0, eDEPTH_BUFFER_FORMAT,  D3DPOOL_DEFAULT, &s_pxDepthTexture,        NULL );

		Asura_Assert( s_pxSceneResolveTexture && s_pxFrontBuffer && s_pxNormalTexture && s_pxAlbedoTexture
				   && s_pxDepthMtlTexture && s_pxDepthTexture, "Unable to create device textures." );

		// Alias a texture for resolves after tone mapping in the same place as the one before, but
		// with a different format. Make sure we can fit in the same memory!

		#if defined ASURA_ASSERT
			const u_int uPreToneMapBPP = XGBitsPerPixelFromFormat( eSCENE_RESOLVE_FORMAT );
			const u_int uPostToneMapBPP = XGBitsPerPixelFromFormat( ePOST_TONE_MAP_FORMAT );
		#endif

		// Would like to make this a static assert, but the above are function calls

		Asura_Assertf( uPreToneMapBPP >= uPostToneMapBPP,
			("Pre tone-map texture format has fewer bits per pixel than post tone-map (%u vs. %u). Memory will be trashed.",
			uPreToneMapBPP, uPostToneMapBPP) );

		XGSetTextureHeader( s_iScreenWidth, s_iScreenHeight, 1, 0, ePOST_TONE_MAP_FORMAT_SRGB,
			D3DPOOL_DEFAULT, 0, 0, 0, &s_xPostToneMapResolveTexture, NULL, NULL );

		s_xPostToneMapResolveTexture.Format.BaseAddress = s_pxSceneResolveTexture->Format.BaseAddress;

		XGSetTextureHeader( s_iScreenWidth, s_iScreenHeight, 1, 0, ePOST_TONE_MAP_FORMAT,
			D3DPOOL_DEFAULT, 0, 0, 0, &s_xPostToneMapLinearResolveTexture, NULL, NULL );

		s_xPostToneMapLinearResolveTexture.Format.BaseAddress = s_pxSceneResolveTexture->Format.BaseAddress;


		// DMR 29/11/07 - Clear the front buffer (needed if the first thing rendered is an assert - Mantis 0046484)
		{
			Direct3D_Texture *pxBuffer;

			pxBuffer = s_pxFrontBuffer;

			D3DLOCKED_RECT xLockedRect;		
			RECT xRect;
			xRect.left   = xRect.top = 0;
			xRect.bottom = s_iScreenHeight;
			xRect.right  = s_iScreenWidth;

			if (D3D_OK == pxBuffer->LockRect(0, &xLockedRect, &xRect, 0))
			{				
				// This is surprisingly arkward, as we can't just clear 1280*720 pixels from xLockedRect.pBits - as it's a tiled surface, and
				// it doesn't actually seem to start at that address... we've got to use the tiling functions. Might have been easier to do 
				// a GPU clear...
				uint8 *pBlank = new uint8[s_iScreenWidth*s_iScreenHeight*4];

				if (pBlank)
				{				
					memset(pBlank, 0, s_iScreenWidth*s_iScreenHeight*4);					
					XGTileSurface(xLockedRect.pBits, s_iScreenWidth, s_iScreenHeight, NULL, pBlank, s_iScreenWidth*4, NULL, 4);
					delete [] pBlank;
				}

				pxBuffer->UnlockRect(0);				
			}			
		}

	}

	// Create render target and depth-stencil surfaces
	{
		UINT uTileWidth = XGNextMultiple( s_iScreenWidth / uNUM_TILES, GPU_TEXTURE_TILE_DIMENSION );
		UINT uTileHeight = XGNextMultiple( s_iScreenHeight, GPU_TEXTURE_TILE_DIMENSION );

		// These are used during predicated tiling
		s_pxD3DDevice->CreateRenderTarget( uTileWidth, uTileHeight, eCOLOUR_TILE_FORMAT,   ASURA_XBOX360_MULTISAMPLE_TYPE, 0, FALSE, &s_pxColourTile,   &s_axSurfaceParams[ SP_COLOUR_TILE ] );
		s_pxD3DDevice->CreateRenderTarget( uTileWidth, uTileHeight, eNORMAL_TILE_FORMAT,   ASURA_XBOX360_MULTISAMPLE_TYPE, 0, FALSE, &s_pxNormalTile,   &s_axSurfaceParams[ SP_NORMAL_TILE ] );
		s_pxD3DDevice->CreateRenderTarget( uTileWidth, uTileHeight, eALBEDO_TILE_FORMAT,   ASURA_XBOX360_MULTISAMPLE_TYPE, 0, FALSE, &s_pxAlbedoTile,   &s_axSurfaceParams[ SP_ALBEDO_TILE ] );
		s_pxD3DDevice->CreateRenderTarget( uTileWidth, uTileHeight, eDEPTHMTL_TILE_FORMAT, ASURA_XBOX360_MULTISAMPLE_TYPE, 0, FALSE, &s_pxDepthMtlTile, &s_axSurfaceParams[ SP_DEPTHMTL_TILE ] );

		s_pxD3DDevice->CreateDepthStencilSurface( uTileWidth, uTileHeight, eDEPTH_BUFFER_FORMAT, ASURA_XBOX360_MULTISAMPLE_TYPE, 0, FALSE, &s_pxDepthTile, &s_axSurfaceParams[ SP_DEPTH_TILE ] );

		// Second predicated tiling bracket, always 2 tiles
		uTileWidth = XGNextMultiple( s_iScreenWidth / 2, GPU_TEXTURE_TILE_DIMENSION );
		uTileHeight = XGNextMultiple( s_iScreenHeight, GPU_TEXTURE_TILE_DIMENSION );

		s_pxD3DDevice->CreateRenderTarget( uTileWidth, uTileHeight, eSRT_TILE_FORMAT, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxSRTColourTile,   &s_axSurfaceParams[ SP_SRT_COLOUR_TILE ] );
		s_pxD3DDevice->CreateDepthStencilSurface( uTileWidth, uTileHeight, eDEPTH_BUFFER_FORMAT, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxSRTDepthTile, &s_axSurfaceParams[ SP_SRT_DEPTH_TILE ] );
		s_pxD3DDevice->CreateDepthStencilSurface( uTileWidth/2, uTileHeight/2, eDEPTH_BUFFER_FORMAT, D3DMULTISAMPLE_4_SAMPLES, 0, FALSE, &s_pxHiZRestoreSRT, &s_axSurfaceParams[ SP_SRT_DEPTH_TILE ] );

		// These afterwards
		s_pxD3DDevice->CreateRenderTarget( s_iScreenWidth, s_iScreenHeight, eCOLOUR_TILE_FORMAT, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxFinalRT, &s_axSurfaceParams[ SP_FINAL_RT ] );
		s_pxD3DDevice->CreateDepthStencilSurface( s_iScreenWidth, s_iScreenHeight, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxFinalDepth, &s_axSurfaceParams[ SP_FINAL_DEPTH ] );
		s_pxD3DDevice->CreateDepthStencilSurface( s_iScreenWidth/2, s_iScreenHeight/2, D3DFMT_D24S8, D3DMULTISAMPLE_4_SAMPLES, 0, FALSE, &s_pxHiZRestore, &s_axSurfaceParams[ SP_FINAL_DEPTH ] );

		// And this after tone mapping. Can go in the same place as the above. This is the point we start sRGB writes
		s_pxD3DDevice->CreateRenderTarget( s_iScreenWidth, s_iScreenHeight, ePOST_TONE_MAP_FORMAT_SRGB, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxPostToneMapRT, &s_axSurfaceParams[ SP_FINAL_RT ] );

		// Also create a linear version of the same RT. Use this for overlay quads, which need to avoid sRGB writes to mimic the PC
		s_pxD3DDevice->CreateRenderTarget( s_iScreenWidth, s_iScreenHeight, ePOST_TONE_MAP_FORMAT, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxPostToneMapLinearRT, &s_axSurfaceParams[ SP_FINAL_RT ] );

		Asura_Assert( s_pxColourTile && s_pxNormalTile && s_pxAlbedoTile && s_pxDepthMtlTile
			&& s_pxDepthTile && s_pxFinalRT && s_pxFinalDepth && s_pxPostToneMapRT
			&& s_pxPostToneMapLinearRT, "Unable to create render surface." );

		s_pxD3DDevice->CreateRenderTarget( uTileWidth, uTileHeight, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxStencilRestoreSRT_RT, &s_axSurfaceParams[ SP_SRT_DEPTH_TILE ] );
		Asura_Assert( s_pxStencilRestoreSRT_RT, "Failed to create stencil-restore render target." );
	}

	// Misc state
	{
		Asura_Viewport::Set( s_iScreenWidth, s_iScreenHeight );

#ifdef ASURA_GUI_MENU_ALWAYS_WIDESCREEN
		Asura_GUIMenu_System::SetVirtualResolution( 1280.0f, (1280.0f / s_iScreenWidth) * s_iScreenHeight );
#endif

		s_pxD3DDevice->SetRenderState_Inline( D3DRS_PRIMITIVERESETENABLE, TRUE );
	}

	// Default texture filters to linear in case shader doesn't specify
	for( u_int u = 0; u < D3DSAMP_MAXSAMPLERS; ++u )
	{
		s_pxD3DDevice->SetSamplerState_Inline( u, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
		s_pxD3DDevice->SetSamplerState_Inline( u, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		s_pxD3DDevice->SetSamplerState_Inline( u, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	}

	// Full-screen render vertex data
	{
		s_pxD3DDevice->CreateVertexDeclaration( axFULL_SCREEN_VERTEX_DECLARATION, &s_pxFullScreenVD );
		Asura_Assert( s_pxFullScreenVD, "Failed to create full-screen vertex declaration." );

		s_xFullScreenVB.CreateStatic( sizeof( short ) * 2, 3 );

		short* const psVertices = static_cast< short* >( s_xFullScreenVB.Lock() );
		if( psVertices )
		{
			// Can't use Asura_Mem::Copy(), because that's XMemCpy()
			memcpy( psVertices, asFULL_SCREEN_VERTICES, sizeof( asFULL_SCREEN_VERTICES ) );
			s_xFullScreenVB.Unlock();
		}
	}
	s_pxD3DDevice->CreateVertexDeclaration( axCOPY_TO_EDRAM_VERTEX_DECLARATION, &s_pxCopyToEDRAMVD );
	

	// EDRAM copy data
	{
		s_pxEDRAMCopyEffect = Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auEDRAMCopy);

		const Asura_Xbox360_Effect_System::Handle xSceneTextureHandle = s_pxEDRAMCopyEffect->GetParameterHandle( "g_xSceneTexture" );
		s_pxEDRAMCopyEffect->SetTexture( xSceneTextureHandle, s_pxSceneResolveTexture );

		const Asura_Xbox360_Effect_System::Handle xDepthTextureHandle = s_pxEDRAMCopyEffect->GetParameterHandle( "g_xDepthTexture" );
		s_pxEDRAMCopyEffect->SetTexture( xDepthTextureHandle, s_pxDepthTexture );
	}

	// Data needed to restore the stencil buffer after predicated tiling
	{
		// Create stencil-restore RT at the same location as the final depth-stencil buffer, but with ARGB format. We'll write to r only, which is where the stencil bits go
		s_pxD3DDevice->CreateRenderTarget( s_iScreenWidth, s_iScreenHeight, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &s_pxStencilRestoreRT, &s_axSurfaceParams[ SP_FINAL_DEPTH ] );
		Asura_Assert( s_pxStencilRestoreRT, "Failed to create stencil-restore render target." );



		// Alias a texture in ARGB format at the same location as the post-tiling resolved depth-stencil texture
		XGSetTextureHeader( s_iScreenWidth, s_iScreenHeight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, 0, 0, 0, &s_xStencilRestoreTexture, NULL, NULL );
		s_xStencilRestoreTexture.Format.BaseAddress = s_pxDepthTexture->Format.BaseAddress;

		// Set the effect's sampler to read from that. Stencil values will appear in the blue channel
		s_pxStencilRestoreEffect = Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auStencilRestore);
		const Asura_Xbox360_Effect_System::Handle xStencilRestoreTextureHandle = s_pxStencilRestoreEffect->GetParameterHandle( "g_xStencilTexture" );
		s_pxStencilRestoreEffect->GetInterface()->SetSampler( xStencilRestoreTextureHandle, &s_xStencilRestoreTexture );

		s_xVposOffsetHandle = s_pxStencilRestoreEffect->GetParameterHandle( "g_xVPOSOffsetVector" );




		// Create vertex declaration and buffer

		s_pxD3DDevice->CreateVertexDeclaration( axSTENCIL_RESTORE_VERTEX_DECLARATION, &s_pxStencilRestoreVD );
		Asura_Assert( s_pxStencilRestoreVD, "Failed to create stencil restore vertex declaration." );

		// Two strips per tile
		s_uNumStencilRestoreStrips = 2 * s_iScreenWidth / GPU_EDRAM_TILE_WIDTH_1X;
		s_xStencilRestoreVB.CreateStatic( sizeof( float ) * 4, s_uNumStencilRestoreStrips * 3 );

		float* pfVertices = static_cast< float* >( s_xStencilRestoreVB.Lock() );
		if( pfVertices )
		{
			const u_short usTileWidth = GPU_EDRAM_TILE_WIDTH_1X;
			const u_short usTileHalfWidth = usTileWidth / 2;
			const UINT uTileHeight = XGNextMultiple( s_iScreenHeight, GPU_TEXTURE_TILE_DIMENSION );

			u_short usTileStartX = 0;

			for( u_int u = 0; u < s_uNumStencilRestoreStrips / 2; ++u )
			{
				// First strip writes to left half of tile, reading from right side of texture
				{
					// XY
					*pfVertices++ = (float(usTileStartX) / float(s_iScreenWidth)) * 2.0f - 1.0f;
					*pfVertices++ = -((float(s_iScreenHeight) /  float(uTileHeight)) * 2.0f - 1.0f);

					// UV
					*pfVertices++ = float(usTileStartX + usTileHalfWidth);
					*pfVertices++ = float(s_iScreenHeight);

					// XY
					*pfVertices++ = (float(usTileStartX + usTileHalfWidth) / float(s_iScreenWidth)) * 2.0f - 1.0f;
					*pfVertices++ = -((float(s_iScreenHeight) /  float(uTileHeight)) * 2.0f - 1.0f);

					// UV
					*pfVertices++ = float(usTileStartX + usTileWidth);
					*pfVertices++ = float(s_iScreenHeight);

					// XY
					*pfVertices++ = (float(usTileStartX) / float(s_iScreenWidth)) * 2.0f - 1.0f;
					*pfVertices++ = 1.0f;

					// UV
					*pfVertices++ = float(usTileStartX + usTileHalfWidth);
					*pfVertices++ = 0.0f;;
				}

				// Second strip writes to right half, reading from left
				{
					// XY
					*pfVertices++ = (float(usTileStartX + usTileHalfWidth) / float(s_iScreenWidth)) * 2.0f - 1.0f;
					*pfVertices++ = -((float(s_iScreenHeight) /  float(uTileHeight)) * 2.0f - 1.0f);

					// UV
					*pfVertices++ = float(usTileStartX);
					*pfVertices++ = float(s_iScreenHeight);

					// XY
					*pfVertices++ = (float(usTileStartX + usTileWidth) / float(s_iScreenWidth)) * 2.0f - 1.0f;
					*pfVertices++ = -((float(s_iScreenHeight) /  float(uTileHeight)) * 2.0f - 1.0f);

					// UV
					*pfVertices++ = float(usTileStartX + usTileHalfWidth);
					*pfVertices++ = float(s_iScreenHeight);

					// XY
					*pfVertices++ = (float(usTileStartX + usTileHalfWidth) / float(s_iScreenWidth)) * 2.0f - 1.0f;
					*pfVertices++ = 1.0f;

					// UV
					*pfVertices++ = float(usTileStartX);
					*pfVertices++ = 0.0f;
				}

				usTileStartX += usTileWidth;
			}

			s_xStencilRestoreVB.Unlock();
		}
	}
	
	#if defined ASURA_USE_PROFILING_SYSTEM
		ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__TOTALFRAME, "total frame");
	#endif // defined ASURA_USE_PROFILING_SYSTEM

	Asura_Xbox360_Graphics::GetDevice()->ReleaseThreadOwnership();

}

void Asura_Xbox360_Graphics::GetDefaultParams(D3DPRESENT_PARAMETERS* pxParams)
{
	Asura_Mem::Set( pxParams, 0, sizeof( pxParams ) );

	pxParams->BackBufferWidth  = s_iScreenWidth;
	pxParams->BackBufferHeight = s_iScreenHeight;
	pxParams->BackBufferFormat = D3DFMT_A8R8G8B8;
	pxParams->Windowed         = FALSE;

	// Using predicated tiling, don't create any buffers automatically
	pxParams->EnableAutoDepthStencil = FALSE;
	pxParams->DisableAutoBackBuffer  = TRUE;
	pxParams->DisableAutoFrontBuffer = TRUE;

	pxParams->RingBufferParameters.Flags = 0;
	pxParams->RingBufferParameters.PrimarySize = 0;									// Defaults to 32 KB
	pxParams->RingBufferParameters.pPrimary = NULL;
	pxParams->RingBufferParameters.SecondarySize = ASURA_XBOX360_COMMAND_BUFFER_SIZE;	// Defaults to 2 MB
	pxParams->RingBufferParameters.pSecondary = NULL;
	pxParams->RingBufferParameters.SegmentCount = 0;									// Defaults to 32

	#if defined ASURA_XBOX360_USE_VSYNC
	// If using predicated tiling, also need to manually sync to this interval; see PresentFrame()
	pxParams->PresentationInterval = D3DPRESENT_INTERVAL_ONE;
	#else
	pxParams->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	#endif
}

void Asura_Xbox360_Graphics::Reset()
{
	SCOPED_DEVICE_OWNERSHIP();

	// Must unbind all set resources on 360 since D3D uses weak reference holding
	s_pxD3DDevice->UnsetAll();
}

void Asura_Xbox360_Graphics::Shutdown()
{
	AcquireThreadOwnership();

	Asura_Xbox360_Effect_System::Destroy( s_pxEDRAMCopyEffect );

	s_pxFullScreenVD->Release();
	s_xFullScreenVB.Destroy();

	s_pxCopyToEDRAMVD->Release();

	s_pxColourTile->Release();
	s_pxNormalTile->Release();
	s_pxDepthMtlTile->Release();
	s_pxAlbedoTile->Release();
	s_pxDepthTile->Release();

	s_pxSRTColourTile->Release();
	s_pxSRTDepthTile->Release();

	s_pxFinalRT->Release();
	s_pxFinalDepth->Release();

	s_pxPostToneMapRT->Release();
	s_pxPostToneMapLinearRT->Release();

	s_pxFrontBuffer->Release();
	s_pxSceneResolveTexture->Release();

	s_pxDepthTexture->Release();
	s_pxNormalTexture->Release();
	s_pxDepthMtlTexture->Release();
	s_pxAlbedoTexture->Release();

	Asura_Xbox360_MemoryManagement::FreePhysical(Asura_Xbox360_MemoryManagement::MT_READ_ONLY, s_pExtraMem, XALLOC_PHYSICAL_ALIGNMENT_DEFAULT);

	if( s_pxD3DDevice )
	{
		s_pxD3DDevice->Release();
	}

	if( s_pxD3DObject )
	{
		s_pxD3DObject->Release();
	}

	s_pxD3DDevice = NULL;
	s_pxD3DObject = NULL;
}

void Asura_Xbox360_Graphics::BeginFrame()
{
	AcquireThreadOwnership();
	ASURA_GPU_PROFILE_START( ASURA_GPUPROFILE_TOTALFRAME );

	// If fog parameters have changed they will need to update the fog texture.
	// Is is safe to do this update here, rather than at some point during render
	// when access to the texture may not be allowed, so we make this call to 
	// force the update if changes have occurred.
	Asura_Xbox360_Fog::GetRenderTexture();

	// PIX suggests this as a good match for our shaders
	s_pxD3DDevice->SetShaderGPRAllocation( 0, 32, 96 );

	// Some little project render loops (loading screens etc.) contain no tiling passes or EDRAM
	// management. Just start out pointing straight at the final render target for these cases.
	// Also use the final z buffer, for rendering skins in GUI
	s_pxD3DDevice->SetRenderTarget( 0, GetFinalRT() );
	s_pxD3DDevice->SetDepthStencilSurface( GetFinalDepth() );

	#if defined ASURA_DEBUG_VARIABLE_SYSTEM
		s_pxD3DDevice->SetRenderState_Inline( D3DRS_MULTISAMPLEANTIALIAS, dvs_bMSAA );
	#endif
}

void Asura_Xbox360_Graphics::EndFrame()
{
	if( IsTiling() )
	{
		// If we're still tiling, end it now and copy the results back to EDRAM
		EndTiling();
		CopySceneToEDRAM();
	}
	if( IsTiling2() )
	{
		EndTiling2();
		CopySceneToEDRAM();
	}

	#if defined ASURA_USE_PROFILING_SYSTEM

		ASURA_CPU_PROFILE__STOP( ASURA_CPU_PROFILE__TOTALFRAME );

		PIXBeginNamedEvent( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "CPU Profile Bars" );

		Asura_CPUProfile_Renderer::Render( true );
		Asura_CPUProfile_Renderer::ClearValues();

		// Note - don't call ClearValues for the GPU profiler, it gets done in the 
		// callback when the frame ends
		Asura_GPUProfile_Renderer::Render( true );		

		PIXEndNamedEvent(); // CPU Profile Bars

		// Start total frame marker from after VSync...
		ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__TOTALFRAME, "total frame");

	#endif // defined ASURA_USE_PROFILING_SYSTEM

	#if defined ASURA_XBOX360_ALLOW_STATS

		// Don't display the stats if the DVS is up, they get in the way
		#if defined ASURA_DEBUG_VARIABLE_SYSTEM
			const bool bDVSOpen = Asura_DebugVariableSystem::IsOpen();
		#else
			const bool bDVSOpen = false;
		#endif

		if( AreStatsEnabled() && !bDVSOpen )
		{
			PIXBeginNamedEvent( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "Frame Stats" );

			MEMORYSTATUS xStatus;
			GlobalMemoryStatus( &xStatus );

			Asura_Render::AllowFullScreenRendering();

			u_int uMemFreeColour = 0xFFFFFFFF;
			const float fMemFree = static_cast< float >( xStatus.dwAvailPhys ) / 1048576.0f;

			if( fMemFree > 10.0f ) uMemFreeColour = 0xFFFFFF00; // Gold; plenty of memory free.
			if( fMemFree < 2.0f  ) uMemFreeColour = 0xFFFF0000; // Red; memory getting low.

			u_int	uX	= 30;
			u_int	uY	= 4;

			Asura_DebugText::AddTextBlock(uX, uY++, uMemFreeColour, 0, "Mem Free:      %0.1f MB", fMemFree );

			#ifdef ASURA_USE_BOTANICALS
				Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFFFF, 0, "Bot Instances: %u", Asura_Xbox360_Botanicals_System::GetNumInstancesRendered() );
			#endif

			#ifdef ASURA_USE_FASTPATH_PFX
			{
				float fAll  = (float)Asura_Render::GetParticlesRenderedThisFrame();
				float fFast = (float)Asura_Platform_PFX_System_FastPath::GetNumFastPathParticlesRenderedThisFrame();
				if (fAll>0.0f)
				{
					Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFFFF, 0, "Particles:     %d (Fastpath: %0.01f%)", Asura_Render::GetParticlesRenderedThisFrame(), (fFast/fAll) * 100.0f);

					if (Asura_Platform_PFX_System_FastPath::WasVertexBufferFull())
					{			
						Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFF00, 0, "(Increase VERTEXBUFFER_SIZE)");
					}
				}
				else
				{
					Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFFFF, 0, "Particles:     %d", Asura_Render::GetParticlesRenderedThisFrame());
				}
			}
  			#else
			Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFFFF, 0, "Particles:     %d", Asura_Render::GetParticlesRenderedThisFrame());
  			#endif				

			const u_int	uTotalPolys	= Asura_Render::GetNumTriPassesRendered();
			const u_int	uArtPolys	= Asura_Render::GetNumberOfTrianglesRendered();

			uX	= 70;
			uY	= 4;
			Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFFFF, 0, "Total Polys: %u", uTotalPolys);

			if(uTotalPolys > 0)
			{
#ifdef ASURA_USE_BOTANICALS
				const u_int	uBotPolys	= Asura_Xbox360_Botanicals_System::GetNumTrianglesRendered();

				Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFFFF, 0, "Bot Polys:   %u (%0.01f%)", uBotPolys, (100.0f * static_cast<float>(uBotPolys)) / static_cast<float>(uTotalPolys));
#endif
				Asura_DebugText::AddTextBlock(uX, uY++, 0xFFFFFFFF, 0, "Art Polys:   %u (%0.01f%)", uArtPolys, (100.0f * static_cast<float>(uArtPolys)) / static_cast<float>(uTotalPolys));
			}

			PIXEndNamedEvent(); // Frame Stats
		}

		// Artists/designers are too often running master builds and not seeing the stats or asserts, so I'm forcing this to display for
		// the first 250 frames even if stats are turned off
		//
		// Note - the stats are only valid if the texture budget is disabled (a snapshot of the values when the load has finished)
		if (!Asura_Xbox360_TextureManagement::TextureBudgetEnabled())
		{
			static int s_iTexBudgetFrameCount = 250;		
			if (Asura_Xbox360_TextureManagement::GetTextureBudgetExceededAmount()>0)
			{
				if (!bDVSOpen && (AreStatsEnabled() || s_iTexBudgetFrameCount>0))
				{				
					float fLimit =   ((float)Asura_Xbox360_TextureManagement::GetTextureBudget())/(1024.0f*1024.0f);
					float fCurrent = fLimit + ((float)Asura_Xbox360_TextureManagement::GetTextureBudgetExceededAmount())/(1024.0f*1024.0f);

					Asura_DebugText::AddTextBlock(0, 3, 0xffff3333, 0, "Textures overbudget!: %0.02f MB/%0.02f MB", fCurrent, fLimit);

					s_iTexBudgetFrameCount--;
				}
			}				
			else
			{
				s_iTexBudgetFrameCount = 250;
			}

			if (Asura_Xbox360_TextureManagement::GetTextureBudgetExceededAmount()<0)
			{
				if (!bDVSOpen && AreStatsEnabled())
				{				
					uint32 uColour = 0xff33ff33;

					// Change to yellow if we're down to the last 64Mbytes
					if ((-Asura_Xbox360_TextureManagement::GetTextureBudgetExceededAmount()/1024)<65536)
					{
						uColour = 0xffffff00;
					}					

					float fLimit =   ((float)Asura_Xbox360_TextureManagement::GetTextureBudget())/(1024.0f*1024.0f);
					float fCurrent = fLimit + ((float)Asura_Xbox360_TextureManagement::GetTextureBudgetExceededAmount())/(1024.0f*1024.0f);					

					Asura_DebugText::AddTextBlock(0, 3, uColour, 0, "Textures: %0.02f MB/%0.02f MB", fCurrent, fLimit);
				}
			}
		}

	#endif // defined ASURA_XBOX360_ALLOW_STATS

	// DMR 9/10/07
	// Need to vsync before resolving, or we might resolve over the front buffer halfway through the frame, causing tearing
	s_pxD3DDevice->SynchronizeToPresentationInterval();

	Resolve( GetFrontBuffer() );

	#ifdef ASURA_USE_SCREENSHOT_SYSTEM
	if(Asura_ScreenShot_System::ShouldFreezeFrame())
	{
		Asura_ScreenShot_System::DrawBufferIsReady();	
		Resolve( GetFrontBuffer() );
	}	
	#endif

	PresentFrame(false);

	// DMR 06/06/07 - This is to fix a crash when the material response texture is locked during async loading.
	// I was getting this: 
	//
	// ERR[D3D]: The current thread doesn't own the primary D3D device.
	// Such a thread can block on a resource only if InsertFence()
	// has been called some time after the resource was last used.
	//
	// So I did what it wanted, and added an InsertFence()... and it seems to have worked...

	Asura_Xbox360_Graphics::GetDevice()->InsertFence(); 

	ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_TOTALFRAME);

	#if 0 && defined ASURA_USE_PROFILING_SYSTEM

		// Start total frame marker from after VSync...
		ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__TOTALFRAME, "total frame");

	#endif // defined ASURA_USE_PROFILING_SYSTEM

	ReleaseThreadOwnership();
}

void Asura_Xbox360_Graphics::PresentFrame(bool bNeedToSync)
{
	ASURA_CPU_PROFILE__START_LABEL( ASURA_CPU_PROFILE__PRESENT_FRAME, "Present Frame" );
	
	PIXBeginNamedEvent( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "Present Frame" );

	// Need to reset these before Present() or Swap()
	s_pxD3DDevice->SetShaderGPRAllocation( 0, 0, 0 );
	Asura_Xbox360_SkinRenderer::DisownRegisters();

	// The sync is usually done before resolving, but in the case of asserts is done now
	if (bNeedToSync)
	{
		s_pxD3DDevice->SynchronizeToPresentationInterval();
	}
	
	s_pxD3DDevice->Swap( s_pxFrontBuffer, NULL );

	PIXEndNamedEvent(); // Present Frame
	ASURA_CPU_PROFILE__STOP( ASURA_CPU_PROFILE__PRESENT_FRAME);
}

void Asura_Xbox360_Graphics::BeginTiling()
{	
	s_pxD3DDevice->SetRenderTarget( 0, s_pxColourTile );
	s_pxD3DDevice->SetRenderTarget( 1, s_pxNormalTile );
	s_pxD3DDevice->SetRenderTarget( 2, s_pxAlbedoTile );
	s_pxD3DDevice->SetRenderTarget( 3, s_pxDepthMtlTile );
	
	s_pxD3DDevice->SetDepthStencilSurface( s_pxDepthTile );

	s_pxD3DDevice->BeginTiling( 0, uNUM_TILES, &s_axTilingRects[0], NULL, 1.0f, 0 );

	s_bTiling = true;
}

void Asura_Xbox360_Graphics::CorrectVPOSForTiling( const int iTile )
{
	const LONG lX = (IsTiling() ? s_axTilingRects[ iTile ].x1 : s_axTilingRectsSRT[ iTile ].x1);
	const LONG lY = (IsTiling() ? s_axTilingRects[ iTile ].y1 : s_axTilingRectsSRT[ iTile ].y1);

	Asura_Xbox360_Effect_System::SetPredicationVPosOffset( lX, lY );
}

void Asura_Xbox360_Graphics::SetGammaSetting( const float fGamma )
{
	// (AAM - 21.10.2008) This code copied straight from Asura_Direct3D_UserOptions::
	// SetGammaSetting(). No-one can remember where the algorithm itself came from...

	D3DGAMMARAMP xRamp;
	for (int i=0; i<=255; i++)
	{
		float u = i/255.0f;
		float m = u*u;
		float l = 2*u*(1-u);

		float a = m + fGamma*l;
		
		if (a<0) a=0;
		if (a>1.0f) a=1.0f;

		xRamp.red[i]=(WORD)(a*65535);
		xRamp.green[i]=(WORD)(a*65535);
		xRamp.blue[i]=(WORD)(a*65535);
	}

	SCOPED_DEVICE_OWNERSHIP();

	// D3DSGR_CALIBRATE not available on 360
	GetDevice()->SetGammaRamp(0,D3DSGR_NO_CALIBRATION,&xRamp);

	s_fGamma = fGamma;
}

void Asura_Xbox360_Graphics::BeginTiling2()
{
	s_bTiling2 = true;

	s_pxD3DDevice->SetRenderTarget( 0, s_pxSRTColourTile);
	s_pxD3DDevice->SetRenderTarget( 1, NULL );
	s_pxD3DDevice->SetRenderTarget( 2, NULL );
	s_pxD3DDevice->SetRenderTarget( 3, NULL );

	s_pxD3DDevice->SetDepthStencilSurface( s_pxSRTDepthTile);

	const u_int uNumTiles = GetNumberOfPredicatedTilePasses();
	s_pxD3DDevice->BeginTiling( 0, uNumTiles, &s_axTilingRectsSRT[0], NULL, 1.0f, 0 );

	CopySceneToEDRAM();
}

void Asura_Xbox360_Graphics::EndTiling()
{
	ASURA_GPU_PROFILE_START(ASURA_GPUPROFILE_RESOLVE);

	{
		// NB: Can't call PixEndNamedEvent() after EndTiling(), because that breaks the event
		// hierarchy if 'predicated tiling view' isn't enabled in PIX
		SCOPEDPIXEVENT(( D3DCOLOR_XRGB( 0, 0, 0 ), "Additional Tiling Resolves" ));

		// (AAM - 22.06.2007) Resolves can clear for free, so we might as well use them. Since we can
		// switch systems on and off, can't rely on all pixels being written to all render targets

		PIXBeginNamedEvent( D3DCOLOR_XRGB( 0, 0, 0 ), "Resolve Depth-Stencil" );
		s_pxD3DDevice->Resolve( D3DRESOLVE_DEPTHSTENCIL | D3DRESOLVE_FRAGMENT0, NULL,
			s_pxDepthTexture, NULL, 0, 0, NULL, 1.0f, 0, NULL );
		PIXEndNamedEvent();

		PIXBeginNamedEvent( D3DCOLOR_XRGB( 0, 0, 0 ), "Resolve Target 1 (Normal)" );
		s_pxD3DDevice->Resolve( D3DRESOLVE_RENDERTARGET1 | D3DRESOLVE_FRAGMENT0
			| D3DRESOLVE_CLEARRENDERTARGET | D3DRESOLVE_CLEARDEPTHSTENCIL, NULL, s_pxNormalTexture,
			NULL, 0, 0, NULL, 1.0f, 0, NULL );
		PIXEndNamedEvent();

		PIXBeginNamedEvent( D3DCOLOR_XRGB( 0, 0, 0 ), "Resolve Target 2 (Albedo)" );
		s_pxD3DDevice->Resolve( D3DRESOLVE_RENDERTARGET2 | D3DRESOLVE_CLEARRENDERTARGET, NULL,
			s_pxAlbedoTexture, NULL, 0, 0, NULL, 1.0f, 0, NULL );
		PIXEndNamedEvent();

		PIXBeginNamedEvent( D3DCOLOR_XRGB( 0, 0, 0 ), "Resolve Target 3 (Depth+Mtl)" );
		s_pxD3DDevice->Resolve( D3DRESOLVE_RENDERTARGET3 | D3DRESOLVE_FRAGMENT0
			| D3DRESOLVE_CLEARRENDERTARGET, NULL, s_pxDepthMtlTexture, NULL, 0, 0, NULL, 1.0f, 0,
			NULL );
		PIXEndNamedEvent();
	}

	ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_RESOLVE);


	#if defined ASURA_ASSERT
		const HRESULT xResult =
	#endif
		s_pxD3DDevice->EndTiling( D3DRESOLVE_RENDERTARGET0, NULL, GetSceneResolveTexture(), NULL,
			1.0f, 0, NULL );

	Asura_Assert( SUCCEEDED( xResult ), "Command buffer ran out of space." );

	s_bTiling = false;
}

void Asura_Xbox360_Graphics::EndTiling2()
{
	if (!s_bTiling2)
		return;

	ASURA_GPU_PROFILE_START(ASURA_GPUPROFILE_RESOLVE);

	{
		// NB: Can't call PixEndNamedEvent() after EndTiling(), because that breaks the event
		// hierarchy if 'predicated tiling view' isn't enabled in PIX
		SCOPEDPIXEVENT(( D3DCOLOR_XRGB( 0, 0, 0 ), "Additional Tiling Resolves" ));

		// (AAM - 22.06.2007) Resolves can clear for free, so we might as well use them. Since we can
		// switch systems on and off, can't rely on all pixels being written to all render targets

		PIXBeginNamedEvent( D3DCOLOR_XRGB( 0, 0, 0 ), "Resolve Depth-Stencil" );
		s_pxD3DDevice->Resolve( D3DRESOLVE_DEPTHSTENCIL, NULL, s_pxDepthTexture, NULL, 0, 0, NULL, 1.0f, 0, NULL );
		PIXEndNamedEvent();
	}

	ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_RESOLVE);


#if defined ASURA_ASSERT
	const HRESULT xResult =
#endif
		s_pxD3DDevice->EndTiling( D3DRESOLVE_RENDERTARGET0, NULL, GetSceneResolveTexture(), NULL, 1.0f, 0, NULL );


	Asura_Assert( SUCCEEDED( xResult ), "Command buffer ran out of space." );

	s_bTiling2 = false;


	// dont need to copy colour, just depth/stencil as tonemapping is about to write it anyway
	CopySceneToEDRAM(true);

	CorrectVPOSForTiling(0);
}



void Asura_Xbox360_Graphics::Resolve( Direct3D_Texture* const pxTexture )
{
	ASURA_GPU_PROFILE_START( ASURA_GPUPROFILE_RESOLVE );

	s_pxD3DDevice->Resolve( D3DRESOLVE_RENDERTARGET0, NULL,
		pxTexture, NULL, 0, 0, NULL, 1.0f, 0, NULL );

	ASURA_GPU_PROFILE_END( ASURA_GPUPROFILE_RESOLVE );
}

void Asura_Xbox360_Graphics::ClearZBuffer()
{
	SCOPEDPIXEVENT(( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "Clear Z-Buffer" ));

	// (AAM - 12.02.2008) Shouldn't need to do the wireframe check as on PC, because predicated
	// tiling should take care of it for us
	s_pxD3DDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0 );
}

void Asura_Xbox360_Graphics::ApplyCameraSettings()
{
	const Asura_ViewingArea* xViewingArea = Asura_Camera::GetViewingArea();

	Direct3D_Viewport xViewport;
	xViewport.X      = Asura_Maths::FloatToInt(Asura_Viewport::GetWidth()* xViewingArea->m_fXOffset);
	xViewport.Y      = Asura_Maths::FloatToInt(Asura_Viewport::GetHeight()* xViewingArea->m_fYOffset);
	xViewport.Width  = Asura_Maths::FloatToInt(Asura_Viewport::GetWidth()* xViewingArea->m_fWidth);
	xViewport.Height = Asura_Maths::FloatToInt(Asura_Viewport::GetHeight()* xViewingArea->m_fHeight);
	xViewport.MinZ   = s_fViewportMinZ;
	xViewport.MaxZ   = s_fViewportMaxZ;

	GetDevice()->SetViewport(&xViewport);

	const Asura_Vector_3*	pxCameraPosition	= Asura_Camera::GetPosition();
	const Asura_Matrix_3x3*	pxCameraOrientation	= Asura_Camera::GetOrientation();

	// View matrix

	D3DXMatrixIdentity( &s_xViewMatrix );
	D3DXMatrixTranslation( &s_xProjectionMatrix, -pxCameraPosition->x, -pxCameraPosition->y, -pxCameraPosition->z );

	s_xViewMatrix._11 = pxCameraOrientation->e11;
	s_xViewMatrix._12 = -pxCameraOrientation->e12;
	s_xViewMatrix._13 = pxCameraOrientation->e13;
	s_xViewMatrix._21 = pxCameraOrientation->e21;
	s_xViewMatrix._22 = -pxCameraOrientation->e22;
	s_xViewMatrix._23 = pxCameraOrientation->e23;
	s_xViewMatrix._31 = pxCameraOrientation->e31;
	s_xViewMatrix._32 = -pxCameraOrientation->e32;
	s_xViewMatrix._33 = pxCameraOrientation->e33;

	D3DXMatrixMultiply( &s_xViewMatrix, &s_xProjectionMatrix, &s_xViewMatrix );

	// Projection matrix

	D3DXMatrixPerspectiveFovLH( &s_xProjectionMatrix,
								Asura_Camera::GetVerticalFieldOfView(),
								Asura_Camera::GetXOverYAspectRatio(),
								Asura_Camera::GetNearClip(),
								Asura_Camera::GetFarClip());

	if( Asura_Render::ShouldRenderMirrored() )
	{
		s_xProjectionMatrix._11 = -s_xProjectionMatrix._11;
	}

	// Apply the sub-pixel offset (used for high-res screenshots)

	const Asura_Vector_2& xSubPixelOffset = Asura_Render::GetSubPixelOffset();
	const float fOffsetX = xSubPixelOffset.x / static_cast< float >( Asura_Viewport::GetWidth() * 0.5f );
	const float fOffsetY = xSubPixelOffset.y / static_cast< float >( Asura_Viewport::GetHeight() * 0.5f );

	D3DXMATRIX xSubPixelOffsetMatrix;
	D3DXMatrixTranslation( &xSubPixelOffsetMatrix, fOffsetX, -fOffsetY, 0 );
	D3DXMatrixMultiply( &s_xProjectionMatrix, &s_xProjectionMatrix, &xSubPixelOffsetMatrix );

	// Store view-projection too

	D3DXMatrixMultiply( &s_xViewProjectionMatrix, &s_xViewMatrix, &s_xProjectionMatrix );

	// Copy the info to the effect pool's shared parameters
	Asura_Xbox360_Effect_System::SetSharedParameters();
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Xbox360_Graphics::CopySceneToEDRAM()
//
// Copies the final scene back into EDRAM for post-processing. Should be called
// once geometry such as environment and entities are rendered, but before PFX
// etc. Also restores the depth-stencil buffer
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////
void Asura_Xbox360_Graphics::CopySceneToEDRAM(bool bCopyColourBuffer)
{
	SCOPEDPIXEVENT(( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "Copy Scene to EDRAM" ));

	ASURA_GPU_PROFILE_START(ASURA_GPUPROFILE_COPYTOEDRAM);

	s_pxD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALL );
	s_pxD3DDevice->SetRenderTarget( 1, NULL );
	s_pxD3DDevice->SetRenderTarget( 2, NULL );
	s_pxD3DDevice->SetRenderTarget( 3, NULL );
	s_pxD3DDevice->SetDepthStencilSurface( NULL );
	
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	Asura_Render::CurrentStates.RequestAlphaTest( false);
	Asura_Render::CurrentStates.RequestStencilEnabled(false);

	u_int *pxVertexData;


	// First the colour buffers
	if (bCopyColourBuffer)
	{
		SCOPEDPIXEVENT(( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "Colour" ));

		s_pxD3DDevice->SetRenderTarget( 0, GetFinalRT() );

		s_pxD3DDevice->SetVertexDeclaration( s_pxCopyToEDRAMVD );

		s_pxEDRAMCopyEffect->Begin();
		s_pxEDRAMCopyEffect->Commit();
		
		s_pxD3DDevice->BeginVertices(D3DPT_RECTLIST, 3, 8, (void**)&pxVertexData );
		*(pxVertexData++) = 0x80018001; *(pxVertexData++) = 0x00007fff;  // x = -32767, y = -32767, u = 00000, v = 00000;
		*(pxVertexData++) = 0x80017fff; *(pxVertexData++) = 0x00000000;  // x = -32767, y =  32767, u = 00000, v = 32767;
		*(pxVertexData++) = 0x7fff8001; *(pxVertexData++) = 0x7fff7fff;  // x =  32767, y = -32767, u = 32767, v = 00000;
		s_pxD3DDevice->EndVertices();
		s_pxEDRAMCopyEffect->End();
	}



	// Then the stencil buffer. Needs its own pass because of the funky depth-stencil EDRAM tile layout
	
	{
		SCOPEDPIXEVENT(( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "Depth/Stencil" ));

		s_pxD3DDevice->SetRenderTarget( 0, IsTiling2() ? s_pxStencilRestoreSRT_RT : s_pxStencilRestoreRT );

		s_pxD3DDevice->SetVertexDeclaration( s_pxFullScreenVD );
		s_xFullScreenVB.Bind();	

		if (IsTiling2())
		{
			float afTileOffset[4] = {0.0f, 0.0f, 0.0f, 0.0f};
			const u_int uNumTiles = GetNumberOfPredicatedTilePasses();
			for (u_int u = 0; u < uNumTiles; ++u)
			{
				afTileOffset[0] = (float)s_axTilingRectsSRT[u].x1;
	
				s_pxD3DDevice->SetPredication(D3DPRED_TILE(u));
	
				s_pxStencilRestoreEffect->SetVector(s_xVposOffsetHandle, afTileOffset);
				s_pxStencilRestoreEffect->Begin();
				s_pxStencilRestoreEffect->Commit();
				s_pxD3DDevice->DrawVertices( D3DPT_RECTLIST, 0, 3 );
				s_pxStencilRestoreEffect->End();
			}

			s_pxD3DDevice->SetPredication(0);
		}
		else
		{
			float afTile0Offset[4] = {0.0f, 0.0f, 0.0f, 0.0f};
			s_pxStencilRestoreEffect->SetVector(s_xVposOffsetHandle, afTile0Offset);
			s_pxStencilRestoreEffect->Begin();
			s_pxStencilRestoreEffect->Commit();
			s_pxD3DDevice->DrawVertices( D3DPT_RECTLIST, 0, 3 );

			s_pxStencilRestoreEffect->End();
		}
	}

	// Restore HiZ/Stencil
	s_pxD3DDevice->SetRenderState_Inline( D3DRS_ZENABLE, TRUE );
	s_pxD3DDevice->SetRenderState_Inline( D3DRS_ZWRITEENABLE, TRUE );
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( true );

	Asura_Xbox360_Graphics::GetDevice()->FlushHiZStencil(D3DFHZS_ASYNCHRONOUS);
	s_pxD3DDevice->SetRenderTarget( 0, NULL );
	s_pxD3DDevice->SetDepthStencilSurface( IsTiling2() ? s_pxHiZRestoreSRT : s_pxHiZRestore );
	{
		SCOPEDPIXEVENT(( D3DCOLOR_XRGB( 0xFF, 0xFF, 0xFF ), "HiZ Update" ));
		s_pxD3DDevice->SetRenderState_Inline( D3DRS_ZFUNC, D3DCMP_NEVER );

		s_pxEDRAMCopyEffect->Begin(1, 0);
		s_pxEDRAMCopyEffect->Commit();
		s_pxD3DDevice->SetPixelShader( NULL );
		s_pxD3DDevice->DrawVertices( D3DPT_RECTLIST, 0, 3 );
		s_pxEDRAMCopyEffect->End();


		s_pxD3DDevice->SetRenderState_Inline( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
	}
	s_pxD3DDevice->SetRenderTarget( 0, GetFinalRT() );
	s_pxD3DDevice->SetDepthStencilSurface( GetFinalDepth() );
	Asura_Xbox360_Graphics::GetDevice()->FlushHiZStencil(D3DFHZS_ASYNCHRONOUS);
	ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_COPYTOEDRAM);
}


void Asura_Xbox360_Graphics::SetTranslucencyModeMRT(int iRenderTarget,  ASURA_TRANSLUCENCY_MODE eMode)
{
	Asura_Assert(iRenderTarget>0, "Don't use this to set the RT 0 blend mode - use Asura_Renderstates instead!");

	D3DBLENDSTATE xBlendState;
	xBlendState.BlendOp = xBlendState.BlendOpAlpha = D3DBLENDOP_ADD;

	switch( eMode )
	{
	case ASURA_TRANSLUCENCY_OFF:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_ONE;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ZERO;
			break;
		}

	case ASURA_TRANSLUCENCY_NORMAL:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_SRCALPHA;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_INVSRCALPHA;
			break;
		}

	case ASURA_TRANSLUCENCY_COLOUR:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_DESTCOLOR;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ZERO;
			break;
		}

	case ASURA_TRANSLUCENCY_DOUBLECOLOUR:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_DESTCOLOR;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_SRCCOLOR;
			break;
		}

	case ASURA_TRANSLUCENCY_INVCOLOUR:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_ZERO;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_INVSRCCOLOR;
			break;
		}

	case ASURA_TRANSLUCENCY_GLOWING:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_SRCALPHA;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ONE;
			break;
		}

	case ASURA_TRANSLUCENCY_DARKENINGCOLOUR:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_INVDESTCOLOR;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ZERO;
			break;
		}

	case ASURA_TRANSLUCENCY_JUSTSETZ:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_ZERO;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ONE;
			break;
		}

	case ASURA_TRANSLUCENCY_ALPHACOMPARE:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_DESTALPHA;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ZERO;
			break;
		}

	case ASURA_TRANSLUCENCY_UGH:
		{
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_DESTCOLOR;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ONE;
			break;
		}

	default:
		{
			Asura_Assert( false, "Unknown translucency mode." );
			xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_ONE;
			xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ZERO;
			break;
		}
	}

	Asura_Xbox360_Graphics::GetDevice()->SetBlendState( iRenderTarget, xBlendState );
}


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Xbox360_Graphics::ResetDepthBias
// Author       : Andy Tate
// Date         : 20-Jul-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_Graphics::ResetDepthBias(void)
{
	if (s_bIsCurrentlyRenderingInSkybox)
	{
		SetSkyboxDepthBias();
	} else {
		s_pxD3DDevice->SetRenderState_Inline(D3DRS_DEPTHBIAS, 0);
	}
}

/// End of function 'Asura_Xbox360_Graphics::ResetDepthBias'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Xbox360_Graphics::SetDecalDepthBias
// Author       : Andy Tate
// Date         : 20-Jul-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_Graphics::SetDecalDepthBias(void)
{
	const DWORD	dwDepthBias	= *(DWORD*)(&g_fDecalDepthBias);

	s_pxD3DDevice->SetRenderState_Inline(D3DRS_DEPTHBIAS, dwDepthBias);
}

/// End of function 'Asura_Xbox360_Graphics::SetDecalDepthBias'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Xbox360_Graphics::SetSkyboxDepthBias
// Author       : Andy Tate
// Date         : 20-Jul-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_Graphics::SetSkyboxDepthBias(void)
{
	const DWORD	dwDepthBias	= *(DWORD*)(&g_fSkyboxDepthBias);

	s_pxD3DDevice->SetRenderState_Inline(D3DRS_DEPTHBIAS, dwDepthBias);
}

/// End of function 'Asura_Xbox360_Graphics::SetSkyboxDepthBias'. ///

#if defined ASURA_SUPPORT_COMMAND_CONSOLE

bool Asura_Xbox360_Graphics::UseNullHardware( bool bHelp )
{
	if( bHelp )
	{
		Asura_CommandConsole::Printf( "Enables infinitely-fast hardware. Nothing will be rendered. Cannot be undone." );
	}
	else
	{
		// NB this cannot be reversed
		D3D__NullHardware = TRUE;

		// Having the DVS/console up will consume CPU time and distort results. Best close them
		// automatically otherwise the user might forget (since nothing more will happen on-screen)

		if( Asura_CommandConsole::IsConsoleDown() )
		{
			Asura_CommandConsole::Hide( true, true );
		}

		#if defined ASURA_DEBUG_VARIABLE_SYSTEM
			Asura_DebugVariableSystem::CloseDisplayDVS();
		#endif
	}

	return true;
}

#endif // defined ASURA_SUPPORT_COMMAND_CONSOLE

bool Asura_Xbox360_Graphics::SuspendDevice ()
{
	if(s_pxD3DDevice)
	{
		// ART - 29/04/2008 - Need to swap buffers before we do the "Suspend()",
		// otherwise we hang on a black screen (or report an error in Debug).
		PresentFrame();

		s_pxD3DDevice->Suspend();
		s_bDeviceSuspended = true;
		return true;
	}
	return false;
}

bool Asura_Xbox360_Graphics::ResumeDevice  ()
{
	if(s_pxD3DDevice && s_bDeviceSuspended)
	{
		s_pxD3DDevice->Resume();
		s_bDeviceSuspended = false;
		return true;
	}
	return false;
}

/// End of file. ///
