//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_HDR.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#if defined ASURA_USE_HDR

#include "Rendering/Asura_PS3_HDR.h"

// Asura Platform-independent
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Timers.h"
#include "Interface/Asura_CommandConsole.h"
#include "Rendering/Asura_Render.h"
#include "Interface/Asura_CommandConsole.h"

// Asura PS3
#include "Rendering/Shaders/Asura_PS3_GCM_Shader.h"
#include "Rendering/Asura_PS3_GCM_VertexBuffer.h"
#include "Rendering/Asura_PS3_Graphics.h"
#include "Rendering/Asura_PS3_FrameBuffer.h"
#include "Rendering/Asura_PS3_QuadOverlayRenderer.h"
#include "Rendering/Asura_PS3_RSXProfiling.h"
#include "Rendering/Asura_PS3_TextureManagement.h"

//Shaders definition

#include "Rendering/ShadersDefinition/vd/Fog_VD.h"

#include "Rendering/ShadersDefinition/vp/Fog_VP.h"

#include "Rendering/ShadersDefinition/fp/HDR_AdaptLuminance_FP.h"
#include "Rendering/ShadersDefinition/fp/HDR_ApplyToneMapping_FP.h"
#include "Rendering/ShadersDefinition/fp/HDR_CalcFinalLuminance_FP.h"
#include "Rendering/ShadersDefinition/fp/HDR_CalcLogLuminance_FP.h"
#include "Rendering/ShadersDefinition/fp/HDR_DownsampleFiltering_FP.h"
#include "Rendering/ShadersDefinition/fp/HDR_DownsamplePoint_FP.h"

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

static u_int					g_uiWidth					= 0;
static u_int					g_uiHeight					= 0;

// Working render targets for downsampling
//static Asura_PS3_FrameBuffer*	g_pxFrameBuffer				= NULL;
static Asura_PS3_Texture		g_xWorkingTexture_RGB;
static Asura_PS3_Texture		g_xWorkingTexture_R;
static Asura_PS3_Texture		g_xWorkingTexture_R16;

// Holds the current luminance value. TODO: splitscreen - use 4 texels here?
static Asura_PS3_Texture		g_xCurrentLuminanceTexture;

#define HDR_AdaptLuminance				1
#define HDR_ApplyToneMapping			2
#define HDR_ApplyToneMappingFixValue	3
#define HDR_CalcFinalLuminance			4
#define HDR_CalcLogLuminance			5
#define HDR_DownsampleFiltering			6
#define HDR_DownsamplePoint				7

enum UNIFORM_ELEMENTS_MATERIAL_RESPONSE_SYSTEM
{
	UE_MR_ExtractLumUVScaleOffset,
	UE_MR_AdaptationFactor,
	UE_MR_MaxLuminance,
	UE_MR_MinLuminance,
	UE_MR_KeyValue,
	UE_MR_WhitePointSquared,
	UE_MR_ScaleParams,
	UE_MR_FixedLuminance,

	UE_MR_ELEMENTS
};

enum TEXTURE_UNITS_MATERIAL_RESPONSE_SYSTEM
{
	TU_MR_DownsizeSampler,
	TU_MR_CurrentLuminanceSampler,
	TU_MR_SceneLuminanceSampler,
	TU_MR_InputSceneSampler,

	TU_MR_ELEMENTS
};

class Asura_PS3_HDR_Handles : public Asura_PS3_FP_Handles
{
public:

	Asura_PS3_HDR_Handles() : Asura_PS3_FP_Handles( UE_MR_ELEMENTS, TU_MR_ELEMENTS )
	{

	}

	void InitialiseFromShader( Asura_PS3_FragmentShaderBase* pxShader )
	{
		pxShader->InitUniformElement( UE_MR_ExtractLumUVScaleOffset,		"g_xExtractLumUVScaleOffset",	1 );
		pxShader->InitUniformElement( UE_MR_AdaptationFactor,		"g_fAdaptationFactor",	1 );
		pxShader->InitUniformElement( UE_MR_MaxLuminance,			"g_fMaxLuminance",		1 );
		pxShader->InitUniformElement( UE_MR_MinLuminance,			"g_fMinLuminance",		1 );
		pxShader->InitUniformElement( UE_MR_KeyValue,				"g_fKeyValue",			1 );
		pxShader->InitUniformElement( UE_MR_WhitePointSquared,		"g_fWhitePointSquared",	1 );
		pxShader->InitUniformElement( UE_MR_ScaleParams,			"g_xScaleParams",		1 );
		pxShader->InitUniformElement( UE_MR_FixedLuminance,			"g_fFixedLuminance",	1 );

		pxShader->InitTextureUnit( TU_MR_DownsizeSampler,			"g_xDownsizeSampler",			CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_LINEAR );
		pxShader->InitTextureUnit( TU_MR_CurrentLuminanceSampler,	"g_xCurrentLuminanceSampler",	CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
		pxShader->InitTextureUnit( TU_MR_SceneLuminanceSampler,		"g_xSceneLuminanceSampler",		CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
		pxShader->InitTextureUnit( TU_MR_InputSceneSampler,			"g_xInputSceneSampler",			CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
	}
};

static Asura_PS3_GCM_VertexShader*		g_pxVertexShaderSimple		= NULL;
static Asura_PS3_GCM_VertexBuffer		g_xVertexBuffer;


DVSVAR bool dvs_bShowRegion = false;
DVSVAR bool dvs_bShowDownsampleBuffer = false;


/******************************************************************************/
/* Local Functions															  */
/******************************************************************************/

void SetViewport( u_int uWidth, u_int uHeight )
{
	cellGcmSetZcullStatsEnableInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_FALSE );

    Asura_PS3_RenderingStates::SetViewport( uWidth, uHeight );
}

#if defined(ASURA_PS3_ALLOW_SHADER_RELOAD)
bool ReloadHDRShaders ( bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf("recompile & reload shaders");
		return true;
	}

	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/HDR/HDR_AdaptLuminance_FP.fpx", ASURA_SHADER_HASH_HDR, HDR_AdaptLuminance, NULL, NULL, new Asura_PS3_HDR_Handles );
	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/HDR/HDR_ApplyToneMapping_FP.fpx", ASURA_SHADER_HASH_HDR, HDR_ApplyToneMapping, NULL, NULL, new Asura_PS3_HDR_Handles );
	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/HDR/HDR_ApplyToneMappingFixValue_FP.fpx", ASURA_SHADER_HASH_HDR, HDR_ApplyToneMappingFixValue, NULL, NULL, new Asura_PS3_HDR_Handles );
	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/HDR/HDR_CalcFinalLuminance_FP.fpx", ASURA_SHADER_HASH_HDR, HDR_CalcFinalLuminance, NULL, NULL, new Asura_PS3_HDR_Handles );
	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/HDR/HDR_CalcLogLuminance_FP.fpx", ASURA_SHADER_HASH_HDR, HDR_CalcLogLuminance, NULL, NULL, new Asura_PS3_HDR_Handles );
	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/HDR/HDR_DownsampleFiltering_FP.fpx", ASURA_SHADER_HASH_HDR, HDR_DownsampleFiltering, NULL, NULL, new Asura_PS3_HDR_Handles );
	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/HDR/HDR_DownsamplePoint_FP.fpx", ASURA_SHADER_HASH_HDR, HDR_DownsamplePoint, NULL, NULL, new Asura_PS3_HDR_Handles );

	return true;
}
#endif

/******************************************************************************/
/* Asura_PS3_HDR Static Class Functions										  */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_HDR::Initialise()
//
// Initialises the HDR rendering system on PS3
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////
DVSVAR bool dvs_bHDR = true;
void Asura_PS3_HDR::Initialise()
{
#if defined( ASURA_DEBUG_VARIABLE_SYSTEM)
	Asura_CommandConsole::AddVar( L"Render.Enable.HDR", &dvs_bHDR, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.HDR.ShowRegion", &dvs_bShowRegion, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.HDR.ShowBuffer", &dvs_bShowDownsampleBuffer, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
#if defined(ASURA_PS3_ALLOW_SHADER_RELOAD)
	Asura_CommandConsole::AddCmd( L"Render.ReloadShaders.HDR", ReloadHDRShaders, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif

	// Create all render targets in the same place
	g_uiWidth				= Asura_PS3_Graphics::GetScreenWidth() >> 2;
	g_uiHeight				= Asura_PS3_Graphics::GetScreenHeight() >> 2;

	g_xWorkingTexture_RGB.CreateTexture( g_uiWidth, g_uiHeight, ASURA_PS3_TEXTUREFORMAT_ABGR16F );
	g_xWorkingTexture_R.CreateTexture( 64, 64, ASURA_PS3_TEXTUREFORMAT_ABGR16F ); //maybe we can change to RGBA8 and use the pack and unpack in the shaders
	g_xWorkingTexture_R16.CreateTexture( 16, 16, ASURA_PS3_TEXTUREFORMAT_ABGR16F ); //maybe we can change to RGBA8 and use the pack and unpack in the shaders
	g_xCurrentLuminanceTexture.CreateTexture( 16, 16, ASURA_PS3_TEXTUREFORMAT_ABGR16F );


	g_pxVertexShaderSimple		= new Asura_PS3_VP_Fog();

	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_HDR, HDR_AdaptLuminance, &_binary_PS3_HDR_AdaptLuminance_FP_bin_start, ( (unsigned char *)&_binary_PS3_HDR_AdaptLuminance_FP_bin_end - (unsigned char *)&_binary_PS3_HDR_AdaptLuminance_FP_bin_start ), new Asura_PS3_HDR_Handles, 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_HDR, HDR_ApplyToneMapping, &_binary_PS3_HDR_ApplyToneMapping_FP_bin_start, ( (unsigned char *)&_binary_PS3_HDR_ApplyToneMapping_FP_bin_end - (unsigned char *)&_binary_PS3_HDR_ApplyToneMapping_FP_bin_start ), new Asura_PS3_HDR_Handles, 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_HDR, HDR_ApplyToneMappingFixValue, &_binary_PS3_HDR_ApplyToneMappingFixValue_FP_bin_start, ( (unsigned char *)&_binary_PS3_HDR_ApplyToneMappingFixValue_FP_bin_end - (unsigned char *)&_binary_PS3_HDR_ApplyToneMappingFixValue_FP_bin_start ), new Asura_PS3_HDR_Handles, 2, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_HDR, HDR_CalcFinalLuminance, &_binary_PS3_HDR_CalcFinalLuminance_FP_bin_start, ( (unsigned char *)&_binary_PS3_HDR_CalcFinalLuminance_FP_bin_end - (unsigned char *)&_binary_PS3_HDR_CalcFinalLuminance_FP_bin_start ), new Asura_PS3_HDR_Handles, 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_HDR, HDR_CalcLogLuminance, &_binary_PS3_HDR_CalcLogLuminance_FP_bin_start, ( (unsigned char *)&_binary_PS3_HDR_CalcLogLuminance_FP_bin_end - (unsigned char *)&_binary_PS3_HDR_CalcLogLuminance_FP_bin_start ), new Asura_PS3_HDR_Handles, 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_HDR, HDR_DownsampleFiltering, &_binary_PS3_HDR_DownsampleFiltering_FP_bin_start, ( (unsigned char *)&_binary_PS3_HDR_DownsampleFiltering_FP_bin_end - (unsigned char *)&_binary_PS3_HDR_DownsampleFiltering_FP_bin_start ), new Asura_PS3_HDR_Handles, 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_HDR, HDR_DownsamplePoint, &_binary_PS3_HDR_DownsamplePoint_FP_bin_start, ( (unsigned char *)&_binary_PS3_HDR_DownsamplePoint_FP_bin_end - (unsigned char *)&_binary_PS3_HDR_DownsamplePoint_FP_bin_start ), new Asura_PS3_HDR_Handles, 1, true );


	g_xVertexBuffer.Initialise( FOG_VERTEX_SIZE, 4, CELL_GCM_LOCATION_MAIN );
	g_pxVertexShaderSimple->InitVertexBufferDeclaration( g_axVertexDeclaration, VD_ELEMENTS );
	float* pfVertexData = ( float* )g_xVertexBuffer.LockForWriting();
	pfVertexData[ 0 ] = -1; pfVertexData[ 1 ] =  1; //Top left
	pfVertexData[ 2 ] =  1; pfVertexData[ 3 ] =  1; //Top right
	pfVertexData[ 4 ] =  1; pfVertexData[ 5 ] = -1; //Bottom right
	pfVertexData[ 6 ] = -1; pfVertexData[ 7 ] = -1; //Bottom left

	//TODO: Need to set the luminance value to 0.18;
/*
	if( g_pxCurrentLuminanceTexture )
	{
		// Initialise the current luminance texture

		D3DLOCKED_RECT xLockedRect;
		g_pxCurrentLuminanceTexture->LockRect( 0, &xLockedRect, NULL, 0 );

		*static_cast< float* >( xLockedRect.pBits ) = 0.18f;
		g_pxCurrentLuminanceTexture->UnlockRect(0);
	}
*/
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_HDR::Shutdown()
//
// Shuts down the HDR rendering system on PS3
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_HDR::Shutdown()
{
//	delete g_pxFrameBuffer;
	delete g_pxVertexShaderSimple;
	g_xWorkingTexture_RGB.Destroy();
	g_xWorkingTexture_R.Destroy();
	g_xWorkingTexture_R16.Destroy();
	g_xCurrentLuminanceTexture.Destroy();
	g_xVertexBuffer.Shutdown();

	g_pxVertexShaderSimple	= NULL;
//	g_pxFrameBuffer			= NULL;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_HDR::ApplyToneMapping()
//
// Applies tone mapping to the current scene
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_HDR::ApplyToneMapping()
{
#if defined( ASURA_DEBUG_VARIABLE_SYSTEM )
	if( !dvs_bHDR ) return;
#endif

	ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__FSFX, "Apply Tone Mapping" );

	Asura_PS3_FragmentShaderCopy* pxFP;
	g_pxVertexShaderSimple->Bind();
	g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );

	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	Asura_PS3_RenderingStates::RequestZBufferEnabled( false );
	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_NONE );
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled( false );
	Asura_PS3_RenderingStates::RequestAlphaTest( false );
	Asura_PS3_RenderingStates::SetAlphaBlending( false );

	Asura_CameraData::HDRSettings xSettings;
	Asura_Camera::GetHDRSettings( xSettings );

	const float fLeft = (float)g_uiWidth * ( 0.5f + 0.5f * xSettings.m_xRegion.MinX * Asura_Camera::GetYOverXAspectRatio() );
	const float fBottom = (float)g_uiHeight * ( 0.5f - 0.5f * xSettings.m_xRegion.MaxY );
	const float fWidth = (float)g_uiWidth * ( 0.5f * (xSettings.m_xRegion.MaxX - xSettings.m_xRegion.MinX) * Asura_Camera::GetYOverXAspectRatio() );
	const float fHeight = (float)g_uiHeight * ( 0.5f * (xSettings.m_xRegion.MaxY - xSettings.m_xRegion.MinY) );

	Asura_Assert( fLeft >= 0.0f && fBottom >= 0.0f && fWidth <= g_uiWidth && fHeight <= g_uiHeight, "HDR metering region is outside screen! This can cause a crash." );
	// if we need to support this, clamp the values.

	// Simple linear rescale on initial sample, to give the concept of "black point"
	Asura_Assert( xSettings.m_fBlackPoint < xSettings.m_fWhitePoint,
		"HDR black point must be < white point." );

	const float fGradient =
		xSettings.m_fWhitePoint / (xSettings.m_fWhitePoint - xSettings.m_fBlackPoint);

	const float fOffset = -xSettings.m_fBlackPoint * fGradient;

	const float afScaleParams[] = { fGradient, fOffset };

	if( xSettings.m_bDynamicExposure )
	{
		Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->BindAsSingleRT();
		
		// First downsample to quarter size
		{

			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_DownsampleFiltering" );
			
			//set the viewport to correspond to the region that we're metering
			Asura_PS3_RenderingStates::SetViewport( (u_int)fLeft, (u_int)fBottom, (u_int)fWidth, (u_int)fHeight );

			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_DownsampleFiltering );
			pxFP->SetTexture( TU_MR_DownsizeSampler, Asura_PS3_DeferredRendering::GetRTAsTexture( ASURA_DEFERRED_RENDER_TARGET_CURRENT_FRONT_BUFFER ), false );
			pxFP->Bind();
			g_xVertexBuffer.RenderQuads( 4 );
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}
		
		// Then downsample to 64x64 and calculate average luminance for each 3x3
		{
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_CalcLogLuminance" );

			//BW this is wasteful, we should ping-pong our renders between the 2 buffers (working texture and deferred buffer).
			Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->ResolveBackBuffer( &g_xWorkingTexture_RGB );
			
			g_pxVertexShaderSimple->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_CalcLogLuminance );
			pxFP->SetTexture( TU_MR_DownsizeSampler, &g_xWorkingTexture_RGB, false );
			SetViewport( 64, 64 );
			
			// scale by width-2 to prevent sampling outside the region
			float afScaleOffset[] = { ( fWidth - 2.0f ) / 64.0f, ( fHeight - 2.0f ) / 64.0f, fLeft, fBottom };
			pxFP->SetFloats( UE_MR_ExtractLumUVScaleOffset, 4 , afScaleOffset );
			pxFP->Bind();
			g_xVertexBuffer.RenderQuads( 4 );
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}

		// Switch to sourcing from the single-channel working texture. Switch to unfiltered
		// downsampling pass

		// Down to 16x16
		{
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_DownsamplePoint" );

			Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->ResolveBackBuffer( &g_xWorkingTexture_R );
			g_pxVertexShaderSimple->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_DownsamplePoint );
			SetViewport( 16, 16 );
			pxFP->SetTexture( TU_MR_DownsizeSampler, &g_xWorkingTexture_R, false );
			pxFP->Bind();

			g_xVertexBuffer.RenderQuads( 4 );
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}

		// Then to 4x4
		{
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_DownsamplePoint" );

			Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->ResolveBackBuffer( &g_xWorkingTexture_R16 );
			g_pxVertexShaderSimple->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_DownsamplePoint );
			SetViewport( 4, 4 );

			pxFP->SetTexture( TU_MR_DownsizeSampler, &g_xWorkingTexture_R16, false );
			pxFP->Bind();
			g_xVertexBuffer.RenderQuads( 4 );
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}

		// In the final downsampling step, switch PS to apply the exp()
		{
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_CalcFinalLuminance" );

			Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->ResolveBackBuffer( &g_xWorkingTexture_R16, 4, 4 );
			g_pxVertexShaderSimple->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_CalcFinalLuminance );
			pxFP->SetTexture( TU_MR_DownsizeSampler, &g_xWorkingTexture_R16, false );
			SetViewport( 1, 1 );
			pxFP->Bind();
			g_xVertexBuffer.RenderQuads( 4 );
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}

		// Now we've found the scene's luminance value (in the top-left texel of the
		// viewport), retrieve it and lerp the current value towards it by a small amount
		{
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_AdaptLuminance" );

			Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->ResolveBackBuffer( &g_xWorkingTexture_R16, 1, 1 );
			g_pxVertexShaderSimple->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_AdaptLuminance );
			pxFP->SetFloats( UE_MR_MinLuminance, 1, &xSettings.m_fMinLuminance );
			pxFP->SetFloats( UE_MR_MaxLuminance, 1, &xSettings.m_fMaxLuminance );

			float fAdaptationFactor = Asura_Maths::Min( xSettings.m_fAdaptationRate * Asura_Timers::GetGameTimeSlice(), 1.0f );
			pxFP->SetFloats( UE_MR_AdaptationFactor, 1, &fAdaptationFactor );

			pxFP->SetTexture( TU_MR_SceneLuminanceSampler, &g_xWorkingTexture_R16, false );
			pxFP->SetTexture( TU_MR_CurrentLuminanceSampler, &g_xCurrentLuminanceTexture, false );
			pxFP->Bind();
			g_xVertexBuffer.RenderQuads( 4 );
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}
		// Final pass: apply the tone mapping
		{
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_ApplyToneMapping" );

			Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->ResolveBackBuffer( &g_xCurrentLuminanceTexture, 1, 1 );

			Asura_PS3_Graphics::SetViewportToDefaults();
			Asura_PS3_DeferredRendering::GetHDRBuffer()->BindAsSingleRT();

			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_ApplyToneMapping );
			g_pxVertexShaderSimple->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
			Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
			Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_NONE );

			pxFP->SetFloats( UE_MR_KeyValue, 1, &xSettings.m_fKeyValue );
			float fWhitePoint = xSettings.m_fWhitePoint * xSettings.m_fWhitePoint;
			pxFP->SetFloats( UE_MR_WhitePointSquared, 1, &fWhitePoint );
			pxFP->SetFloats( UE_MR_ScaleParams, 2, afScaleParams );
			pxFP->SetTexture( TU_MR_CurrentLuminanceSampler, &g_xCurrentLuminanceTexture, false );
			pxFP->SetTexture( TU_MR_InputSceneSampler, Asura_PS3_DeferredRendering::GetRTAsTexture( ASURA_DEFERRED_RENDER_TARGET_CURRENT_FRONT_BUFFER ), false );
			Asura_PS3_RenderingStates::SetSRGBWriteEnabled( Asura_PS3_Graphics::UseGammaCorrection() );
			pxFP->Bind();
			g_xVertexBuffer.RenderQuads( 4 );
			Asura_PS3_DeferredRendering::SetAfterToneMapping();
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}
	}
	else// Final pass: apply the tone mapping Fix Value
	{
		// Final pass: apply the tone mapping
		{
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "HDR_ApplyToneMappingFixValue" );

			Asura_PS3_Graphics::SetViewportToDefaults();
			Asura_PS3_DeferredRendering::GetHDRBuffer()->BindAsSingleRT();

			// Restore the vertex shader and the vertex buffer bind
			g_pxVertexShaderSimple->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
			Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
			Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_NONE );

			pxFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_HDR, HDR_ApplyToneMappingFixValue );
			pxFP->SetFloats( UE_MR_KeyValue, 1, &xSettings.m_fKeyValue );
			float fWhitePoint = xSettings.m_fWhitePoint * xSettings.m_fWhitePoint;
			pxFP->SetFloats( UE_MR_WhitePointSquared, 1, &fWhitePoint );
			pxFP->SetFloats( UE_MR_ScaleParams, 2, afScaleParams );
			pxFP->SetFloats( UE_MR_FixedLuminance, 1, &xSettings.m_fFixedLuminance );
			pxFP->SetTexture( TU_MR_InputSceneSampler, Asura_PS3_DeferredRendering::GetRTAsTexture( ASURA_DEFERRED_RENDER_TARGET_CURRENT_FRONT_BUFFER ), false );
			Asura_PS3_RenderingStates::SetSRGBWriteEnabled( Asura_PS3_Graphics::UseGammaCorrection() );
			pxFP->Bind();
			g_xVertexBuffer.RenderQuads( 4 );
			Asura_PS3_DeferredRendering::SetAfterToneMapping();
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

		}
	}

	ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__FSFX );

	//DEBUG renders:
#ifdef ASURA_DEBUG_VARIABLE_SYSTEM
	// Draw region if enabled
	if( dvs_bShowRegion )
	{
		const float fScreenWidth = static_cast< float >( Asura_PS3_Graphics::GetScreenWidth() );
		const float fScreenHeight = static_cast< float >( Asura_PS3_Graphics::GetScreenHeight() );
		const float fYOverX = fScreenHeight / fScreenWidth;

		#define FROMCLIP_X(x) ((x) * 0.5f + 0.5f)
		#define FROMCLIP_Y(y) (0.5f - (y) * 0.5f)

		// NB both dimensions specified relative to screen height. Convert from clip space
		const float fRegionLeft = FROMCLIP_X( xSettings.m_xRegion.MinX * fYOverX ) * fScreenWidth;
		const float fRegionRight = FROMCLIP_X( xSettings.m_xRegion.MaxX * fYOverX  ) * fScreenWidth;
		const float fRegionTop = FROMCLIP_Y( xSettings.m_xRegion.MaxY ) * fScreenHeight;
		const float fRegionBottom = FROMCLIP_Y( xSettings.m_xRegion.MinY ) * fScreenHeight;

		#undef FROMCLIP_X
		#undef FROMCLIP_Y

		Asura_Render::Render2DLine( fRegionLeft,	fRegionTop,		fRegionRight,	fRegionTop,		0xFFFFFFFF );
		Asura_Render::Render2DLine( fRegionRight,	fRegionTop,		fRegionRight,	fRegionBottom,	0xFFFFFFFF );
		Asura_Render::Render2DLine( fRegionRight,	fRegionBottom,	fRegionLeft,	fRegionBottom,	0xFFFFFFFF );
		Asura_Render::Render2DLine( fRegionLeft,	fRegionBottom,	fRegionLeft,	fRegionTop,		0xFFFFFFFF );
	}

	if( dvs_bShowDownsampleBuffer )
	{
		//show all steps of the downsampling
		Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->ResolveBackBuffer( &g_xWorkingTexture_RGB );

		Asura_Overlay_Quad xQuad;

		xQuad.SetRectUV( 0, 0, 1, 1 );
		xQuad.SetRectXY( 0, 0, g_uiWidth, g_uiHeight );
		xQuad.TextureIndex = ASURA_TEXTURE_NONE;
		xQuad.TranslucencyMode = ASURA_TRANSLUCENCY_OFF;
		xQuad.Colour = 0xFFFFFFFF;
		
		Asura_PS3_QuadOverlayRenderer::RenderEx( xQuad, &g_xWorkingTexture_RGB );
	}
#endif // #ifdef ASURA_DEBUG_VARIABLE_SYSTEM
}

#endif // defined ASURA_USE_HDR
