/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include <math.h>
		#include "gpu/gpu_renderer.h"
		#include "gpu/gpu_geometry.h"
		#include "gpu/gpu_shader_object.h"
		#include "gpu/gpu_draw_context.h"
		#include "core/geometry.h"
		#include "rand/rand.h"
		#include "log/log.h"

		using namespace GS;
		using namespace GPU;


//------------------------------------------------------------------------------
static	bool		IsPostProcessExcluded(NML::Tag *exclusion_key, const char *tag)
{
	NML::Tag *__t = exclusion_key ? exclusion_key->GetTypedTag(tag, Variant::VariantBool) : NULL;
	return __t && __t->GetBool();
}
//------------------------------------------------------------------------------

//---------------------------------------------------------------
#define	_SwapTarget	{	tmp = t[0]; t[0] = t[1]; t[1] = tmp;	}
//---------------------------------------------------------------

//------------------------------------------------------------------------------
void				Renderer::GetPostProcessNormalDepth(const Stack <RenderPrimitive *> display_lists[2])
{
	if	(normal_depth_updated)
		return;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	fRect old_viewport = GetViewport();

	// Generate normal/depth buffer.
	SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight()));
	fx_fbo->SetColorTexture(t_fx[2]);

	if	(render_technique == TechniqueDeferred)
	{
		SetCurrentFBO(fx_fbo);
		RenderFullscreenQuad(*single_texture_program, old_viewport.GetWidth() / (float)dimensions.x, old_viewport.GetHeight() / (float)dimensions.y, t_gbuffer[0]);
	}
	else
	{
		fx_fbo->SetDepthTexture(t_fx_depth);
		SetCurrentFBO(fx_fbo);

		Clear(0, 0, 0);
		DrawContext	dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_NormalDepth);
		DrawList(display_lists[0], dc);

		fx_fbo->SetDepthTexture(NULL);
	}

	SetViewport(old_viewport);
	normal_depth_updated = true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Renderer::ApplyDirectionalBlur(Render::Texture *t[2], const Vector2 &d, float attn)
{
	if	(!fx_blur_program)
		return;

	Render::Texture *tmp;

	ShaderInput *u_pass = fx_blur_program->GetInput("u_pass"),
				*u_blur_d = fx_blur_program->GetInput("u_blur_d"),
				*u_attenuation = fx_blur_program->GetInput("u_attenuation");

	SetShaderProgram(fx_blur_program);
	u_blur_d->SetValue(d.x, d.y);
	u_attenuation->SetValue(attn);

	fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);

	for	(int n = 0; n < 4; ++n)
	{
		fx_fbo->SetColorTexture(t[1]);
		SetCurrentFBO(fx_fbo);
		u_pass->SetValue(float(n));
		RenderFullscreenQuad(*fx_blur_program, nviewport, fRect(0, 0, 1, 1), t[0]);
		_SwapTarget
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Renderer::ApplyBloomFilter(Render::Texture *t_in, float strength, float threshold, float exponent, float radius, float strength_white_screen)
{
	if	(!strength || !single_texture_cutoff_program || !tone_mapping_program)
		return;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	Render::Texture *t[] = { t_fx[0], t_fx[1] };
	ShaderInput *u_cutoff = single_texture_cutoff_program->GetInput("u_cutoff"),
		*u_strength = tone_mapping_program->GetInput("u_strength"),
		*u_strength_white_screen = tone_mapping_program->GetInput("u_strength_white_screen");

	if	(!u_cutoff || !u_strength || !u_strength_white_screen)
		return;

	fRect old_viewport = GetViewport();

	fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
	fRect fx_viewport(old_viewport.sx / fx_scale, old_viewport.sy / fx_scale, old_viewport.ex / fx_scale, old_viewport.ey / fx_scale);

	//--------------------------------------------------------------------------
	#define	CutoffFrameToBloomFX \
	{ \
		SetViewport(fx_viewport); \
		\
		fx_fbo->SetColorTexture(t[0]); \
		SetCurrentFBO(fx_fbo);\
		SetShaderProgram(single_texture_cutoff_program); \
		u_cutoff->SetValue(threshold); \
		RenderFullscreenQuad(*single_texture_cutoff_program, nviewport, fRect(0, 0, 1, 1), t_in); \
	}


	#define	OutputBloomFXToFrame \
	{ \
		SetViewport(old_viewport);\
		\
		EnableBlending(true); \
		SetBlendFunc(BlendOne, BlendOne); \
		\
		fx_fbo->SetColorTexture(t_in); \
		SetCurrentFBO(fx_fbo);\
		SetShaderProgram(tone_mapping_program); \
		u_strength->SetValue(strength * 1.25f); \
		u_strength_white_screen->SetValue(strength_white_screen); \
		RenderFullscreenQuad(*tone_mapping_program, nviewport, fRect(0, 0, 1, 1), t[0]); \
		\
		EnableBlending(false); \
	}
	//--------------------------------------------------------------------------

	if	(view_registry->GetBool("PostProcess:Bloom:Streak:Enabled", false))
	{
		float angle = view_registry->GetReal("PostProcess:Bloom:Streak:Angle"),
			  attn = view_registry->GetReal("PostProcess:Bloom:Streak:Attenuation", 0.975f);

		for	(int n = 0; n < 2; ++n)
		{
			Vector2 d = Vector2(cos(angle), sin(angle)) * radius / 12.f;

			CutoffFrameToBloomFX
			ApplyDirectionalBlur(t, d, attn);
			OutputBloomFXToFrame

			if	(!view_registry->GetBool("PostProcess:Bloom:Streak:CrossShaped", false))
				break;

			angle += Units::Deg(90.f);
		}
	}
	else
	{
		CutoffFrameToBloomFX
		ApplyDirectionalBlur(t, Vector2(radius / 64.f, 0));
		ApplyDirectionalBlur(t, Vector2(0, radius / 64.f));
		OutputBloomFXToFrame
	}

	SetViewport(old_viewport);
	SetClippingRect(&old_viewport);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplyNoiseFilter(Render::Texture *t_in, Render::Texture *t_out, float strength, float mono, float bias)
{
	if	(!strength || !noise_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	ShaderInput *u_strength = noise_program->GetInput("u_strength"),
				*u_mono = noise_program->GetInput("u_mono"),
				*u_bias = noise_program->GetInput("u_bias"),
				*u_random = noise_program->GetInput("u_random");

	if	(!u_strength || !u_mono || !u_bias || !u_random)
		return false;

	fx_fbo->SetColorTexture(t_out);
	SetCurrentFBO(fx_fbo);

	float random[2] = { Random::FRand(), Random::FRand() };

	SetShaderProgram(noise_program);
	u_strength->SetValue(strength);
	u_mono->SetValue(mono);
	u_bias->SetValue(bias);
	u_random->SetValue(random[0], random[1]);
	RenderFullscreenQuad(*noise_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplySSAAFilter(Render::Texture *t_in, Render::Texture *t_out)
{
	if	(!ssaa_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	fx_fbo->SetColorTexture(t_out);
	SetCurrentFBO(fx_fbo);

	SetShaderProgram(ssaa_program);
	RenderFullscreenQuad(*ssaa_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);

	return true;
}

//------------------------------------------------------------------------------
bool Renderer::ApplyResolveMSAADepth(Render::Texture *t_depth_msaa, Render::Texture *t_out)
{
	if (!t_depth_msaa || !t_out || !resolve_msaa_depth_program)
		return false;
	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	fx_fbo->SetColorTexture(t_out);
	SetCurrentFBO(fx_fbo);

	ShaderInput *msaa_depth = resolve_msaa_depth_program->GetInput("u_depthMSAA");
	SetShaderProgram(resolve_msaa_depth_program);
	msaa_depth->SetValue(t_depth_msaa);

	RenderFullscreenQuad(*resolve_msaa_depth_program,viewport.GetWidth() / (float)dimensions.x,viewport.GetHeight() / (float)dimensions.y);

	return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplySSAOFilter(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> display_lists[2], float s, float r, float d, float blur_r)
{
	if	(!s || !ssao_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	ShaderInput *u_strength = ssao_program->GetInput("u_strength"),
				*u_radius = ssao_program->GetInput("u_radius"),
				*u_distance_scale = ssao_program->GetInput("u_distance_scale"),
				*u_iproj2d = ssao_program->GetInput("u_iproj2d");

	if	(!u_strength || !u_radius || !u_distance_scale)
		return false;

	GetPostProcessNormalDepth(display_lists);

#if	0
	// Normal/depth debug output.
	fx_fbo->SetColorTexture(t_in);
	SetCurrentFBO(fx_fbo);
	RenderFullscreenQuad(single_texture_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]);
	return false;
#endif

	fRect old_viewport = GetViewport();
	SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight()));

	// Render raw SSAO to FX texture.
	SetShaderProgram(ssao_program);
	u_strength->SetValue(s);
	u_radius->SetValue(r * (4 / fx_scale));
	u_distance_scale->SetValue(d);

	float iproj[] = { 1.f / m_projection.m[0][0], 1.f / m_projection.m[1][1] };
	if	(!gpu_config.tex_origin_is_top_left)
		iproj[1] = -iproj[1];
	u_iproj2d->SetValue(iproj[0], iproj[1]);

	EnableAlphaTest(false);
	EnableBlending(false);

	int out_index = 0;
	fx_fbo->SetColorTexture(t_fx[out_index]);
	SetCurrentFBO(fx_fbo);
	Clear(0, 0, 0, 0, 1, ClearColor);			// FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue.
	RenderFullscreenQuad(*ssao_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]);

	if	(ssao_blur_program)
	{
		// SSAO blur.
		if	(blur_r > 0.f)
		{
			ShaderInput *u_blur_radius = ssao_blur_program->GetInput("u_blur_radius"),
						*u_normal_depth = ssao_blur_program->GetInput("u_normal_depth");	// the normal depth texture is not always used, depending on the quality settings.

			if	(u_blur_radius && u_normal_depth)
			{
				fx_fbo->SetColorTexture(t_fx[1 - out_index]);
				SetCurrentFBO(fx_fbo);
				Clear(0, 0, 0, 0, 1, ClearColor);			// FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue.

				SetShaderProgram(ssao_blur_program);
				u_blur_radius->SetValue(blur_r);
				u_normal_depth->SetValue(t_fx[2].c_ptr());
				RenderFullscreenQuad(*ssao_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[out_index]);

				out_index = 1 - out_index;
			}
		}
	}

	// Composite back over input.
	SetViewport(old_viewport);

	fx_fbo->SetColorTexture(t_in);
	SetCurrentFBO(fx_fbo);

	EnableBlending(true);
	SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
	RenderFullscreenQuad(*single_texture_program, 1, 1, t_fx[out_index]);
	EnableBlending(false);
	return false;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplySharpenFilter(Render::Texture *t_in, Render::Texture *t_out, float strength)
{
	if	(!strength || !sharpen_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	ShaderInput *u_strength = sharpen_program->GetInput("u_strength");
	if	(!u_strength)
		return false;

	fx_fbo->SetColorTexture(t_out);
	SetCurrentFBO(fx_fbo);

	SetShaderProgram(sharpen_program);
	u_strength->SetValue(strength);
	RenderFullscreenQuad(*sharpen_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplyHSL(Render::Texture *t_in, Render::Texture *t_out, float H, float S, float L)
{
	if	(((H == 1.f) && (S == 1.f) && (L == 1.f)) || !hsl_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	ShaderInput *u_H = hsl_program->GetInput("u_H"),
				*u_S = hsl_program->GetInput("u_S"),
				*u_L = hsl_program->GetInput("u_L");
	if	(!u_H || !u_S || !u_L)
		return false;

	fx_fbo->SetColorTexture(t_out);
	SetCurrentFBO(fx_fbo);

	SetShaderProgram(hsl_program);
	u_H->SetValue(H);
	u_S->SetValue(S);
	u_L->SetValue(L);
	RenderFullscreenQuad(*hsl_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplyChromaticDispersion(Render::Texture *t_in, Render::Texture *t_out, float width)
{
	if	((width == 0.f) || !chromatic_dispersion_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	ShaderInput *u_width = chromatic_dispersion_program->GetInput("u_width");
	if	(!u_width)
		return false;

	fx_fbo->SetColorTexture(t_out);
	SetCurrentFBO(fx_fbo);

	SetShaderProgram(chromatic_dispersion_program);
	u_width->SetValue(width);
	RenderFullscreenQuad(*chromatic_dispersion_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplyRadialBlur(Render::Texture *t_in, Render::Texture *t_out, float strength, float center_x, float center_y)
{
	if	(!strength || !radial_blur_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	ShaderInput *u_strength = radial_blur_program->GetInput("u_strength"),
				*u_center = radial_blur_program->GetInput("u_center");

	if	(!u_strength || !u_center)
		return false;

	fx_fbo->SetColorTexture(t_out);
	SetCurrentFBO(fx_fbo);

	SetShaderProgram(radial_blur_program);
	u_strength->SetValue(strength);
	float center[] = { center_x * viewport.GetWidth() / (float)dimensions.x, center_y * viewport.GetHeight() / (float)dimensions.y };
	u_center->SetValue(center[0], center[1]);
	RenderFullscreenQuad(*radial_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Renderer::ApplyMotionBlur(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> display_lists[2], float strength, int quality)
{
	if	(!strength || !quality || !motion_blur_program)
		return false;

	ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);

	float k_u = viewport.GetWidth() / dimensions.x, k_v = viewport.GetHeight() / dimensions.y;

	// Generate a velocity buffer from the opaque surface list.
	fRect old_viewport = GetViewport();
	{
		ScopedPerfEvent event(this, "Render Velocity Buffer", Color::Purple);

		SetViewport(fRect(0, 0, (float)t_fx[0]->GetWidth(), (float)t_fx[0]->GetHeight()));

		fx_fbo->SetColorTexture(t_fx[0]);
		fx_fbo->SetDepthTexture(t_fx_depth);
		SetCurrentFBO(fx_fbo);

		Clear(0, 0, 0);

		DrawContext	dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_Velocity);
		DrawList(display_lists[0], dc);
	}

	// Vector field blur.
#if	0
	float k_blur = 0.5;
	Render::Texture	*t_pp_0[] = { t_fx[0], t_fx[1] };
	ApplyDirectionalBlur(t_pp_0, nVector2(k_blur, 0), 0.975f);
	Render::Texture	*t_pp_1[] = { t_fx[1], t_fx[0] };
	ApplyDirectionalBlur(t_pp_1, nVector2(0, k_blur), 0.975f);
#endif

	// Apply velocity field.
	fx_fbo->SetDepthTexture(NULL);

	// Quality: 0 - No PP, 1 - Lores blur, 2 - Hires blur.
	if	(quality == 1)
	{
		fx_fbo->SetColorTexture(t_fx[1]);
		SetCurrentFBO(fx_fbo);
		SetShaderProgram(single_texture_program);
		RenderFullscreenQuad(*single_texture_program, 1, 1, t_in);
	}
	else
	{
		fx_fbo->SetColorTexture(t_out);
		SetCurrentFBO(fx_fbo);
		SetViewport(old_viewport);
	}

	ShaderInput *u_strength = motion_blur_program->GetInput("u_strength"),
				*u_pow = motion_blur_program->GetInput("u_pow"),
				*u_max = motion_blur_program->GetInput("u_max"),
				*u_source = motion_blur_program->GetInput("u_source"),
				*u_velocity = motion_blur_program->GetInput("u_velocity");

	SetShaderProgram(motion_blur_program);
	const float	k = 1.f; // stats.bench_fps.GetFps() / 60.f;			// Normalize to 60fps.

	u_strength->SetValue(strength * 32.f * k);
	u_pow->SetValue(2.f);
	u_max->SetValue(1.f / 128.f);	// Works in normalized space, does not need to adjust for variable resolution.

	motion_blur_program->Set(*u_velocity->location, *t_fx[0], u_velocity->index);

	if	(quality == 1)	// low-res blur path
	{
		fx_fbo->SetColorTexture(t_fx[2]);
		SetCurrentFBO(fx_fbo);
		u_source->SetValue(t_fx[1].c_ptr());
		RenderFullscreenQuad(*motion_blur_program, 1, 1);

		fx_fbo->SetColorTexture(t_in);
		SetCurrentFBO(fx_fbo);
		SetViewport(old_viewport);

		EnableBlending(true);
		SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
		SetShaderProgram(single_texture_program);
		RenderFullscreenQuad(*single_texture_program, k_u, k_v, t_fx[2]);
		EnableBlending(false);
		return false;
	}

	u_source->SetValue(t_in);
	RenderFullscreenQuad(*motion_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Render::Texture			*Renderer::ApplyPostProcessChain(const Stack <RenderPrimitive *> display_lists[2])
{
	ScopedBenchmark bench(stats.bench_post_process);
	ScopedPerfEvent event(this, "Apply Post-Processing Chain", Color::Green);

	if	(!gpu_config.use_rtt)
		return t_compose[0];

	normal_depth_updated = false;

	NML::Tag *post_process_registry = view_registry ? view_registry->GetTag("PostProcess") : NULL;
	if	(!post_process_registry)
		return t_compose[0];
	NML::Tag *exclusion_key = registry.GetTag("PostProcess:Exclusion");

	fRect old_clipping = GetClippingRect();
	SetClippingRect(NULL);

	uint i_compose = 0;
	NML::Tag *tag;

	// SSAO.
#if	!(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
	if	(((tag = post_process_registry->GetTag("SSAO")) != NULL) && !IsPostProcessExcluded(exclusion_key, "SSAO"))
		if	(ApplySSAOFilter
			(
				t_compose[i_compose],
				t_compose[1 - i_compose],
				display_lists,
				view_registry->GetReal("PostProcess:SSAO:Strength", 1),
				view_registry->GetReal("PostProcess:SSAO:Radius", 2),
				view_registry->GetReal("PostProcess:SSAO:DistanceScale", 0.5),
				view_registry->GetReal("PostProcess:SSAO:BlurRadius", 8)
			))
			i_compose = 1 - i_compose;
#endif

	// Sharpen.
	if	(((tag = post_process_registry->GetTag("Sharpen")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Sharpen"))
		if	(ApplySharpenFilter
			(
				t_compose[i_compose],
				t_compose[1 - i_compose],
				view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f)
			))
			i_compose = 1 - i_compose;

	// SSAA.
	if	(render_technique == TechniqueDeferred)
		if	(registry.GetBool("Antialiasing:Enable", false))
			if	(ApplySSAAFilter
				(
					t_compose[i_compose],
					t_compose[1 - i_compose]
				))
				i_compose = 1 - i_compose;

#if	!(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
	// Motion blur.
	if	(((tag = post_process_registry->GetTag("MotionBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "MotionBlur"))
		if	(ApplyMotionBlur
			(
				t_compose[i_compose],
				t_compose[1 - i_compose],
				display_lists,
				view_registry->GetReal("PostProcess:MotionBlur:Strength", 0.5),
				registry.GetInteger("PostProcess:MotionBlur:Quality", 2)
			))
			i_compose = 1 - i_compose;
#endif

	// Radial blur.
	if	(((tag = post_process_registry->GetTag("RadialBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "RadialBlur"))
		if	(ApplyRadialBlur
			(
				t_compose[i_compose],
				t_compose[1 - i_compose],
				view_registry->GetReal("PostProcess:RadialBlur:Strength", 0.5),
				view_registry->GetReal("PostProcess:RadialBlur:CenterX", 0.5),
				view_registry->GetReal("PostProcess:RadialBlur:CenterY", 0.5)
			))
			i_compose = 1 - i_compose;

	// Bloom.
	if	(((tag = post_process_registry->GetTag("Bloom")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Bloom"))
		ApplyBloomFilter
		(
			t_compose[i_compose],
			view_registry->GetReal("PostProcess:Bloom:Strength", 1),
			view_registry->GetReal("PostProcess:Bloom:Threshold", 0.95f),
			view_registry->GetReal("PostProcess:Bloom:Exponent", 4),
			view_registry->GetReal("PostProcess:Bloom:Radius", 12),
			view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f)
		);

	// Noise.
#if	!(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
	// TODO mobile version of this.
	if	(((tag = post_process_registry->GetTag("Noise")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Noise"))
		if	(ApplyNoiseFilter
			(
				t_compose[i_compose],
				t_compose[1 - i_compose],
				view_registry->GetReal("PostProcess:Noise:Strength", 0.25f),
				view_registry->GetReal("PostProcess:Noise:Monochromatic", 0.f),
				view_registry->GetReal("PostProcess:Noise:LumaBias", 0.5f)
			)	)
			i_compose = 1 - i_compose;
#endif

	// HSL.
	if	(((tag = post_process_registry->GetTag("HueSaturation")) != NULL) && !IsPostProcessExcluded(exclusion_key, "HueSaturation"))
		if	(ApplyHSL
			(
				t_compose[i_compose],
				t_compose[1 - i_compose],
				view_registry->GetReal("PostProcess:HueSaturation:H", 1),
				view_registry->GetReal("PostProcess:HueSaturation:S", 1),
				view_registry->GetReal("PostProcess:HueSaturation:L", 1)
			)	)
			i_compose = 1 - i_compose;

	// Chromatic dispersion.
	if	(((tag = post_process_registry->GetTag("ChromDisp")) != NULL) && !IsPostProcessExcluded(exclusion_key, "ChromDisp"))
		if	(ApplyChromaticDispersion
			(
				t_compose[i_compose],
				t_compose[1 - i_compose],
				view_registry->GetReal("PostProcess:ChromDisp:Width", 1)
			)	)
			i_compose = 1 - i_compose;

	SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);

	SetClippingRect(&old_clipping);

	SetIndexSource(NULL);
	SetVertexSource(NULL, 0);
	return t_compose[i_compose];
}
//------------------------------------------------------------------------------
