void ShaderTextEditor::_code_complete_script(const String &p_code, List<String> *r_options) {

	_check_shader_mode();

	ShaderLanguage sl;
	String calltip;

	Error err = sl.complete(p_code, ShaderTypes::get_singleton()->get_functions(VisualServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types(), r_options, calltip);
	if (err != OK)
		ERR_PRINT("Shaderlang complete failed");

	if (calltip != "") {
		get_text_edit()->set_code_hint(calltip);
	}
}
void ShaderTextEditor::_validate_script() {

	_check_shader_mode();

	String code = get_text_edit()->get_text();
	//List<StringName> params;
	//shader->get_param_list(&params);

	ShaderLanguage sl;

	Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(VisualServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types());

	if (err != OK) {
		String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
		set_error(error_text);
		set_error_pos(sl.get_error_line() - 1, 0);
		for (int i = 0; i < get_text_edit()->get_line_count(); i++)
			get_text_edit()->set_line_as_marked(i, false);
		get_text_edit()->set_line_as_marked(sl.get_error_line() - 1, true);

	} else {
		for (int i = 0; i < get_text_edit()->get_line_count(); i++)
			get_text_edit()->set_line_as_marked(i, false);
		set_error("");
	}

	emit_signal("script_changed");
}
/**
*  @brief
*    Constructor
*/
SPRTTShaders::SPRTTShaders(Renderer &cRenderer) : SPRTT(cRenderer),
	m_pRenderTarget(nullptr),
	m_pColorTarget1(nullptr),
	m_pColorTarget2(nullptr),
	m_pColorTarget3(nullptr),
	m_pSceneVertexShader(nullptr),
	m_pSceneFragmentShader(nullptr),
	m_pSceneProgram(nullptr),
	m_pVertexShader(nullptr),
	m_pFragmentShader(nullptr),
	m_pProgram(nullptr)
{
	// Check/get number of supported color render targets
	uint8 nMaxColorTargets = 4;
	if (nMaxColorTargets > cRenderer.GetCapabilities().nMaxColorRenderTargets)
		nMaxColorTargets = cRenderer.GetCapabilities().nMaxColorRenderTargets;

	{ // Render targets
		// Create the render target. We will create a very low resolution 2D texture buffer to see funny pixels.
		m_pRenderTarget = cRenderer.CreateSurfaceTextureBuffer2D(Vector2i(64, 64), TextureBuffer::R8G8B8, SurfaceTextureBuffer::Depth|SurfaceTextureBuffer::NoMultisampleAntialiasing, nMaxColorTargets);
		if (m_pRenderTarget && nMaxColorTargets > 1) {
			// Set additional color render targets
			if (nMaxColorTargets > 1 && !m_pColorTarget1) {
				Image cImage = Image::CreateImage(DataByte, ColorRGB, Vector3i(64, 64, 1));
				m_pColorTarget1 = cRenderer.CreateTextureBuffer2D(cImage, TextureBuffer::Unknown, TextureBuffer::RenderTarget);
			}
			if (nMaxColorTargets > 2 && !m_pColorTarget2) {
				Image cImage = Image::CreateImage(DataByte, ColorRGB, Vector3i(64, 64, 1));
				m_pColorTarget2 = cRenderer.CreateTextureBuffer2D(cImage, TextureBuffer::Unknown, TextureBuffer::RenderTarget);
			}
			if (nMaxColorTargets > 3 && !m_pColorTarget3) {
				Image cImage = Image::CreateImage(DataByte, ColorRGB, Vector3i(64, 64, 1));
				m_pColorTarget3 = cRenderer.CreateTextureBuffer2D(cImage, TextureBuffer::Unknown, TextureBuffer::RenderTarget);
			}
		}
	}

	// Decide which shader language should be used (for example "GLSL" or "Cg")
	ShaderLanguage *pShaderLanguage = cRenderer.GetShaderLanguage(cRenderer.GetDefaultShaderLanguage());
	if (pShaderLanguage) {
		{ // Scene program (with MRT support)
			// Construct the string containing the fragment shader definitions
			String sDefinitions;
			if (m_pColorTarget1)
				sDefinitions += "#define MRT_1\n";
			if (m_pColorTarget2)
				sDefinitions += "#define MRT_2\n";
			if (m_pColorTarget3)
				sDefinitions += "#define MRT_3\n";

			// Shader source code
			String sVertexShaderSourceCode;
			String sFragmentShaderSourceCode;
			if (pShaderLanguage->GetShaderLanguage() == "GLSL") {
				#include "SPRTTShaders_GLSL.h"
				if (cRenderer.GetAPI() == "OpenGL ES 2.0") {
					// Get shader source codes
					sVertexShaderSourceCode   = "#version 100\n" + sSceneVertexShaderSourceCodeGLSL;
					sFragmentShaderSourceCode = "#version 100\n" + sDefinitions + sSceneFragmentShaderSourceCodeGLSL;
				} else {
					// Remove precision qualifiers so that we're able to use 120 (OpenGL 2.1 shaders) instead of 130 (OpenGL 3.0 shaders,
					// with this version we can keep the precision qualifiers) so that this shader requirements are as low as possible
					// -> In here we're using 120 instead of 110 because matrix casts are quite comfortable...
					sVertexShaderSourceCode   = "#version 120\n" + Shader::RemovePrecisionQualifiersFromGLSL(sSceneVertexShaderSourceCodeGLSL);
					sFragmentShaderSourceCode = "#version 120\n" + sDefinitions + Shader::RemovePrecisionQualifiersFromGLSL(sSceneFragmentShaderSourceCodeGLSL);
				}
			} else if (pShaderLanguage->GetShaderLanguage() == "Cg") {
				#include "SPRTTShaders_Cg.h"
				sVertexShaderSourceCode   = sSceneVertexShaderSourceCodeCg;
				sFragmentShaderSourceCode = sSceneFragmentShaderSourceCodeCg;
			}

			// Create a vertex shader instance
			// -> I define a Cg profile because when using an GLSL Cg profile (which is the default), the shader is not working correctly on my AMD/ATI ("AMD Catalyst™ 11.3") system while it worked on the tested NVIDIA system...
			m_pSceneVertexShader = pShaderLanguage->CreateVertexShader(sVertexShaderSourceCode, "arbvp1");

			// Create a fragment shader instance
			// -> I define a Cg profile because when using an GLSL Cg profile (which is the default), the shader is not working correctly on my AMD/ATI ("AMD Catalyst™ 11.3") system while it worked on the tested NVIDIA system...
			m_pSceneFragmentShader = pShaderLanguage->CreateFragmentShader(sFragmentShaderSourceCode, "arbfp1");

			// Create a program instance and assign the created vertex and fragment shaders to it
			m_pSceneProgram = pShaderLanguage->CreateProgram(m_pSceneVertexShader, m_pSceneFragmentShader);
		}

		{ // Program
			// Shader source code
			String sVertexShaderSourceCode;
			String sFragmentShaderSourceCode;
			if (pShaderLanguage->GetShaderLanguage() == "GLSL") {
				#include "SPRTTShaders_GLSL.h"
				if (cRenderer.GetAPI() == "OpenGL ES 2.0") {
					// Get shader source codes
					sVertexShaderSourceCode   = "#version 100\n" + sVertexShaderSourceCodeGLSL;
					sFragmentShaderSourceCode = "#version 100\n" + sFragmentShaderSourceCodeGLSL;
				} else {
					// Remove precision qualifiers so that we're able to use 110 (OpenGL 2.0 shaders) instead of 130 (OpenGL 3.0 shaders,
					// with this version we can keep the precision qualifiers) so that this shader requirements are as low as possible
					sVertexShaderSourceCode   = "#version 110\n" + Shader::RemovePrecisionQualifiersFromGLSL(sVertexShaderSourceCodeGLSL);
					sFragmentShaderSourceCode = "#version 110\n" + Shader::RemovePrecisionQualifiersFromGLSL(sFragmentShaderSourceCodeGLSL);
				}
			} else if (pShaderLanguage->GetShaderLanguage() == "Cg") {
				#include "SPRTTShaders_Cg.h"
				sVertexShaderSourceCode   = sVertexShaderSourceCodeCg;
				sFragmentShaderSourceCode = sFragmentShaderSourceCodeCg;
			}

			// Create a vertex shader instance
			// -> I define a Cg profile because when using an GLSL Cg profile (which is the default), the shader is not working correctly on my AMD/ATI ("AMD Catalyst™ 11.3") system while it worked on the tested NVIDIA system...
			m_pVertexShader = pShaderLanguage->CreateVertexShader(sVertexShaderSourceCode, "arbvp1");

			// Create a fragment shader instance
			// -> I define a Cg profile because when using an GLSL Cg profile (which is the default), the shader is not working correctly on my AMD/ATI ("AMD Catalyst™ 11.3") system while it worked on the tested NVIDIA system...
			m_pFragmentShader = pShaderLanguage->CreateFragmentShader(sFragmentShaderSourceCode, "arbfp1");

			// Create a program instance and assign the created vertex and fragment shaders to it
			m_pProgram = static_cast<ProgramWrapper*>(pShaderLanguage->CreateProgram(m_pVertexShader, m_pFragmentShader));
		}
	}
}
/**
*  @brief
*    Calculates the logarithmic average luminance
*/
void HDRAverageLuminance::CalculateAverageLuminance(const String &sShaderLanguage, TextureBufferRectangle &cOriginalTexture, const Color3 &cLuminanceConvert)
{
	// Get the internal texture format to use
	const TextureBuffer::EPixelFormat nInternalFormat = (cOriginalTexture.GetFormat() == TextureBuffer::R16G16B16A16F) ? TextureBuffer::L16F : TextureBuffer::L32F;

	// Get the shader language to use
	String sUsedShaderLanguage = sShaderLanguage;
	if (!sUsedShaderLanguage.GetLength())
		sUsedShaderLanguage = m_pRenderer->GetDefaultShaderLanguage();

	// Create the shaders and programs right now?
	if (!m_pVertexShader || m_pVertexShader->GetShaderLanguage() != sUsedShaderLanguage) {
		// If there's an previous instance of the program, destroy it first
		if (m_pDownsampleLogProgram) {
			delete m_pDownsampleLogProgram;
			m_pDownsampleLogProgram = nullptr;
		}
		if (m_pDownsampleLogFragmentShader) {
			delete m_pDownsampleLogFragmentShader;
			m_pDownsampleLogFragmentShader = nullptr;
		}
		if (m_pDownsampleProgram) {
			delete m_pDownsampleProgram;
			m_pDownsampleProgram = nullptr;
		}
		if (m_pDownsampleFragmentShader) {
			delete m_pDownsampleFragmentShader;
			m_pDownsampleFragmentShader = nullptr;
		}
		if (m_pDownsampleVertexShader) {
			delete m_pDownsampleVertexShader;
			m_pDownsampleVertexShader = nullptr;
		}
		if (m_pDownsampleExpProgram) {
			delete m_pDownsampleExpProgram;
			m_pDownsampleExpProgram = nullptr;
		}
		if (m_pDownsampleExpFragmentShader) {
			delete m_pDownsampleExpFragmentShader;
			m_pDownsampleExpFragmentShader = nullptr;
		}
		if (m_pVertexShader) {
			delete m_pVertexShader;
			m_pVertexShader = nullptr;
		}
		m_pDownsampleLogPositionProgramAttribute		= nullptr;
		m_pDownsampleLogTextureSizeProgramUniform		= nullptr;
		m_pDownsampleLogTextureProgramUniform			= nullptr;
		m_pDownsampleLogLuminanceConvertProgramUniform	= nullptr;
		m_pDownsampleLogEpsilonProgramUniform			= nullptr;
		m_pDownsamplePositionProgramAttribute			= nullptr;
		m_pDownsampleTextureSizeProgramUniform			= nullptr;
		m_pDownsampleSizeProgramUniform					= nullptr;
		m_pDownsampleTextureProgramUniform				= nullptr;
		m_pDownsampleExpPositionProgramAttribute		= nullptr;
		m_pDownsampleExpTextureSizeProgramUniform		= nullptr;
		m_pDownsampleExpTextureProgramUniform			= nullptr;

		// Get the shader language instance
		ShaderLanguage *pShaderLanguage = m_pRenderer->GetShaderLanguage(sUsedShaderLanguage);
		if (pShaderLanguage) {
			// Shader source code
			String sVertexShaderSourceCode;
			String sVertexShaderSourceCode_Downsample;
			String sFragmentShaderSourceCode_DownsampleLog;
			String sFragmentShaderSourceCode_Downsample;
			String sFragmentShaderSourceCode_DownsampleExp;
			if (sUsedShaderLanguage == "GLSL") {
				#include "HDRAverageLuminance_GLSL.h"
				sVertexShaderSourceCode					= sHDRAverageLuminance_GLSL_VS;
				sVertexShaderSourceCode_Downsample		= sHDRAverageLuminance_GLSL_VS_Downsample;
				sFragmentShaderSourceCode_DownsampleLog	= sHDRAverageLuminance_GLSL_FS_Common + sHDRAverageLuminance_GLSL_FS_DownsampleLog;
				sFragmentShaderSourceCode_Downsample	= sHDRAverageLuminance_GLSL_FS_Common + sHDRAverageLuminance_GLSL_FS_Downsample;
				sFragmentShaderSourceCode_DownsampleExp	= sHDRAverageLuminance_GLSL_FS_Common + sHDRAverageLuminance_GLSL_FS_DownsampleExp;
			} else if (sUsedShaderLanguage == "Cg") {
				#include "HDRAverageLuminance_Cg.h"
				sVertexShaderSourceCode					= sHDRAverageLuminance_Cg_VS;
				sVertexShaderSourceCode_Downsample		= sHDRAverageLuminance_Cg_VS_Downsample;
				sFragmentShaderSourceCode_DownsampleLog	= sHDRAverageLuminance_Cg_FS_Common + sHDRAverageLuminance_Cg_FS_DownsampleLog;
				sFragmentShaderSourceCode_Downsample	= sHDRAverageLuminance_Cg_FS_Common + sHDRAverageLuminance_Cg_FS_Downsample;
				sFragmentShaderSourceCode_DownsampleExp	= sHDRAverageLuminance_Cg_FS_Common + sHDRAverageLuminance_Cg_FS_DownsampleExp;
			}

			// Create a vertex shader instance
			m_pVertexShader           = pShaderLanguage->CreateVertexShader(sVertexShaderSourceCode);
			m_pDownsampleVertexShader = pShaderLanguage->CreateVertexShader(sVertexShaderSourceCode_Downsample);

			// Create a fragment shader instance
			m_pDownsampleLogFragmentShader = pShaderLanguage->CreateFragmentShader(sFragmentShaderSourceCode_DownsampleLog);
			m_pDownsampleFragmentShader    = pShaderLanguage->CreateFragmentShader(sFragmentShaderSourceCode_Downsample);
			m_pDownsampleExpFragmentShader = pShaderLanguage->CreateFragmentShader(sFragmentShaderSourceCode_DownsampleExp);

			// Create a program instance and assign the created vertex and fragment shaders to it
			m_pDownsampleLogProgram = pShaderLanguage->CreateProgram(m_pVertexShader, m_pDownsampleLogFragmentShader);
			if (m_pDownsampleLogProgram) {
				// Add our nark which will inform us as soon as the program gets dirty
				m_pDownsampleLogProgram->EventDirty.Connect(EventHandlerDirty);

				// Get attributes and uniforms
				OnDirty(m_pDownsampleLogProgram);
			}
			m_pDownsampleProgram = pShaderLanguage->CreateProgram(m_pDownsampleVertexShader, m_pDownsampleFragmentShader);
			if (m_pDownsampleProgram) {
				// Add our nark which will inform us as soon as the program gets dirty
				m_pDownsampleProgram->EventDirty.Connect(EventHandlerDirty);

				// Get attributes and uniforms
				OnDirty(m_pDownsampleProgram);
			}
			m_pDownsampleExpProgram = pShaderLanguage->CreateProgram(m_pVertexShader, m_pDownsampleExpFragmentShader);
			if (m_pDownsampleExpProgram) {
				// Add our nark which will inform us as soon as the program gets dirty
				m_pDownsampleExpProgram->EventDirty.Connect(EventHandlerDirty);

				// Get attributes and uniforms
				OnDirty(m_pDownsampleExpProgram);
			}
		}
	}

	// Create the fullscreen quad instance if required
	if (!m_pFullscreenQuad)
		m_pFullscreenQuad = new FullscreenQuad(*m_pRenderer);

	// Get the vertex buffer of the fullscreen quad
	VertexBuffer *pVertexBuffer = m_pFullscreenQuad->GetVertexBuffer();
	if (pVertexBuffer) {
		// First step: Downsample 2x2, calculate pixel luminance and log - I don't use a "bilinear filter" because this would mess up the incoming texel data "before" the log calculation was performed!
		if (m_pRenderer->SetProgram(m_pDownsampleLogProgram)) {
			// Get the size of the original HDR texture
			const Vector2i vRTSize = cOriginalTexture.GetSize()/2;
			if (vRTSize.x != 0 && vRTSize.y != 0) {
				// Render target size change?
				if (m_pDownsampleLogRenderTarget && (m_pDownsampleLogRenderTarget->GetSize() != vRTSize || m_pDownsampleLogRenderTarget->GetFormat() != nInternalFormat)) {
					// Destroy the downsample 2x2 log render target
					if (m_pDownsampleLogRenderTarget) {
						delete m_pDownsampleLogRenderTarget;
						m_pDownsampleLogRenderTarget = nullptr;
					}
				}

				// Create the downsample 2x2 log render target right now?
				if (!m_pDownsampleLogRenderTarget)
					m_pDownsampleLogRenderTarget = m_pRenderer->CreateSurfaceTextureBufferRectangle(vRTSize, nInternalFormat, SurfaceTextureBuffer::NoMultisampleAntialiasing);
			}
			
			// Make the downsample 2x2 log render target to the current render target
			m_pRenderer->SetRenderTarget(m_pDownsampleLogRenderTarget);

			// Set program vertex attributes, this creates a connection between "Vertex Buffer Attribute" and "Vertex Shader Attribute"
			if (m_pDownsampleLogPositionProgramAttribute)
				m_pDownsampleLogPositionProgramAttribute->Set(pVertexBuffer, PLRenderer::VertexBuffer::Position);

			// Set the "TextureSize" fragment shader parameter
			if (m_pDownsampleLogTextureSizeProgramUniform)
				m_pDownsampleLogTextureSizeProgramUniform->Set(cOriginalTexture.GetSize());

			// Set the "Texture" fragment shader parameter
			if (m_pDownsampleLogTextureProgramUniform) {
				const int nTextureUnit = m_pDownsampleLogTextureProgramUniform->Set(&cOriginalTexture);
				if (nTextureUnit >= 0) {
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::AddressU, TextureAddressing::Wrap);
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::AddressV, TextureAddressing::Wrap);
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::None);
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::None);
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
				}
			}

			// Set the "LuminanceConvert" fragment shader parameter
			if (m_pDownsampleLogLuminanceConvertProgramUniform)
				m_pDownsampleLogLuminanceConvertProgramUniform->Set(cLuminanceConvert);

			// Set the "Epsilon" fragment shader parameter
			if (m_pDownsampleLogEpsilonProgramUniform)
				m_pDownsampleLogEpsilonProgramUniform->Set(0.0001f);

			// Draw the fullscreen quad
			m_pFullscreenQuad->Draw();
		}

		Vector2i vFinalTextureSize;
		TextureBufferRectangle *pFinalTextureBufferRectangle = nullptr;

		// Second step: Reduce to <4>x<4>
		if (m_pDownsampleLogRenderTarget && m_pRenderer->SetProgram(m_pDownsampleProgram)) {
			const uint32 TextureScaleFactor = 2;

			// Set program vertex attributes, this creates a connection between "Vertex Buffer Attribute" and "Vertex Shader Attribute"
			if (m_pDownsamplePositionProgramAttribute)
				m_pDownsamplePositionProgramAttribute->Set(pVertexBuffer, PLRenderer::VertexBuffer::Position);

			// Get the size of the original HDR texture
			const Vector2i vRTSize = m_pDownsampleLogRenderTarget->GetSize()/TextureScaleFactor;
			if (vRTSize.x != 0 && vRTSize.y != 0) {
				// Render target size change?
				if (m_pDownsampleRenderTarget && (m_pDownsampleRenderTarget->GetSize() != vRTSize || m_pDownsampleRenderTarget->GetFormat() != nInternalFormat)) {
					// Destroy the downsample 4x4 render target
					if (m_pDownsampleRenderTarget) {
						delete m_pDownsampleRenderTarget;
						m_pDownsampleRenderTarget = nullptr;
					}
				}

				// Create the downsample 4x4 render target right now?
				if (!m_pDownsampleRenderTarget)
					m_pDownsampleRenderTarget = m_pRenderer->CreateSurfaceTextureBufferRectangle(vRTSize, nInternalFormat, SurfaceTextureBuffer::NoMultisampleAntialiasing);
			}

			// Get the maximum size
			Vector2i vCurrentSize = m_pDownsampleLogRenderTarget->GetSize();
			uint32   nSize		  = Math::Max(vCurrentSize.x, vCurrentSize.y);

			// Downscale in multiple render passes, we render into m_pDownsampleRenderTarget and m_pDownsampleLogRenderTarget in a ping-pong way
			bool bRenderTarget = false;
			for (; nSize>TextureScaleFactor; nSize/=TextureScaleFactor, bRenderTarget=!bRenderTarget, vCurrentSize/=TextureScaleFactor) {
				// Get the render target we will render into
				SurfaceTextureBuffer *pRenderTargetTexture = bRenderTarget ? m_pDownsampleLogRenderTarget : m_pDownsampleRenderTarget;

				// Get the texture buffer we will downscale
				TextureBufferRectangle *pTextureBufferRectangle = static_cast<TextureBufferRectangle*>(bRenderTarget ? m_pDownsampleRenderTarget->GetTextureBuffer() : m_pDownsampleLogRenderTarget->GetTextureBuffer());

				// Set the current render target
				m_pRenderer->SetRenderTarget(pRenderTargetTexture);

				// Backup texture width
				vFinalTextureSize			 = vCurrentSize/TextureScaleFactor;
				pFinalTextureBufferRectangle = static_cast<TextureBufferRectangle*>(pRenderTargetTexture->GetTextureBuffer());

				// Set the "TextureSize" fragment shader parameter
				if (m_pDownsampleTextureSizeProgramUniform)
					m_pDownsampleTextureSizeProgramUniform->Set(vCurrentSize);

				// Set the "Size" fragment shader parameter
				if (m_pDownsampleSizeProgramUniform)
					m_pDownsampleSizeProgramUniform->Set(static_cast<float>(vFinalTextureSize.x)/pRenderTargetTexture->GetSize().x, static_cast<float>(vFinalTextureSize.y)/pRenderTargetTexture->GetSize().y);

				// Set the "Texture" fragment shader parameter
				if (m_pDownsampleTextureProgramUniform) {
					const int nTextureUnit = m_pDownsampleTextureProgramUniform->Set(pTextureBufferRectangle);
					if (nTextureUnit >= 0) {
						m_pRenderer->SetSamplerState(nTextureUnit, Sampler::AddressU, TextureAddressing::Wrap);
						m_pRenderer->SetSamplerState(nTextureUnit, Sampler::AddressV, TextureAddressing::Wrap);
						m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::None);	// [TODO] Use bilinear filter
						m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::None);	// [TODO] Use bilinear filter
						m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
					}
				}

				// Draw the fullscreen quad
				m_pFullscreenQuad->Draw();
			}
		}

		// Third step: Reduce <4>x<4> to <1>x<1> and calculate the exponent
		if (m_pRenderer->SetProgram(m_pDownsampleExpProgram)) {
			// Render format change?
			if (m_pAverageLuminanceTextureBuffer2D && m_pAverageLuminanceTextureBuffer2D->GetFormat() != nInternalFormat) {
				// Destroy the result render target
				if (m_pAverageLuminanceTextureBuffer2D) {
					delete m_pAverageLuminanceTextureBuffer2D;
					m_pAverageLuminanceTextureBuffer2D = nullptr;
				}
			}

			// Create the result render target right now?
			if (!m_pAverageLuminanceTextureBuffer2D)
				m_pAverageLuminanceTextureBuffer2D = m_pRenderer->CreateSurfaceTextureBuffer2D(Vector2i::One, nInternalFormat, SurfaceTextureBuffer::NoMultisampleAntialiasing);

			// Make the result render target to the current render target
			m_pRenderer->SetRenderTarget(m_pAverageLuminanceTextureBuffer2D);

			// Set program vertex attributes, this creates a connection between "Vertex Buffer Attribute" and "Vertex Shader Attribute"
			if (m_pDownsampleExpPositionProgramAttribute)
				m_pDownsampleExpPositionProgramAttribute->Set(pVertexBuffer, PLRenderer::VertexBuffer::Position);

			// Set the "TextureSize" fragment shader parameter
			if (m_pDownsampleExpTextureSizeProgramUniform)
				m_pDownsampleExpTextureSizeProgramUniform->Set(vFinalTextureSize);

			// Set the "Texture" fragment shader parameter
			if (m_pDownsampleExpTextureProgramUniform) {
				const int nTextureUnit = m_pDownsampleExpTextureProgramUniform->Set(pFinalTextureBufferRectangle);
				if (nTextureUnit >= 0) {
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::AddressU, TextureAddressing::Wrap);
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::AddressV, TextureAddressing::Wrap);
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::None);	// [TODO] Use bilinear filter
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::None);	// [TODO] Use bilinear filter
					m_pRenderer->SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
				}
			}

			// Draw the fullscreen quad
			m_pFullscreenQuad->Draw();
		}
	}
}