From dafa93304335c3c48042e4b5a014dea2480f253f Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Tue, 11 Oct 2022 16:33:51 -0500
Subject: [PATCH] SL-18190 Fix for mystery circle showing up on east side of
 reflection probes.  Add one probe to rule them all as a fallback for pixels
 that aren't inside any influence volume.

---
 .../class3/deferred/reflectionProbeF.glsl     | 140 +++++++-----------
 indra/newview/llreflectionmapmanager.cpp      |  33 ++++-
 indra/newview/llreflectionmapmanager.h        |   2 +
 indra/newview/llviewerregion.cpp              |   2 +
 4 files changed, 82 insertions(+), 95 deletions(-)

diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
index 925398671c2..6d3bff05621 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
@@ -114,8 +114,7 @@ bool shouldSampleProbe(int i, vec3 pos)
 void preProbeSample(vec3 pos)
 {
     // TODO: make some sort of structure that reduces the number of distance checks
-
-    for (int i = 0; i < refmapCount; ++i)
+    for (int i = 1; i < refmapCount; ++i)
     {
         // found an influencing probe
         if (shouldSampleProbe(i, pos))
@@ -200,6 +199,12 @@ void preProbeSample(vec3 pos)
             }
         }
     }
+
+    if (probeInfluences == 0)
+    { // probe at index 0 is a special fallback probe
+        probeIndex[0] = 0;
+        probeInfluences = 1;
+    }
 }
 
 // from https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
@@ -316,7 +321,7 @@ vec3 boxIntersect(vec3 origin, vec3 dir, int i)
 // c - center of probe
 // r2 - radius of probe squared
 // i - index of probe 
-vec3 tapRefMap(vec3 pos, vec3 dir, out vec3 vi, out vec3 wi, float lod, vec3 c, float r2, int i)
+vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out vec3 vi, out vec3 wi, float lod, vec3 c, int i)
 {
     //lod = max(lod, 1);
     // parallax adjustment
@@ -326,10 +331,26 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out vec3 vi, out vec3 wi, float lod, vec3 c,
     if (refIndex[i].w < 0)
     {
         v = boxIntersect(pos, dir, i);
+        w = 1.0;
     }
     else
     {
-        v = sphereIntersect(pos, dir, c, r2);
+        float r = refSphere[i].w; // radius of sphere volume
+        float rr = r * r; // radius squared
+
+        v = sphereIntersect(pos, dir, c, rr);
+
+        float p = float(abs(refIndex[i].w)); // priority
+ 
+        float r1 = r * 0.1; // 90% of radius (outer sphere to start interpolating down)
+        vec3 delta = pos.xyz - refSphere[i].xyz;
+        float d2 = max(dot(delta, delta), 0.001);
+        float r2 = r1 * r1;
+
+        float atten = 1.0 - max(d2 - r2, 0.0) / max((rr - r2), 0.001);
+
+        w = 1.0 / d2;
+        w *= atten;
     }
 
     vi = v;
@@ -352,19 +373,32 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out vec3 vi, out vec3 wi, float lod, vec3 c,
 // c - center of probe
 // r2 - radius of probe squared
 // i - index of probe 
-vec3 tapIrradianceMap(vec3 pos, vec3 dir, vec3 c, float r2, int i)
+vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, vec3 c, int i)
 {
-    //lod = max(lod, 1);
     // parallax adjustment
-
     vec3 v;
     if (refIndex[i].w < 0)
     {
         v = boxIntersect(pos, dir, i);
+        w = 1.0;
     }
     else
     {
-        v = sphereIntersect(pos, dir, c, r2);
+        float r = refSphere[i].w; // radius of sphere volume
+        float p = float(abs(refIndex[i].w)); // priority
+        float rr = r * r; // radius squred
+
+        v = sphereIntersect(pos, dir, c, rr);
+
+        float r1 = r * 0.1; // 75% of radius (outer sphere to start interpolating down)
+        vec3 delta = pos.xyz - refSphere[i].xyz;
+        float d2 = dot(delta, delta);
+        float r2 = r1 * r1;
+
+        w = 1.0 / d2;
+
+        float atten = 1.0 - max(d2 - r2, 0.0) / (rr - r2);
+        w *= atten;
     }
 
     v -= c;
@@ -387,26 +421,17 @@ vec3 sampleProbes(vec3 pos, vec3 dir, float lod, bool errorCorrect)
         {
             continue;
         }
-        float r = refSphere[i].w; // radius of sphere volume
-        float p = float(abs(refIndex[i].w)); // priority
-        
-        float rr = r*r; // radius squred
-        float r1 = r * 0.1; // 90% of radius (outer sphere to start interpolating down)
-        vec3 delta = pos.xyz-refSphere[i].xyz;
-        float d2 = dot(delta,delta);
-        float r2 = r1*r1; 
-        
-        {
-            vec3 vi, wi;
 
-            float atten = 1.0 - max(d2 - r2, 0.0) / (rr - r2);
-            float w;
-            vec3 refcol;
+        float w;
+        vec3 vi, wi;
+        vec3 refcol;
 
+        
+        {
             if (errorCorrect && refIndex[i].w >= 0)
             { // error correction is on and this probe is a sphere
               //take a sample to get depth value, then error correct
-                refcol = tapRefMap(pos, dir, vi, wi, abs(lod + 2), refSphere[i].xyz, rr, i);
+                refcol = tapRefMap(pos, dir, w, vi, wi, abs(lod + 2), refSphere[i].xyz, i);
 
                 //adjust lookup by distance result
                 float d = length(vi - wi);
@@ -425,43 +450,15 @@ vec3 sampleProbes(vec3 pos, vec3 dir, float lod, bool errorCorrect)
             }
             else
             {
-                w = 1.0 / d2;
-                refcol = tapRefMap(pos, dir, vi, wi, lod, refSphere[i].xyz, rr, i);
+                refcol = tapRefMap(pos, dir, w, vi, wi, lod, refSphere[i].xyz, i);
             }
 
-            w *= atten;
-
-            //w *= p; // boost weight based on priority
             col += refcol.rgb*w;
 
             wsum += w;
         }
     }
 
-    if (probeInfluences < 1)
-    { //edge-of-scene probe or no probe influence, mix in with embiggened version of probes closest to camera 
-        for (int idx = 0; idx < 8; ++idx)
-        {
-            if (refIndex[idx].w < 0)
-            { // don't fallback to box probes, they are *very* specific
-                continue;
-            }
-            int i = idx;
-            vec3 delta = pos.xyz-refSphere[i].xyz;
-            float d2 = dot(delta,delta);
-            
-            {
-                vec3 vi, wi;
-                vec3 refcol = tapRefMap(pos, dir, vi, wi, lod, refSphere[i].xyz, d2, i);
-
-                float w = 1.0/d2;
-                w *= w;
-                col += refcol.rgb*w;
-                wsum += w;
-            }
-        }
-    }
-
     if (wsum > 0.0)
     {
         col *= 1.0/wsum;
@@ -487,52 +484,17 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir)
         {
             continue;
         }
-        float r = refSphere[i].w; // radius of sphere volume
-        float p = float(abs(refIndex[i].w)); // priority
-        
-        float rr = r*r; // radius squred
-        float r1 = r * 0.1; // 75% of radius (outer sphere to start interpolating down)
-        vec3 delta = pos.xyz-refSphere[i].xyz;
-        float d2 = dot(delta,delta);
-        float r2 = r1*r1; 
         
         {
-            vec3 refcol = tapIrradianceMap(pos, dir, refSphere[i].xyz, rr, i);
-            
-            float w = 1.0/d2;
+            float w;
+            vec3 refcol = tapIrradianceMap(pos, dir, w, refSphere[i].xyz, i);
 
-            float atten = 1.0-max(d2-r2, 0.0)/(rr-r2);
-            w *= atten;
-            //w *= p; // boost weight based on priority
             col += refcol*w;
             
             wsum += w;
         }
     }
 
-    if (probeInfluences <= 1)
-    { //edge-of-scene probe or no probe influence, mix in with embiggened version of probes closest to camera 
-        for (int idx = 0; idx < 8; ++idx)
-        {
-            if (refIndex[idx].w < 0)
-            { // don't fallback to box probes, they are *very* specific
-                continue;
-            }
-            int i = idx;
-            vec3 delta = pos.xyz-refSphere[i].xyz;
-            float d2 = dot(delta,delta);
-            
-            {
-                vec3 refcol = tapIrradianceMap(pos, dir, refSphere[i].xyz, d2, i);
-                
-                float w = 1.0/d2;
-                w *= w;
-                col += refcol*w;
-                wsum += w;
-            }
-        }
-    }
-
     if (wsum > 0.0)
     {
         col *= 1.0/wsum;
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index 8f1ee8f70bd..19f0a8d0893 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -41,10 +41,13 @@ extern BOOL gTeleportDisplay;
 
 LLReflectionMapManager::LLReflectionMapManager()
 {
-    for (int i = 0; i < LL_MAX_REFLECTION_PROBE_COUNT; ++i)
+    for (int i = 1; i < LL_MAX_REFLECTION_PROBE_COUNT; ++i)
     {
         mCubeFree[i] = true;
     }
+
+    // cube index 0 is reserved for the fallback probe
+    mCubeFree[0] = false;
 }
 
 struct CompareReflectionMapDistance
@@ -102,6 +105,15 @@ void LLReflectionMapManager::update()
     }
 
 
+    if (mDefaultProbe.isNull())
+    {
+        mDefaultProbe = addProbe();
+        mDefaultProbe->mDistance = -4096.f; // hack to make sure the default probe is always first in sort order
+        mDefaultProbe->mRadius = 4096.f;
+    }
+
+    mDefaultProbe->mOrigin.load3(LLViewerCamera::getInstance()->getOrigin().mV);
+    
     LLVector4a camera_pos;
     camera_pos.load3(LLViewerCamera::instance().getOrigin().mV);
 
@@ -174,8 +186,11 @@ void LLReflectionMapManager::update()
             closestDynamic = probe;
         }
 
-        d.setSub(camera_pos, probe->mOrigin);
-        probe->mDistance = d.getLength3().getF32()-probe->mRadius;
+        if (probe != mDefaultProbe)
+        {
+            d.setSub(camera_pos, probe->mOrigin);
+            probe->mDistance = d.getLength3().getF32() - probe->mRadius;
+        }
     }
 
     if (realtime && closestDynamic != nullptr)
@@ -196,7 +211,8 @@ void LLReflectionMapManager::update()
         if (probe->mCubeIndex == -1)
         {
             probe->mCubeArray = mTexture;
-            probe->mCubeIndex = allocateCubeIndex();
+
+            probe->mCubeIndex = probe == mDefaultProbe ? 0 : allocateCubeIndex();
         }
 
         probe->autoAdjustOrigin();
@@ -346,6 +362,8 @@ void LLReflectionMapManager::deleteProbe(U32 i)
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
     LLReflectionMap* probe = mProbes[i];
 
+    llassert(probe != mDefaultProbe);
+
     if (probe->mCubeIndex != -1)
     { // mark the cube index used by this probe as being free
         mCubeFree[probe->mCubeIndex] = true;
@@ -429,7 +447,6 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face)
         {
             LL_PROFILE_GPU_ZONE("probe mip");
             mMipChain[i].bindTarget();
-
             if (i == 0)
             {
                 
@@ -607,6 +624,10 @@ void LLReflectionMapManager::shift(const LLVector4a& offset)
 void LLReflectionMapManager::updateNeighbors(LLReflectionMap* probe)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+    if (mDefaultProbe == probe)
+    {
+        return;
+    }
 
     //remove from existing neighbors
     {
@@ -627,7 +648,7 @@ void LLReflectionMapManager::updateNeighbors(LLReflectionMap* probe)
         LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmun - search");
         for (auto& other : mProbes)
         {
-            if (other != probe)
+            if (other != mDefaultProbe && other != probe)
             {
                 if (probe->intersects(other))
                 {
diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h
index 4dcd8226778..14b762998af 100644
--- a/indra/newview/llreflectionmapmanager.h
+++ b/indra/newview/llreflectionmapmanager.h
@@ -155,6 +155,8 @@ class alignas(16) LLReflectionMapManager
     LLReflectionMap* mUpdatingProbe = nullptr;
     U32 mUpdatingFace = 0;
 
+    LLPointer<LLReflectionMap> mDefaultProbe;  // default reflection probe to fall back to for pixels with no probe influences (should always be at cube index 0)
+
     // number of reflection probes to use for rendering (based on saved setting RenderReflectionProbeCount)
     U32 mReflectionProbeCount;
 };
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 628d0ecc6ac..3167d1161c7 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1235,6 +1235,7 @@ U32 LLViewerRegion::getNumOfVisibleGroups() const
 
 void LLViewerRegion::updateReflectionProbes()
 {
+#if 1
     const F32 probe_spacing = 32.f;
     const F32 probe_radius = sqrtf((probe_spacing * 0.5f) * (probe_spacing * 0.5f) * 3.f);
     const F32 hover_height = 2.f;
@@ -1270,6 +1271,7 @@ void LLViewerRegion::updateReflectionProbes()
             mReflectionMaps[idx]->mRadius = probe_radius;
         }
     }
+#endif
 }
 
 void LLViewerRegion::addToVOCacheTree(LLVOCacheEntry* entry)
-- 
GitLab