From 66283201301b0e55336f4b20407fed811acdc739 Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Tue, 11 Jul 2023 09:51:14 -0700
Subject: [PATCH] SL-19567: Add option RenderGlowNoise for low precision glow
 dithering, enabled by default

---
 indra/llrender/llshadermgr.cpp                   |  3 ++-
 indra/llrender/llshadermgr.h                     |  1 +
 indra/newview/app_settings/settings.xml          | 11 +++++++++++
 .../shaders/class1/effects/glowExtractF.glsl     | 15 ++++++++++++++-
 indra/newview/llviewercontrol.cpp                |  1 +
 indra/newview/llviewershadermgr.cpp              | 11 ++++++++++-
 indra/newview/pipeline.cpp                       | 16 ++++++++++++++++
 indra/newview/pipeline.h                         |  1 +
 8 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 75d6ef6c461..22940dc703f 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1300,8 +1300,9 @@ void LLShaderMgr::initAttribsAndUniforms()
 	mReservedUniforms.push_back("warmthAmount");
 	mReservedUniforms.push_back("glowStrength");
 	mReservedUniforms.push_back("glowDelta");
+	mReservedUniforms.push_back("glowNoiseMap");
 
-	llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_DELTA+1);
+	llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_NOISE_MAP+1);
 
 
 	mReservedUniforms.push_back("minimum_alpha");
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 46f352aa58c..ac4b393fb7d 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -131,6 +131,7 @@ class LLShaderMgr
         GLOW_WARMTH_AMOUNT,                 //  "warmthAmount"
         GLOW_STRENGTH,                      //  "glowStrength"
         GLOW_DELTA,                         //  "glowDelta"
+        GLOW_NOISE_MAP,                     //  "glowNoiseMap"
 
         MINIMUM_ALPHA,                      //  "minimum_alpha"
         EMISSIVE_BRIGHTNESS,                //  "emissive_brightness"
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9f3a5bd5b9e..8e5e092250e 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10225,6 +10225,17 @@
       <key>Value</key>
       <real>1.3</real>
     </map>
+    <key>RenderGlowNoise</key>
+    <map>
+      <key>Comment</key>
+      <string>Enables glow noise (dithering). Reduces banding from glow in certain cases.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <real>1</real>
+    </map>
     <key>DisableAllRenderTypes</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
index 7a5e14566bb..b5437d43d28 100644
--- a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
+++ b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
@@ -28,6 +28,10 @@
 out vec4 frag_color;
 
 uniform sampler2D diffuseMap;
+#if HAS_NOISE
+uniform sampler2D glowNoiseMap;
+uniform vec2 screen_res;
+#endif
 uniform float minLuminance;
 uniform float maxExtractAlpha;
 uniform vec3 lumWeights;
@@ -44,7 +48,16 @@ void main()
 	float lum = smoothstep(minLuminance, minLuminance+1.0, dot(col.rgb, lumWeights ) );
 	float warmth = smoothstep(minLuminance, minLuminance+1.0, max(col.r * warmthWeights.r, max(col.g * warmthWeights.g, col.b * warmthWeights.b)) ); 
 	
-	frag_color.rgb = col.rgb; 
+#if HAS_NOISE
+    float TRUE_NOISE_RES = 128; // See mTrueNoiseMap
+    // *NOTE: Usually this is vary_fragcoord not vary_texcoord0, but glow extraction is in screen space
+    vec3 glow_noise = texture(glowNoiseMap, vary_texcoord0.xy * (screen_res / TRUE_NOISE_RES)).xyz;
+    // Dithering. Reduces banding effects in the reduced precision glow buffer.
+    float NOISE_DEPTH = 64.0;
+    col.rgb += glow_noise / NOISE_DEPTH;
+    col.rgb = max(col.rgb, vec3(0));
+#endif
+	frag_color.rgb = col.rgb;
 	frag_color.a = max(col.a, mix(lum, warmth, warmthAmount) * maxExtractAlpha);
 	
 }
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 9707a2a7e65..9f4287c23d2 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -710,6 +710,7 @@ void settings_setup_listeners()
     setting_setup_signal_listener(gSavedSettings, "RenderGlow", handleSetShaderChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderGlowResolutionPow", handleReleaseGLBufferChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderGlowHDR", handleReleaseGLBufferChanged);
+    setting_setup_signal_listener(gSavedSettings, "RenderGlowNoise", handleSetShaderChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderGammaFull", handleSetShaderChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderVolumeLODFactor", handleVolumeLODChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderAvatarLODFactor", handleAvatarLODChanged);
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 4559d71d6d0..82b16d67bd6 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -898,11 +898,20 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
 	
 	if (success)
 	{
-		gGlowExtractProgram.mName = "Glow Extract Shader (Post)";
+        const bool use_glow_noise = gSavedSettings.getBOOL("RenderGlowNoise");
+        const std::string glow_noise_label = use_glow_noise ? " (+Noise)" : "";
+
+		gGlowExtractProgram.mName = llformat("Glow Extract Shader (Post)%s", glow_noise_label.c_str());
 		gGlowExtractProgram.mShaderFiles.clear();
 		gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractV.glsl", GL_VERTEX_SHADER));
 		gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractF.glsl", GL_FRAGMENT_SHADER));
 		gGlowExtractProgram.mShaderLevel = mShaderLevel[SHADER_EFFECT];
+
+        if (use_glow_noise)
+        {
+            gGlowExtractProgram.addPermutation("HAS_NOISE", "1");
+        }
+
 		success = gGlowExtractProgram.createShader(NULL, NULL);
 		if (!success)
 		{
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 41839902619..d50e671e059 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -157,6 +157,7 @@ S32 LLPipeline::RenderGlowResolutionPow;
 S32 LLPipeline::RenderGlowIterations;
 F32 LLPipeline::RenderGlowWidth;
 F32 LLPipeline::RenderGlowStrength;
+bool LLPipeline::RenderGlowNoise;
 bool LLPipeline::RenderDepthOfField;
 bool LLPipeline::RenderDepthOfFieldInEditMode;
 F32 LLPipeline::CameraFocusTransitionTime;
@@ -517,6 +518,7 @@ void LLPipeline::init()
 	connectRefreshCachedSettingsSafe("RenderGlowIterations");
 	connectRefreshCachedSettingsSafe("RenderGlowWidth");
 	connectRefreshCachedSettingsSafe("RenderGlowStrength");
+	connectRefreshCachedSettingsSafe("RenderGlowNoise");
 	connectRefreshCachedSettingsSafe("RenderDepthOfField");
 	connectRefreshCachedSettingsSafe("RenderDepthOfFieldInEditMode");
 	connectRefreshCachedSettingsSafe("CameraFocusTransitionTime");
@@ -1007,6 +1009,7 @@ void LLPipeline::refreshCachedSettings()
 	RenderGlowIterations = gSavedSettings.getS32("RenderGlowIterations");
 	RenderGlowWidth = gSavedSettings.getF32("RenderGlowWidth");
 	RenderGlowStrength = gSavedSettings.getF32("RenderGlowStrength");
+	RenderGlowNoise = gSavedSettings.getBOOL("RenderGlowNoise");
 	RenderDepthOfField = gSavedSettings.getBOOL("RenderDepthOfField");
 	RenderDepthOfFieldInEditMode = gSavedSettings.getBOOL("RenderDepthOfFieldInEditMode");
 	CameraFocusTransitionTime = gSavedSettings.getF32("CameraFocusTransitionTime");
@@ -6886,6 +6889,19 @@ void LLPipeline::generateGlow(LLRenderTarget* src)
 			warmthWeights.mV[2]);
 		gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_WARMTH_AMOUNT, warmthAmount);
 
+        if (RenderGlowNoise)
+        {
+            S32 channel = gGlowExtractProgram.enableTexture(LLShaderMgr::GLOW_NOISE_MAP);
+            if (channel > -1)
+            {
+                gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mTrueNoiseMap);
+                gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
+            }
+            gGlowExtractProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES,
+                                          mGlow[2].getWidth(),
+                                          mGlow[2].getHeight());
+        }
+
 		{
 			LLGLEnable blend_on(GL_BLEND);
 
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 961a55330a2..c0559ce83bc 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -1001,6 +1001,7 @@ class LLPipeline
 	static S32 RenderGlowIterations;
 	static F32 RenderGlowWidth;
 	static F32 RenderGlowStrength;
+	static bool RenderGlowNoise;
 	static bool RenderDepthOfField;
 	static bool RenderDepthOfFieldInEditMode;
 	static F32 CameraFocusTransitionTime;
-- 
GitLab