diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 9478f8c9687b5610a89915d341f7c06629b1f8be..b0c04e0fc35746042644b5aa8d84ec2c8077674e 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -129,6 +129,7 @@ set(viewer_SOURCE_FILES
     alpanelmusicticker.cpp
     alpanelquicksettings.cpp
     alpanelquicksettingspulldown.cpp
+    alrenderutils.cpp
     altoolalign.cpp
     alunzip.cpp
     alviewermenu.cpp
@@ -800,6 +801,7 @@ set(viewer_HEADER_FILES
     alpanelmusicticker.h
     alpanelquicksettings.h
     alpanelquicksettingspulldown.h
+    alrenderutils.h
     altoolalign.h
     alunzip.h
     alviewermenu.h
diff --git a/indra/newview/alcontrolcache.cpp b/indra/newview/alcontrolcache.cpp
index 94404221d66eb22a3a813bd0cab9e230f2e2f54f..1bc4c35a0913acbcb055ed5183caf517a35363e2 100644
--- a/indra/newview/alcontrolcache.cpp
+++ b/indra/newview/alcontrolcache.cpp
@@ -27,6 +27,8 @@ bool ALControlCache::SnapEnabled;
 S32  ALControlCache::ToastGap;
 F32  ALControlCache::YawFromMousePosition = 90.f;
 F32  ALControlCache::PitchFromMousePosition = 90.f;
+U32  ALControlCache::RenderToneMapType = 0;
+F32  ALControlCache::RenderToneMapExposure = 1.f;
 
 
 #define DECLARE_CTRL(ctrl, type, ctrl_type) \
@@ -80,4 +82,7 @@ void ALControlCache::initControls()
 	DECLARE_CTRL_S32(ToastGap);
     DECLARE_CTRL_F32(YawFromMousePosition);
     DECLARE_CTRL_F32(PitchFromMousePosition);
+
+	DECLARE_CTRL_U32(RenderToneMapType);
+	DECLARE_CTRL_F32(RenderToneMapExposure);
 }
diff --git a/indra/newview/alcontrolcache.h b/indra/newview/alcontrolcache.h
index 44b6fa2aae488f38a7f136681fad76b3c1473db9..a53b138cf01e344e8ac12afaf07468ebe4cbfa70 100644
--- a/indra/newview/alcontrolcache.h
+++ b/indra/newview/alcontrolcache.h
@@ -29,6 +29,8 @@ struct ALControlCache
 	static S32		ToastGap;
     static F32		YawFromMousePosition;
     static F32      PitchFromMousePosition;
+	static U32		RenderToneMapType;
+	static F32		RenderToneMapExposure;
 };
 
 #endif
\ No newline at end of file
diff --git a/indra/newview/alrenderutils.cpp b/indra/newview/alrenderutils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f14b8b74b43d9cde8986b21f3103f56ee3cff20d
--- /dev/null
+++ b/indra/newview/alrenderutils.cpp
@@ -0,0 +1,135 @@
+/**
+* @file alrenderutils.cpp
+* @brief Alchemy Render Utility
+*
+* $LicenseInfo:firstyear=2021&license=viewerlgpl$
+* Alchemy Viewer Source Code
+* Copyright (C) 2021, Alchemy Viewer Project.
+* Copyright (C) 2021, Rye Mutt <rye@alchemyviewer.org>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "alrenderutils.h"
+
+#include "llrendertarget.h"
+#include "llvertexbuffer.h"
+
+#include "alcontrolcache.h"
+#include "llviewershadermgr.h"
+
+const U32 ALRENDER_BUFFER_MASK = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1;
+
+static LLStaticHashedString al_exposure("exposure");
+
+void ALRenderUtil::restoreVertexBuffers()
+{
+	mRenderBuffer = new LLVertexBuffer(ALRENDER_BUFFER_MASK, 0);
+	mRenderBuffer->allocateBuffer(3, 0, true);
+
+	LLStrider<LLVector3> vert;
+	LLStrider<LLVector2> tc0;
+	LLStrider<LLVector2> tc1;
+	mRenderBuffer->getVertexStrider(vert);
+	mRenderBuffer->getTexCoord0Strider(tc0);
+	mRenderBuffer->getTexCoord1Strider(tc1);
+
+	vert[0].set(-1.f, -1.f, 0.f);
+	vert[1].set(3.f, -1.f, 0.f);
+	vert[2].set(-1.f, 3.f, 0.f);
+
+	mRenderBuffer->flush();
+}
+
+void ALRenderUtil::resetVertexBuffers()
+{
+	mRenderBuffer = nullptr;
+}
+
+void ALRenderUtil::renderTonemap(LLRenderTarget* src, LLRenderTarget* dst)
+{
+	gGL.matrixMode(LLRender::MM_PROJECTION);
+	gGL.pushMatrix();
+	gGL.loadIdentity();
+	gGL.matrixMode(LLRender::MM_MODELVIEW);
+	gGL.pushMatrix();
+	gGL.loadIdentity();
+
+	LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+
+	dst->bindTarget();
+	LLGLSLShader* shader;
+
+	switch (ALControlCache::RenderToneMapType)
+	{
+	default:
+	case ALTonemap::NONE:
+		shader = &gPostTonemapProgram[NONE];
+		break;
+	case ALTonemap::LINEAR:
+		shader = &gPostTonemapProgram[LINEAR];
+		break;
+	case ALTonemap::REINHARD:
+		shader = &gPostTonemapProgram[REINHARD];
+		break;
+	case ALTonemap::REINHARD2:
+		shader = &gPostTonemapProgram[REINHARD2];
+		break;
+	case ALTonemap::FILMIC:
+		shader = &gPostTonemapProgram[FILMIC];
+		break;
+	case ALTonemap::UNREAL:
+		shader = &gPostTonemapProgram[UNREAL];
+		break;
+	case ALTonemap::ACES:
+		shader = &gPostTonemapProgram[ACES];
+		break;
+	case ALTonemap::UCHIMURA:
+		shader = &gPostTonemapProgram[UCHIMURA];
+		break;
+	case ALTonemap::LOTTES:
+		shader = &gPostTonemapProgram[LOTTES];
+		break;
+	}
+
+	shader->bind();
+
+	S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, src->getUsage());
+	if (channel > -1)
+	{
+		src->bindTexture(0, channel, LLTexUnit::TFO_POINT);
+	}
+	shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, dst->getWidth(), dst->getHeight());
+
+
+	F32 exposure = llclamp(ALControlCache::RenderToneMapExposure, 0.1f, 8.f);
+	shader->uniform1f(al_exposure, exposure);
+
+	mRenderBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX);
+	mRenderBuffer->drawArrays(LLRender::TRIANGLES, 0, 3);
+	stop_glerror();
+
+	shader->unbind();
+	dst->flush();
+
+	gGL.matrixMode(LLRender::MM_PROJECTION);
+	gGL.popMatrix();
+	gGL.matrixMode(LLRender::MM_MODELVIEW);
+	gGL.popMatrix();
+}
\ No newline at end of file
diff --git a/indra/newview/alrenderutils.h b/indra/newview/alrenderutils.h
new file mode 100644
index 0000000000000000000000000000000000000000..25ea95c5579720ffe7c13119a3d701669faf892f
--- /dev/null
+++ b/indra/newview/alrenderutils.h
@@ -0,0 +1,63 @@
+/**
+* @file alrenderutils.h
+* @brief Alchemy Render Utility
+*
+* $LicenseInfo:firstyear=2021&license=viewerlgpl$
+* Alchemy Viewer Source Code
+* Copyright (C) 2021, Alchemy Viewer Project.
+* Copyright (C) 2021, Rye Mutt <rye@alchemyviewer.org>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* $/LicenseInfo$
+*/
+
+#pragma once
+
+#include "llpointer.h"
+
+#define AL_TONEMAP_COUNT 9
+
+class LLRenderTarget;
+class LLVertexBuffer;
+
+class ALRenderUtil
+{
+public:
+	ALRenderUtil() = default;
+	~ALRenderUtil() = default;
+
+	void restoreVertexBuffers();
+	void resetVertexBuffers();
+
+	enum ALTonemap : uint32_t
+	{
+		NONE = 0,
+		LINEAR,
+		REINHARD,
+		REINHARD2,
+		FILMIC,
+		UNREAL,
+		ACES,
+		UCHIMURA,
+		LOTTES,
+		TONEMAP_COUNT
+	};
+	void renderTonemap(LLRenderTarget* src, LLRenderTarget* dst);
+
+
+private:
+	LLPointer<LLVertexBuffer> mRenderBuffer;
+};
diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml
index 9913d87c8a50204c7a8bdc35753cd53d83226ff8..10c4a025c5ce0aaee5025208db3b440005ca7d1a 100644
--- a/indra/newview/app_settings/settings_alchemy.xml
+++ b/indra/newview/app_settings/settings_alchemy.xml
@@ -684,6 +684,28 @@
       <key>Value</key>
       <real>0.275</real>
     </map>
+    <key>RenderToneMapType</key>
+    <map>
+      <key>Comment</key>
+      <string>Tonemapping type 0 - None, 1 - Linear, 2 - Reinhard, 3 - Reinhard2, 4 - Filmic, 5 - Unreal, 6 - ACES, 7 - Uchimura, 8 - Lottes</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>U32</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
+    <key>RenderToneMapExposure</key>
+    <map>
+      <key>Comment</key>
+      <string>Tonemapping exposure multiplier</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>1.0</real>
+    </map>
     <key>ResetUserColorsOnLogout</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/alchemy/postNoTCV.glsl b/indra/newview/app_settings/shaders/class1/alchemy/postNoTCV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..94165b01f20115e01e95cedb8b0eb1ef0ccc6385
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/alchemy/postNoTCV.glsl
@@ -0,0 +1,40 @@
+/** 
+ * @file postNoTCV.glsl
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
+ * Alchemy Viewer Source Code
+ * Copyright (C) 2021, Rye Mutt<rye@alchemyviewer.org>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * $/LicenseInfo$
+ */
+
+uniform mat4 modelview_projection_matrix;
+
+ATTRIBUTE vec3 position;
+
+VARYING vec2 vary_fragcoord;
+
+uniform vec2 screen_res = vec2(1.f, 1.f);
+
+void main()
+{
+	//transform vertex
+	vec4 pos = modelview_projection_matrix * vec4(position.xyz, 1.0);
+	gl_Position = pos;
+
+	vary_fragcoord = (pos.xy*0.5+0.5) * screen_res;
+}
diff --git a/indra/newview/app_settings/shaders/class1/alchemy/toneMapF.glsl b/indra/newview/app_settings/shaders/class1/alchemy/toneMapF.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..59bc56d08560286dedf22081c3b6c62aed30bcb5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/alchemy/toneMapF.glsl
@@ -0,0 +1,166 @@
+/** 
+ * @file toneMapF.glsl
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
+ * Alchemy Viewer Source Code
+ * Copyright (C) 2021, Rye Mutt<rye@alchemyviewer.org>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * $/LicenseInfo$
+ */
+
+/*[EXTRA_CODE_HERE]*/
+
+#ifdef DEFINE_GL_FRAGCOLOR
+out vec4 frag_color;
+#else
+#define frag_color gl_FragColor
+#endif
+
+VARYING vec2 vary_fragcoord;
+
+uniform sampler2DRect diffuseRect;
+
+uniform vec2 screen_res;
+uniform float exposure;
+
+vec3 linear_to_srgb(vec3 cl);
+
+vec3 reinhard(vec3 x)
+{
+	return x/(1+x);
+}
+
+vec3 reinhard2(vec3 x) {
+    const float L_white = 4.0;
+    return (x * (1.0 + x / (L_white * L_white))) / (1.0 + x);
+}
+
+vec3 filmic(vec3 color)
+{
+	color = max(vec3(0.), color - vec3(0.004));
+	color = (color * (6.2 * color + .5)) / (color * (6.2 * color + 1.7) + 0.06);
+	return color;
+}
+
+vec3 unreal(vec3 x) {
+    // Unreal 3, Documentation: "Color Grading"
+    // Adapted to be close to Tonemap_ACES, with similar range
+    // Gamma 2.2 correction is baked in, don't use with sRGB conversion!
+    return x / (x + 0.155) * 1.019;
+}
+
+vec3 aces(vec3 x) {
+    // Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
+    const float a = 2.51;
+    const float b = 0.03;
+    const float c = 2.43;
+    const float d = 0.59;
+    const float e = 0.14;
+    return (x * (a * x + b)) / (x * (c * x + d) + e);
+}
+
+vec3 uchimura(vec3 x, float P, float a, float m, float l, float c, float b) {
+  float l0 = ((P - m) * l) / a;
+  float L0 = m - m / a;
+  float L1 = m + (1.0 - m) / a;
+  float S0 = m + l0;
+  float S1 = m + a * l0;
+  float C2 = (a * P) / (P - S1);
+  float CP = -C2 / P;
+
+  vec3 w0 = vec3(1.0 - smoothstep(0.0, m, x));
+  vec3 w2 = vec3(step(m + l0, x));
+  vec3 w1 = vec3(1.0 - w0 - w2);
+
+  vec3 T = vec3(m * pow(x / m, vec3(c)) + b);
+  vec3 S = vec3(P - (P - S1) * exp(CP * (x - S0)));
+  vec3 L = vec3(m + a * (x - m));
+
+  return T * w0 + L * w1 + S * w2;
+}
+
+vec3 uchimura(vec3 x) {
+  const float P = 1.0;  // max display brightness
+  const float a = 1.0;  // contrast
+  const float m = 0.22; // linear section start
+  const float l = 0.4;  // linear section length
+  const float c = 1.33; // black
+  const float b = 0.0;  // pedestal
+
+  return uchimura(x, P, a, m, l, c, b);
+}
+
+// Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines"
+vec3 lottes(vec3 x) {
+  const vec3 a = vec3(1.6);
+  const vec3 d = vec3(0.977);
+  const vec3 hdrMax = vec3(8.0);
+  const vec3 midIn = vec3(0.18);
+  const vec3 midOut = vec3(0.267);
+
+  const vec3 b =
+      (-pow(midIn, a) + pow(hdrMax, a) * midOut) /
+      ((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
+  const vec3 c =
+      (pow(hdrMax, a * d) * pow(midIn, a) - pow(hdrMax, a) * pow(midIn, a * d) * midOut) /
+      ((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
+
+  return pow(x, a) / (pow(x, a * d) * b + c);
+}
+
+void main()
+{
+    vec4 diff = texture2DRect(diffuseRect, vary_fragcoord);
+
+#if TONEMAP_METHOD != 0
+    // Exposure adjustment
+    diff.rgb *= exposure; 
+#endif
+
+#if TONEMAP_METHOD == 0 // None, Gamma Correct Only
+    #define NEEDS_GAMMA_CORRECT 1
+#elif TONEMAP_METHOD == 1 // Linear 
+    #define NEEDS_GAMMA_CORRECT 1
+#elif TONEMAP_METHOD == 2 // Reinhard method
+    #define NEEDS_GAMMA_CORRECT 1
+    diff.rgb = reinhard(diff.rgb);
+#elif TONEMAP_METHOD == 3 // Reinhard2 method
+    #define NEEDS_GAMMA_CORRECT 1
+    diff.rgb = reinhard2(diff.rgb);
+#elif TONEMAP_METHOD == 4 // Filmic method
+    diff.rgb = filmic(diff.rgb);
+#elif TONEMAP_METHOD == 5 // Unreal method
+    diff.rgb = unreal(diff.rgb);
+#elif TONEMAP_METHOD == 6 // Aces method
+    #define NEEDS_GAMMA_CORRECT 1
+    diff.rgb = aces(diff.rgb);
+#elif TONEMAP_METHOD == 7 // Uchimura's Gran Turismo method
+    #define NEEDS_GAMMA_CORRECT 1
+    diff.rgb = uchimura(diff.rgb);
+#elif TONEMAP_METHOD == 8 // Lottes 2016
+    #define NEEDS_GAMMA_CORRECT 1
+    diff.rgb = lottes(diff.rgb);
+#else
+    #define NEEDS_GAMMA_CORRECT 1
+#endif
+
+#if NEEDS_GAMMA_CORRECT
+    diff.rgb = linear_to_srgb(diff.rgb);
+#endif
+    frag_color = diff;
+}
+
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 3ec7c99a868c16512e3a15580c5b075ef6d9c7fc..84e89127e3c6fce918cb16b0db8cd1579917dece 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -179,6 +179,7 @@ LLGLSLShader			gPostNightVisionProgram;
 LLGLSLShader		    gPostSRGBToLinearProgram;
 LLGLSLShader			gPostLinearToSRGBProgram;
 LLGLSLShader            gPostCASProgram;
+LLGLSLShader			gPostTonemapProgram[AL_TONEMAP_COUNT];
 LLGLSLShader            gPostSMAAEdgeDetect[4];
 LLGLSLShader            gPostSMAABlendWeights[4];
 LLGLSLShader            gPostSMAANeighborhoodBlend[4];
@@ -824,6 +825,10 @@ void LLViewerShaderMgr::unloadShaders()
     gPostSRGBToLinearProgram.unload();
     gPostLinearToSRGBProgram.unload();
     gPostCASProgram.unload();
+	for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
+	{
+		gPostTonemapProgram[i].unload();
+	}
 
 	gDeferredDiffuseProgram.unload();
 	gDeferredDiffuseAlphaMaskProgram.unload();
@@ -1172,6 +1177,10 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
         gPostSRGBToLinearProgram.unload();
         gPostLinearToSRGBProgram.unload();
         gPostCASProgram.unload();
+		for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
+		{
+			gPostTonemapProgram[i].unload();
+		}
 		return TRUE;
 	}
 
@@ -1236,6 +1245,24 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
         success = gPostCASProgram.createShader(NULL, NULL);
     }
 
+	for (U32 i = 0; i < AL_TONEMAP_COUNT; ++i)
+	{
+		if (success)
+		{
+			gPostTonemapProgram[i].mName = "Tonemapping Shader " + std::to_string(i);
+			gPostTonemapProgram[i].mFeatures.hasSrgb = true;
+			gPostTonemapProgram[i].mShaderFiles.clear();
+			gPostTonemapProgram[i].mShaderFiles.push_back(make_pair("alchemy/postNoTCV.glsl", GL_VERTEX_SHADER));
+			gPostTonemapProgram[i].mShaderFiles.push_back(make_pair("alchemy/toneMapF.glsl", GL_FRAGMENT_SHADER));
+			gPostTonemapProgram[i].mShaderLevel = mShaderLevel[SHADER_EFFECT];
+
+			gPostTonemapProgram[i].clearPermutations();
+			gPostTonemapProgram[i].addPermutation("TONEMAP_METHOD", std::to_string(i));
+
+			success = gPostTonemapProgram[i].createShader(NULL, NULL);
+		}
+	}
+
 	return success;
 
 }
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 079f0963adad1f73e7a9c2ab8f287a18ffdee0f0..689b28b71bcf10a297360a4838c0637e4a3a4079 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -30,6 +30,8 @@
 #include "llshadermgr.h"
 #include "llmaterial.h"
 
+#include "alrenderutils.h"
+
 #define LL_DEFERRED_MULTI_LIGHT_COUNT 16
 
 class LLViewerShaderMgr: public LLShaderMgr
@@ -262,6 +264,8 @@ extern LLGLSLShader			gPostNightVisionProgram;
 extern LLGLSLShader         gPostSRGBToLinearProgram;
 extern LLGLSLShader         gPostLinearToSRGBProgram;
 extern LLGLSLShader         gPostCASProgram;
+extern LLGLSLShader			gPostTonemapProgram[AL_TONEMAP_COUNT];
+
 extern LLGLSLShader         gPostSMAAEdgeDetect[4];
 extern LLGLSLShader         gPostSMAABlendWeights[4];
 extern LLGLSLShader         gPostSMAANeighborhoodBlend[4];
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 102a6f6bd442c142db6b2bd20dad74a1cc373cf6..56d4e8742cb13a3f635ec5307d6179523019678c 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -465,6 +465,8 @@ void LLPipeline::init()
 {
 	refreshCachedSettings();
 
+	mALRenderUtil = std::make_unique<ALRenderUtil>();
+
 	gOctreeMaxCapacity = gSavedSettings.getU32("OctreeMaxNodeCapacity");
 	gOctreeMinSize = gSavedSettings.getF32("OctreeMinimumNodeSize");
 	sDynamicLOD = gSavedSettings.getBOOL("RenderDynamicLOD");
@@ -551,6 +553,9 @@ void LLPipeline::init()
 
 	mDeferredVB = new LLVertexBuffer(DEFERRED_VB_MASK, 0);
 	mDeferredVB->allocateBuffer(8, 0, true);
+
+	mALRenderUtil->restoreVertexBuffers();
+
 	setLightingDetail(-1);
 	
 	//
@@ -727,6 +732,8 @@ void LLPipeline::cleanup()
 	mDeferredVB = NULL;
 
 	mCubeVB = NULL;
+
+	mALRenderUtil.reset();
 }
 
 //============================================================================
@@ -7527,6 +7534,8 @@ void LLPipeline::doResetVertexBuffers(bool forced)
 
 	mCubeVB = NULL;
 
+	mALRenderUtil->resetVertexBuffers();
+
 	for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList())
 	{
 		for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++)
@@ -7594,7 +7603,7 @@ void LLPipeline::doResetVertexBuffers(bool forced)
 	mDeferredVB = new LLVertexBuffer(DEFERRED_VB_MASK, 0);
 	mDeferredVB->allocateBuffer(8, 0, true);
 
-
+	mALRenderUtil->restoreVertexBuffers();
 }
 
 void LLPipeline::renderObjects(U32 type, U32 mask, bool texture, bool batch_texture)
@@ -9367,54 +9376,10 @@ void LLPipeline::renderDeferredLighting(LLRenderTarget *screen_target)
 
 	 screen_target->flush();
 
-    // gamma correct lighting
-    gGL.matrixMode(LLRender::MM_PROJECTION);
-    gGL.pushMatrix();
-    gGL.loadIdentity();
-    gGL.matrixMode(LLRender::MM_MODELVIEW);
-    gGL.pushMatrix();
-    gGL.loadIdentity();
-
-    {
-        LLGLDepthTest depth(GL_FALSE, GL_FALSE);
-
-        LLVector2 tc1(0, 0);
-        LLVector2 tc2((F32) screen_target->getWidth() * 2, (F32) screen_target->getHeight() * 2);
-
-        screen_target->bindTarget();
-        // Apply gamma correction to the frame here.
-        gDeferredPostGammaCorrectProgram.bind();
-
-		S32 channel = gDeferredPostGammaCorrectProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, screen_target->getUsage());
-        if (channel > -1)
-        {
-            screen_target->bindTexture(0, channel, LLTexUnit::TFO_POINT);
-        }
-
-        gDeferredPostGammaCorrectProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, screen_target->getWidth(), screen_target->getHeight());
-
-		
-        gGL.begin(LLRender::TRIANGLE_STRIP);
-        gGL.texCoord2f(tc1.mV[0], tc1.mV[1]);
-        gGL.vertex2f(-1, -1);
-
-        gGL.texCoord2f(tc1.mV[0], tc2.mV[1]);
-        gGL.vertex2f(-1, 3);
-
-        gGL.texCoord2f(tc2.mV[0], tc1.mV[1]);
-        gGL.vertex2f(3, -1);
-
-        gGL.end();
-
-        gGL.getTexUnit(channel)->unbind(screen_target->getUsage());
-        gDeferredPostGammaCorrectProgram.unbind();
-        screen_target->flush();
-    }
-
-    gGL.matrixMode(LLRender::MM_PROJECTION);
-    gGL.popMatrix();
-    gGL.matrixMode(LLRender::MM_MODELVIEW);
-    gGL.popMatrix();
+	// Tonemapping & Gamma Correction
+	{
+		 mALRenderUtil->renderTonemap(screen_target, screen_target);
+	}
 
     screen_target->bindTarget();
 
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 58b791d969f6defc2c555bb38df7aca78887261a..274320c84afb94673ddba68a0316e3eef8ce448a 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -39,6 +39,8 @@
 #include "lldrawable.h"
 #include "llrendertarget.h"
 
+#include "alrenderutils.h"
+
 #include <stack>
 
 class LLViewerTexture;
@@ -610,6 +612,8 @@ class LLPipeline
 
 	static LLTrace::EventStatHandle<S64> sStatBatchSize;
 
+	std::unique_ptr<ALRenderUtil> mALRenderUtil;
+
 	//screen texture
 	U32 					mScreenWidth;
 	U32 					mScreenHeight;