diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index 85d6209964afa774fdd761e3d86b136d1d4f0f6c..fa46e0f7d03ed2f185127c4743baa0a91d8c6e9b 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -473,6 +473,8 @@ void LLRenderTarget::release()
 
 void LLRenderTarget::bindTarget()
 {
+    llassert(mFBO);
+
 	if (mFBO)
 	{
 		stop_glerror();
@@ -514,6 +516,7 @@ void LLRenderTarget::bindTarget()
 void LLRenderTarget::clear(U32 mask_in)
 {
     LL_PROFILE_GPU_ZONE("clear");
+    llassert(mFBO);
 	U32 mask = GL_COLOR_BUFFER_BIT;
 	if (mUseDepth)
 	{
@@ -579,6 +582,7 @@ void LLRenderTarget::bindTexture(U32 index, S32 channel, LLTexUnit::eTextureFilt
 void LLRenderTarget::flush(bool fetch_depth)
 {
 	gGL.flush();
+    llassert(mFBO);
 	if (!mFBO)
 	{
 		gGL.getTexUnit(0)->bind(this);
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 327dfe6955502e0faaed095f7ef2f3ac00098a94..35a79f12de7fa53c901aec72adfe6d72a5f1ede2 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10280,6 +10280,17 @@
       <key>Value</key>
       <integer>2</integer>
     </map>
+  <key>RenderReflectionProbeDetail</key>
+  <map>
+    <key>Comment</key>
+    <string>Detail of reflections.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>S32</string>
+    <key>Value</key>
+    <integer>1</integer>
+  </map>
 
   <key>RenderReflectionProbeDrawDistance</key>
   <map>
diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
index 3fd001e7f5ba0abb44c106076d4973a7f1045f38..b3396baebaa8fdfdfc33a71b73959e8268df573c 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
@@ -74,6 +74,8 @@ bool isAbove(vec3 pos, vec4 plane)
     return (dot(plane.xyz, pos) + plane.w) > 0;
 }
 
+int max_priority = 0;
+
 // return true if probe at index i influences position pos
 bool shouldSampleProbe(int i, vec3 pos)
 {
@@ -86,6 +88,8 @@ bool shouldSampleProbe(int i, vec3 pos)
         {
             return false;
         }
+
+        max_priority = max(max_priority, -refIndex[i].w);
     }
     else
     {
@@ -98,6 +102,8 @@ bool shouldSampleProbe(int i, vec3 pos)
         { //outside bounding sphere
             return false;
         }
+
+        max_priority = max(max_priority, refIndex[i].w);
     }
 
     return true;
@@ -343,8 +349,13 @@ vec3 sampleProbes(vec3 pos, vec3 dir, float lod, float minweight)
     for (int idx = 0; idx < probeInfluences; ++idx)
     {
         int i = probeIndex[idx];
+        if (refIndex[i].w < max_priority)
+        {
+            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;
@@ -358,7 +369,7 @@ vec3 sampleProbes(vec3 pos, vec3 dir, float lod, float minweight)
 
             float atten = 1.0-max(d2-r2, 0.0)/(rr-r2);
             w *= atten;
-            w *= p; // boost weight based on priority
+            //w *= p; // boost weight based on priority
             col += refcol*w*max(minweight, refParams[i].x);
             
             wsum += w;
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
index ddce22fa20aab9f31241d491cbc56b2eeab00542..956539cd982c8e360ea06fcd2f9cd55eb66b705d 100644
--- a/indra/newview/llpanelvolume.cpp
+++ b/indra/newview/llpanelvolume.cpp
@@ -152,8 +152,6 @@ BOOL	LLPanelVolume::postBuild()
         childSetCommitCallback("Probe Volume Type", onCommitProbe, this);
         childSetCommitCallback("Probe Ambiance", onCommitProbe, this);
         childSetCommitCallback("Probe Near Clip", onCommitProbe, this);
-
-
     }
 
 	// PHYSICS Parameters
@@ -695,7 +693,7 @@ void LLPanelVolume::clearCtrls()
 	getChildView("Light Radius")->setEnabled(false);
 	getChildView("Light Falloff")->setEnabled(false);
 
-    getChildView("Reflection Probe Checkbox Ctrl")->setEnabled(false);;
+    getChildView("Reflection Probe")->setEnabled(false);;
     getChildView("Probe Volume Type")->setEnabled(false);
     getChildView("Probe Dynamic")->setEnabled(false);
     getChildView("Probe Ambiance")->setEnabled(false);
@@ -746,7 +744,7 @@ void LLPanelVolume::sendIsReflectionProbe()
     }
     LLVOVolume* volobjp = (LLVOVolume*)objectp;
 
-    BOOL value = getChild<LLUICtrl>("Reflection Probe Checkbox Ctrl")->getValue();
+    BOOL value = getChild<LLUICtrl>("Reflection Probe")->getValue();
     volobjp->setIsReflectionProbe(value);
     LL_INFOS() << "update reflection probe sent" << LL_ENDL;
 }
diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp
index 39e0841fc5db573006eed9b7027a84fb4146ebb7..500485fc70f48e97ad28275017de1a2649e66802 100644
--- a/indra/newview/llreflectionmap.cpp
+++ b/indra/newview/llreflectionmap.cpp
@@ -54,28 +54,6 @@ void LLReflectionMap::update(U32 resolution, U32 face)
     gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, getNearClip(), getIsDynamic());
 }
 
-bool LLReflectionMap::shouldUpdate()
-{
-    const F32 TIMEOUT_INTERVAL = 30.f; // update no less than this often
-    const F32 RENDER_TIMEOUT = 1.f; // don't update if hasn't been used for rendering for this long
-    
-    if (mLastBindTime > gFrameTimeSeconds - RENDER_TIMEOUT)
-    {   
-        if (mLastUpdateTime < gFrameTimeSeconds - TIMEOUT_INTERVAL)
-        {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-void LLReflectionMap::dirty()
-{
-    mDirty = true;
-    mLastUpdateTime = gFrameTimeSeconds;
-}
-
 void LLReflectionMap::autoAdjustOrigin()
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
@@ -245,7 +223,9 @@ F32 LLReflectionMap::getNearClip()
 
 bool LLReflectionMap::getIsDynamic()
 {
-    if (mViewerObject && mViewerObject->getVolume())
+    if (gSavedSettings.getS32("RenderReflectionProbeDetail") > (S32) LLReflectionMapManager::DetailLevel::STATIC_ONLY &&
+        mViewerObject && 
+        mViewerObject->getVolume())
     {
         return ((LLVOVolume*)mViewerObject)->getReflectionProbeIsDynamic();
     }
diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h
index 071568e53c029e796294d9eceea45a1110721d5a..cf0bc2ff27a7d57eefd2e3b03f0b7de2fbebdd96 100644
--- a/indra/newview/llreflectionmap.h
+++ b/indra/newview/llreflectionmap.h
@@ -43,12 +43,6 @@ class alignas(16) LLReflectionMap : public LLRefCount
     // resolution - size of cube map to generate
     void update(U32 resolution, U32 face);
 
-    // return true if this probe should update *now*
-    bool shouldUpdate();
-
-    // Mark this reflection map as needing an update (resets last update time, so spamming this call will cause a cube map to never update)
-    void dirty();
-
     // for volume partition probes, try to place this probe in the best spot
     void autoAdjustOrigin();
 
@@ -104,7 +98,5 @@ class alignas(16) LLReflectionMap : public LLRefCount
 
     // what priority should this probe have (higher is higher priority)
     U32 mPriority = 1;
-
-    bool mDirty = true;
 };
 
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index 60396b6c605ef5d9f6ac002f919a639090ac25db..dc733687c32388861c473e6d32f1d86ea5085acf 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -96,11 +96,13 @@ void LLReflectionMapManager::update()
         mRenderTarget.allocate(targetRes, targetRes, color_fmt, use_depth_buffer, use_stencil_buffer, LLTexUnit::TT_RECT_TEXTURE);
 
         // hack to allocate render targets using gPipeline code
+        gCubeSnapshot = TRUE;
         auto* old_rt = gPipeline.mRT;
         gPipeline.mRT = &gProbeRT;
         gPipeline.allocateScreenBuffer(targetRes, targetRes);
         gPipeline.allocateShadowBuffer(targetRes, targetRes);
         gPipeline.mRT = old_rt;
+        gCubeSnapshot = FALSE;
     }
 
     if (mMipChain.empty())
@@ -154,6 +156,10 @@ void LLReflectionMapManager::update()
 
     bool did_update = false;
 
+    bool realtime = gSavedSettings.getS32("RenderReflectionProbeDetail") >= (S32)LLReflectionMapManager::DetailLevel::REALTIME;
+    
+    LLReflectionMap* closestDynamic = nullptr;
+
     LLReflectionMap* oldestProbe = nullptr;
 
     if (mUpdatingProbe != nullptr)
@@ -183,11 +189,30 @@ void LLReflectionMapManager::update()
             oldestProbe = probe;
         }
 
+        if (realtime && 
+            closestDynamic == nullptr && 
+            probe->mCubeArray.notNull() &&
+            probe->getIsDynamic())
+        {
+            closestDynamic = probe;
+        }
+
         d.setSub(camera_pos, probe->mOrigin);
         probe->mDistance = d.getLength3().getF32()-probe->mRadius;
     }
 
-#if 1
+    if (realtime && closestDynamic != nullptr)
+    {
+        LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmu - realtime");
+        // update the closest dynamic probe realtime
+        closestDynamic->autoAdjustOrigin();
+        for (U32 i = 0; i < 6; ++i)
+        {
+            updateProbeFace(closestDynamic, i);
+        }
+    }
+
+    // switch to updating the next oldest probe
     if (!did_update && oldestProbe != nullptr)
     {
         LLReflectionMap* probe = oldestProbe;
@@ -201,9 +226,7 @@ void LLReflectionMapManager::update()
 
         mUpdatingProbe = probe;
         doProbeUpdate();
-        probe->mDirty = false;
     }
-#endif
 
     // update distance to camera for all probes
     std::sort(mProbes.begin(), mProbes.end(), CompareProbeDistance());
@@ -214,7 +237,6 @@ LLReflectionMap* LLReflectionMapManager::addProbe(LLSpatialGroup* group)
     LLReflectionMap* probe = new LLReflectionMap();
     probe->mGroup = group;
     probe->mOrigin = group->getOctreeNode()->getCenter();
-    probe->mDirty = true;
 
     if (gCubeSnapshot)
     { //snapshot is in progress, mProbes is being iterated over, defer insertion until next update
@@ -295,7 +317,6 @@ LLReflectionMap* LLReflectionMapManager::registerViewerObject(LLViewerObject* vo
     LLReflectionMap* probe = new LLReflectionMap();
     probe->mViewerObject = vobj;
     probe->mOrigin.load3(vobj->getPositionAgent().mV);
-    probe->mDirty = true;
 
     if (gCubeSnapshot)
     { //snapshot is in progress, mProbes is being iterated over, defer insertion until next update
@@ -368,10 +389,23 @@ void LLReflectionMapManager::doProbeUpdate()
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
     llassert(mUpdatingProbe != nullptr);
 
+    updateProbeFace(mUpdatingProbe, mUpdatingFace);
+    
+    if (++mUpdatingFace == 6)
+    {
+        updateNeighbors(mUpdatingProbe);
+        mUpdatingProbe = nullptr;
+        mUpdatingFace = 0;
+    }
+}
+
+void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face)
+{
     mRenderTarget.bindTarget();
+    // hacky hot-swap of camera specific render targets
     auto* old_rt = gPipeline.mRT;
     gPipeline.mRT = &gProbeRT;
-    mUpdatingProbe->update(mRenderTarget.getWidth(), mUpdatingFace);
+    probe->update(mRenderTarget.getWidth(), face);
     gPipeline.mRT = old_rt;
     mRenderTarget.flush();
 
@@ -390,9 +424,9 @@ void LLReflectionMapManager::doProbeUpdate()
         gGL.loadIdentity();
 
         gGL.flush();
-        U32 res = LL_REFLECTION_PROBE_RESOLUTION*2;
+        U32 res = LL_REFLECTION_PROBE_RESOLUTION * 2;
 
-        S32 mips = log2((F32) LL_REFLECTION_PROBE_RESOLUTION)+0.5f;
+        S32 mips = log2((F32)LL_REFLECTION_PROBE_RESOLUTION) + 0.5f;
 
         for (int i = 0; i < mMipChain.size(); ++i)
         {
@@ -409,10 +443,10 @@ void LLReflectionMapManager::doProbeUpdate()
             }
 
             gGL.begin(gGL.QUADS);
-            
+
             gGL.texCoord2f(0, 0);
             gGL.vertex2f(-1, -1);
-            
+
             gGL.texCoord2f(res, 0);
             gGL.vertex2f(1, -1);
 
@@ -431,7 +465,7 @@ void LLReflectionMapManager::doProbeUpdate()
             if (mip >= 0)
             {
                 mTexture->bind(0);
-                glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, mUpdatingProbe->mCubeIndex * 6 + mUpdatingFace, 0, 0, res, res);
+                glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, probe->mCubeIndex * 6 + face, 0, 0, res, res);
                 mTexture->unbind();
             }
             mMipChain[i].flush();
@@ -443,13 +477,6 @@ void LLReflectionMapManager::doProbeUpdate()
 
         gReflectionMipProgram.unbind();
     }
-    
-    if (++mUpdatingFace == 6)
-    {
-        updateNeighbors(mUpdatingProbe);
-        mUpdatingProbe = nullptr;
-        mUpdatingFace = 0;
-    }
 }
 
 void LLReflectionMapManager::rebuild()
diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h
index bf963f34866e33c26ded260916661b0ba3f17d66..3b5cdc5520bcd2d8ebd076ffb707b47a65edd714 100644
--- a/indra/newview/llreflectionmapmanager.h
+++ b/indra/newview/llreflectionmapmanager.h
@@ -46,6 +46,13 @@ class alignas(16) LLReflectionMapManager
 {
     LL_ALIGN_NEW
 public:
+    enum class DetailLevel 
+    {
+        STATIC_ONLY = 0,
+        STATIC_AND_DYNAMIC,
+        REALTIME = 2
+    };
+
     // allocate an environment map of the given resolution 
     LLReflectionMapManager();
 
@@ -115,6 +122,9 @@ class alignas(16) LLReflectionMapManager
     
     // perform an update on the currently updating Probe
     void doProbeUpdate();
+
+    // update the specified face of the specified probe
+    void updateProbeFace(LLReflectionMap* probe, U32 face);
     
     // list of active reflection maps
     std::vector<LLPointer<LLReflectionMap> > mProbes;
@@ -133,6 +143,5 @@ class alignas(16) LLReflectionMapManager
 
     LLReflectionMap* mUpdatingProbe = nullptr;
     U32 mUpdatingFace = 0;
-
 };
 
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index a9e807e0f602b0c744e63316934467a8b7687ecb..f445bc98eb9c5cc0439ef67ae5d282a847f58ece 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -738,17 +738,8 @@ BOOL LLSpatialGroup::changeLOD()
 	return FALSE;
 }
 
-void LLSpatialGroup::dirtyReflectionProbe()
-{
-    if (mReflectionProbe != nullptr)
-    {
-        mReflectionProbe->dirty();
-    }
-}
-
 void LLSpatialGroup::handleInsertion(const TreeNode* node, LLViewerOctreeEntry* entry)
 {
-    dirtyReflectionProbe();
 	addObject((LLDrawable*)entry->getDrawable());
 	unbound();
 	setState(OBJECT_DIRTY);
@@ -756,7 +747,6 @@ void LLSpatialGroup::handleInsertion(const TreeNode* node, LLViewerOctreeEntry*
 
 void LLSpatialGroup::handleRemoval(const TreeNode* node, LLViewerOctreeEntry* entry)
 {
-    dirtyReflectionProbe();
 	removeObject((LLDrawable*)entry->getDrawable(), TRUE);
 	LLViewerOctreeGroup::handleRemoval(node, entry);
 }
@@ -793,8 +783,6 @@ void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* c
 {
 	LL_PROFILE_ZONE_SCOPED_CATEGORY_SPATIAL
 
-    dirtyReflectionProbe();
-
 	if (child->getListenerCount() == 0)
 	{
 		new LLSpatialGroup(child, getSpatialPartition());
@@ -809,11 +797,6 @@ void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* c
 	assert_states_valid(this);
 }
 
-void LLSpatialGroup::handleChildRemoval(const oct_node* parent, const oct_node* child)
-{
-    dirtyReflectionProbe();
-}
-
 void LLSpatialGroup::destroyGL(bool keep_occlusion) 
 {
 	setState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::IMAGE_DIRTY);
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index bebd8aec85b5b649dd35e5e0db7ed4cf8bc29044..07d62be7afc8bb5b38c937c23762b7b1a9622269 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -333,7 +333,6 @@ class LLSpatialGroup : public LLOcclusionCullingGroup
 	virtual void handleRemoval(const TreeNode* node, LLViewerOctreeEntry* face);
 	virtual void handleDestruction(const TreeNode* node);
 	virtual void handleChildAddition(const OctreeNode* parent, OctreeNode* child);
-    virtual void handleChildRemoval(const oct_node* parent, const oct_node* child);
 
 public:
 	LL_ALIGN_16(LLVector4a mViewAngle);
@@ -341,8 +340,6 @@ class LLSpatialGroup : public LLOcclusionCullingGroup
 
 	F32 mObjectBoxSize; //cached mObjectBounds[1].getLength3()
 
-    void dirtyReflectionProbe();
-
 protected:
 	virtual ~LLSpatialGroup();
 
diff --git a/indra/newview/llviewercamera.h b/indra/newview/llviewercamera.h
index 549778a841241c72bdb0c30987595f1d1259bbd3..b5841772ed87103042cf0782e5557c4d36440a5d 100644
--- a/indra/newview/llviewercamera.h
+++ b/indra/newview/llviewercamera.h
@@ -47,15 +47,14 @@ class alignas(16) LLViewerCamera : public LLCamera, public LLSimpleton<LLViewerC
 	typedef enum
 	{
 		CAMERA_WORLD = 0,
-		CAMERA_SHADOW0,
-		CAMERA_SHADOW1,
-		CAMERA_SHADOW2,
-		CAMERA_SHADOW3,
-		CAMERA_SHADOW4,
-		CAMERA_SHADOW5,
+		CAMERA_SUN_SHADOW0,
+		CAMERA_SUN_SHADOW1,
+		CAMERA_SUN_SHADOW2,
+		CAMERA_SUN_SHADOW3,
+		CAMERA_SPOT_SHADOW0,
+		CAMERA_SPOT_SHADOW1,
 		CAMERA_WATER0,
 		CAMERA_WATER1,
-		CAMERA_GI_SOURCE,
 		NUM_CAMERAS
 	} eCameraID;
 
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 28dc3781baa112d9225ec544142a8188db7e3f90..8bac5131cf2b70eb7251808a04b59010ec73b9a2 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -739,7 +739,8 @@ void LLPipeline::requestResizeShadowTexture()
 
 void LLPipeline::resizeShadowTexture()
 {
-    releaseShadowTargets();
+    releaseSunShadowTargets();
+    releaseSpotShadowTargets();
     allocateShadowBuffer(mRT->width, mRT->height);
     gResizeShadowTexture = FALSE;
 }
@@ -754,7 +755,8 @@ void LLPipeline::resizeScreenTexture()
 		if (gResizeScreenTexture || (resX != mRT->screen.getWidth()) || (resY != mRT->screen.getHeight()))
 		{
 			releaseScreenBuffers();
-            releaseShadowTargets();
+            releaseSunShadowTargets();
+            releaseSpotShadowTargets();
 		    allocateScreenBuffer(resX,resY);
             gResizeScreenTexture = FALSE;
 		}
@@ -913,7 +915,8 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
     {
         mRT->deferredLight.release();
 
-        releaseShadowTargets();
+        releaseSunShadowTargets();
+        releaseSpotShadowTargets();
 
 		mRT->fxaaBuffer.release();
 		mRT->screen.release();
@@ -942,66 +945,66 @@ inline U32 BlurHappySize(U32 x, F32 scale) { return U32( x * scale + 16.0f) & ~0
 bool LLPipeline::allocateShadowBuffer(U32 resX, U32 resY)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
-	if (LLPipeline::sRenderDeferred)
-	{
-		S32 shadow_detail = RenderShadowDetail;
+    if (LLPipeline::sRenderDeferred)
+    {
+        S32 shadow_detail = RenderShadowDetail;
 
-		const U32 occlusion_divisor = 3;
+        const U32 occlusion_divisor = 3;
 
-		F32 scale = llmax(0.f,RenderShadowResolutionScale);
-		U32 sun_shadow_map_width  = BlurHappySize(resX, scale);
-		U32 sun_shadow_map_height = BlurHappySize(resY, scale);
+        F32 scale = llmax(0.f, RenderShadowResolutionScale);
+        U32 sun_shadow_map_width = BlurHappySize(resX, scale);
+        U32 sun_shadow_map_height = BlurHappySize(resY, scale);
 
-		if (shadow_detail > 0)
-		{ //allocate 4 sun shadow maps
-			for (U32 i = 0; i < 4; i++)
-			{
-				if (!mRT->shadow[i].allocate(sun_shadow_map_width, sun_shadow_map_height, 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE))
+        if (shadow_detail > 0)
+        { //allocate 4 sun shadow maps
+            for (U32 i = 0; i < 4; i++)
+            {
+                if (!mRT->shadow[i].allocate(sun_shadow_map_width, sun_shadow_map_height, 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE))
                 {
                     return false;
                 }
 
-                if (!mRT->shadowOcclusion[i].allocate(sun_shadow_map_width/occlusion_divisor, sun_shadow_map_height/occlusion_divisor, 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE))
+                if (!mRT->shadowOcclusion[i].allocate(sun_shadow_map_width / occlusion_divisor, sun_shadow_map_height / occlusion_divisor, 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE))
                 {
                     return false;
                 }
-			}
-		}
-		else
-		{
-			for (U32 i = 0; i < 4; i++)
-			{
-                releaseShadowTarget(i);
-			}
-		}
+            }
+        }
+        else
+        {
+            for (U32 i = 0; i < 4; i++)
+            {
+                releaseSunShadowTarget(i);
+            }
+        }
 
-		U32 width = (U32) (resX*scale);
-		U32 height = width;
+        if (!gCubeSnapshot) // hack to not allocate spot shadow maps during ReflectionMapManager init
+        {
+            U32 width = (U32)(resX * scale);
+            U32 height = width;
 
-		if (shadow_detail > 1)
-		{ //allocate two spot shadow maps
-			U32 spot_shadow_map_width = width;
-            U32 spot_shadow_map_height = height;
-			for (U32 i = 4; i < 6; i++)
-			{
-                if (!mRT->shadow[i].allocate(spot_shadow_map_width, spot_shadow_map_height, 0, TRUE, FALSE))
-		{
-                    return false;
-			}
-                if (!mRT->shadowOcclusion[i].allocate(spot_shadow_map_width/occlusion_divisor, height/occlusion_divisor, 0, TRUE, FALSE))
-		{
-			return false;
-		}
-	}
+            if (shadow_detail > 1)
+            { //allocate two spot shadow maps
+                U32 spot_shadow_map_width = width;
+                U32 spot_shadow_map_height = height;
+                for (U32 i = 0; i < 2; i++)
+                {
+                    if (!mSpotShadow[i].allocate(spot_shadow_map_width, spot_shadow_map_height, 0, TRUE, FALSE))
+                    {
+                        return false;
+                    }
+                    if (!mSpotShadowOcclusion[i].allocate(spot_shadow_map_width / occlusion_divisor, height / occlusion_divisor, 0, TRUE, FALSE))
+                    {
+                        return false;
+                    }
+                }
+            }
+            else
+            {
+                releaseSpotShadowTargets();
+            }
         }
-	else
-	{
-            for (U32 i = 4; i < 6; i++)
-		{
-                releaseShadowTarget(i);
-		}
-	}
-	}
+    }
 
 	return true;
 }
@@ -1175,7 +1178,8 @@ void LLPipeline::releaseLUTBuffers()
 
 void LLPipeline::releaseShadowBuffers()
 {
-    releaseShadowTargets();
+    releaseSunShadowTargets();
+    releaseSpotShadowTargets();
 }
 
 void LLPipeline::releaseScreenBuffers()
@@ -1191,20 +1195,33 @@ void LLPipeline::releaseScreenBuffers()
 }
 		
 		
-void LLPipeline::releaseShadowTarget(U32 index)
+void LLPipeline::releaseSunShadowTarget(U32 index)
 {
+    llassert(index < 4);
     mRT->shadow[index].release();
     mRT->shadowOcclusion[index].release();
 }
 
-void LLPipeline::releaseShadowTargets()
+void LLPipeline::releaseSunShadowTargets()
 {
-	for (U32 i = 0; i < 6; i++)
+	for (U32 i = 0; i < 4; i++)
 	{
-        releaseShadowTarget(i);
+        releaseSunShadowTarget(i);
 	}
 }
 
+void LLPipeline::releaseSpotShadowTargets()
+{
+    if (!gCubeSnapshot) // hack to avoid freeing spot shadows during ReflectionMapManager init
+    {
+        for (U32 i = 0; i < 2; i++)
+        {
+            mSpotShadow[i].release();
+            mSpotShadowOcclusion[i].release();
+        }
+    }
+}
+
 void LLPipeline::createGLBuffers()
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
@@ -8162,7 +8179,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
 
 	for (U32 i = 0; i < 4; i++)
 	{
-        LLRenderTarget* shadow_target = getShadowTarget(i);
+        LLRenderTarget* shadow_target = getSunShadowTarget(i);
         if (shadow_target)
         {
 		channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_TEXTURE);
@@ -8170,7 +8187,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
 		if (channel > -1)
 		{
 			stop_glerror();
-                gGL.getTexUnit(channel)->bind(getShadowTarget(i), TRUE);
+                gGL.getTexUnit(channel)->bind(getSunShadowTarget(i), TRUE);
                 gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
 			gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
 			stop_glerror();
@@ -8182,27 +8199,27 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
 	}
     }
 
-	for (U32 i = 4; i < 6; i++)
-	{
-		channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i);
-		stop_glerror();
-		if (channel > -1)
-		{
-			stop_glerror();
-			LLRenderTarget* shadow_target = getShadowTarget(i);
-			if (shadow_target)
-			{
-				gGL.getTexUnit(channel)->bind(shadow_target, TRUE);
-				gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
-			gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
-			stop_glerror();
-			
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
-			stop_glerror();
-		}
-	}
-	}
+    for (U32 i = 4; i < 6; i++)
+    {
+        channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0 + i);
+        stop_glerror();
+        if (channel > -1)
+        {
+            stop_glerror();
+            LLRenderTarget* shadow_target = getSpotShadowTarget(i-4);
+            if (shadow_target)
+            {
+                gGL.getTexUnit(channel)->bind(shadow_target, TRUE);
+                gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+                gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+                stop_glerror();
+
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
+                stop_glerror();
+            }
+        }
+    }
 
 	stop_glerror();
 
@@ -8313,7 +8330,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
 	shader.uniform3fv(LLShaderMgr::DEFERRED_SUN_DIR, 1, mTransformedSunDir.mV);
     shader.uniform3fv(LLShaderMgr::DEFERRED_MOON_DIR, 1, mTransformedMoonDir.mV);
 	shader.uniform2f(LLShaderMgr::DEFERRED_SHADOW_RES, mRT->shadow[0].getWidth(), mRT->shadow[0].getHeight());
-	shader.uniform2f(LLShaderMgr::DEFERRED_PROJ_SHADOW_RES, mRT->shadow[4].getWidth(), mRT->shadow[4].getHeight());
+	shader.uniform2f(LLShaderMgr::DEFERRED_PROJ_SHADOW_RES, mSpotShadow[0].getWidth(), mSpotShadow[0].getHeight());
 	shader.uniform1f(LLShaderMgr::DEFERRED_DEPTH_CUTOFF, RenderEdgeDepthCutoff);
 	shader.uniform1f(LLShaderMgr::DEFERRED_NORM_CUTOFF, RenderEdgeNormCutoff);
 	
@@ -9077,7 +9094,7 @@ void LLPipeline::setupSpotLight(LLGLSLShader& shader, LLDrawable* drawablep)
 		shader.uniform1f(LLShaderMgr::PROJECTOR_SHADOW_FADE, 1.f);
 	}
 
-    if (!gCubeSnapshot)
+    //if (!gCubeSnapshot)
 	{
 		LLDrawable* potential = drawablep;
 		//determine if this is a good light for casting shadows
@@ -9668,7 +9685,9 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
         gDeferredShadowCubeProgram.bind();
     }
 
-    LLRenderTarget& occlusion_target = mRT->shadowOcclusion[LLViewerCamera::sCurCameraID - 1];
+    LLRenderTarget& occlusion_target = LLViewerCamera::sCurCameraID >= LLViewerCamera::CAMERA_SPOT_SHADOW0 ?
+        mSpotShadowOcclusion[LLViewerCamera::sCurCameraID - LLViewerCamera::CAMERA_SPOT_SHADOW0] :
+        mRT->shadowOcclusion[LLViewerCamera::sCurCameraID - LLViewerCamera::CAMERA_SUN_SHADOW0];
 
     occlusion_target.bindTarget();
     updateCull(shadow_cam, result);
@@ -9812,7 +9831,9 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
     gGLLastMatrix = NULL;
     gGL.loadMatrix(gGLModelView);
 
-    LLRenderTarget& occlusion_source = mRT->shadow[LLViewerCamera::sCurCameraID - 1];
+    LLRenderTarget& occlusion_source = LLViewerCamera::sCurCameraID >= LLViewerCamera::CAMERA_SPOT_SHADOW0 ?
+        mSpotShadow[LLViewerCamera::sCurCameraID - LLViewerCamera::CAMERA_SPOT_SHADOW0] :
+        mRT->shadow[LLViewerCamera::sCurCameraID - LLViewerCamera::CAMERA_SUN_SHADOW0];
 
     if (occlude > 1)
     {
@@ -10087,11 +10108,18 @@ void LLPipeline::generateHighlight(LLCamera& camera)
 	}
 }
 
-LLRenderTarget* LLPipeline::getShadowTarget(U32 i)
+LLRenderTarget* LLPipeline::getSunShadowTarget(U32 i)
 {
+    llassert(i < 4);
     return &mRT->shadow[i];
 }
 
+LLRenderTarget* LLPipeline::getSpotShadowTarget(U32 i)
+{
+    llassert(i < 2);
+    return &mSpotShadow[i];
+}
+
 static LLTrace::BlockTimerStatHandle FTM_GEN_SUN_SHADOW("Gen Sun Shadow");
 static LLTrace::BlockTimerStatHandle FTM_GEN_SUN_SHADOW_SPOT_RENDER("Spot Shadow Render");
 
@@ -10371,7 +10399,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 				mShadowFrustPoints[j].clear();
 			}
 
-			LLViewerCamera::sCurCameraID = (LLViewerCamera::eCameraID)(LLViewerCamera::CAMERA_SHADOW0+j);
+			LLViewerCamera::sCurCameraID = (LLViewerCamera::eCameraID)(LLViewerCamera::CAMERA_SUN_SHADOW0+j);
 
 			//restore render matrices
 			set_current_modelview(saved_view);
@@ -10745,116 +10773,119 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 	{
         if (!gCubeSnapshot) //skip updating spot shadow maps during cubemap updates
         {
-		    LLTrace::CountStatHandle<>* velocity_stat = LLViewerCamera::getVelocityStat();
-		    F32 fade_amt = gFrameIntervalSeconds.value() 
-			    * llmax(LLTrace::get_frame_recording().getLastRecording().getSum(*velocity_stat) / LLTrace::get_frame_recording().getLastRecording().getDuration().value(), 1.0);
-
-		    //update shadow targets
-		    for (U32 i = 0; i < 2; i++)
-		    { //for each current shadow
-			    LLViewerCamera::sCurCameraID = (LLViewerCamera::eCameraID)(LLViewerCamera::CAMERA_SHADOW4+i);
-
-			    if (mShadowSpotLight[i].notNull() && 
-				    (mShadowSpotLight[i] == mTargetShadowSpotLight[0] ||
-				    mShadowSpotLight[i] == mTargetShadowSpotLight[1]))
-			    { //keep this spotlight
-				    mSpotLightFade[i] = llmin(mSpotLightFade[i]+fade_amt, 1.f);
-			    }
-			    else
-			    { //fade out this light
-				    mSpotLightFade[i] = llmax(mSpotLightFade[i]-fade_amt, 0.f);
-				
-				    if (mSpotLightFade[i] == 0.f || mShadowSpotLight[i].isNull())
-				    { //faded out, grab one of the pending spots (whichever one isn't already taken)
-					    if (mTargetShadowSpotLight[0] != mShadowSpotLight[(i+1)%2])
-					    {
-						    mShadowSpotLight[i] = mTargetShadowSpotLight[0];
-					    }
-					    else
-					    {
-						    mShadowSpotLight[i] = mTargetShadowSpotLight[1];
-					    }
-				    }
-			    }
-		    }
-
-            for (S32 i = 0; i < 2; i++)
-            {
-                set_current_modelview(saved_view);
-                set_current_projection(saved_proj);
+            LLTrace::CountStatHandle<>* velocity_stat = LLViewerCamera::getVelocityStat();
+            F32 fade_amt = gFrameIntervalSeconds.value()
+                * llmax(LLTrace::get_frame_recording().getLastRecording().getSum(*velocity_stat) / LLTrace::get_frame_recording().getLastRecording().getDuration().value(), 1.0);
+
+            //update shadow targets
+            for (U32 i = 0; i < 2; i++)
+            { //for each current shadow
+                LLViewerCamera::sCurCameraID = (LLViewerCamera::eCameraID)(LLViewerCamera::CAMERA_SPOT_SHADOW0 + i);
+
+                if (mShadowSpotLight[i].notNull() &&
+                    (mShadowSpotLight[i] == mTargetShadowSpotLight[0] ||
+                        mShadowSpotLight[i] == mTargetShadowSpotLight[1]))
+                { //keep this spotlight
+                    mSpotLightFade[i] = llmin(mSpotLightFade[i] + fade_amt, 1.f);
+                }
+                else
+                { //fade out this light
+                    mSpotLightFade[i] = llmax(mSpotLightFade[i] - fade_amt, 0.f);
 
-                if (mShadowSpotLight[i].isNull())
-                {
-                    continue;
+                    if (mSpotLightFade[i] == 0.f || mShadowSpotLight[i].isNull())
+                    { //faded out, grab one of the pending spots (whichever one isn't already taken)
+                        if (mTargetShadowSpotLight[0] != mShadowSpotLight[(i + 1) % 2])
+                        {
+                            mShadowSpotLight[i] = mTargetShadowSpotLight[0];
+                        }
+                        else
+                        {
+                            mShadowSpotLight[i] = mTargetShadowSpotLight[1];
+                        }
+                    }
                 }
+            }
+        }
 
-                LLVOVolume* volume = mShadowSpotLight[i]->getVOVolume();
+        for (S32 i = 0; i < 2; i++)
+        {
+            set_current_modelview(saved_view);
+            set_current_projection(saved_proj);
 
-                if (!volume)
-                {
-                    mShadowSpotLight[i] = NULL;
-                    continue;
-                }
+            if (mShadowSpotLight[i].isNull())
+            {
+                continue;
+            }
 
-                LLDrawable* drawable = mShadowSpotLight[i];
+            LLVOVolume* volume = mShadowSpotLight[i]->getVOVolume();
 
-                LLVector3 params = volume->getSpotLightParams();
-                F32 fov = params.mV[0];
+            if (!volume)
+            {
+                mShadowSpotLight[i] = NULL;
+                continue;
+            }
 
-                //get agent->light space matrix (modelview)
-                LLVector3 center = drawable->getPositionAgent();
-                LLQuaternion quat = volume->getRenderRotation();
+            LLDrawable* drawable = mShadowSpotLight[i];
 
-                //get near clip plane
-                LLVector3 scale = volume->getScale();
-                LLVector3 at_axis(0, 0, -scale.mV[2] * 0.5f);
-                at_axis *= quat;
+            LLVector3 params = volume->getSpotLightParams();
+            F32 fov = params.mV[0];
 
-                LLVector3 np = center + at_axis;
-                at_axis.normVec();
+            //get agent->light space matrix (modelview)
+            LLVector3 center = drawable->getPositionAgent();
+            LLQuaternion quat = volume->getRenderRotation();
 
-                //get origin that has given fov for plane np, at_axis, and given scale
-                F32 dist = (scale.mV[1] * 0.5f) / tanf(fov * 0.5f);
+            //get near clip plane
+            LLVector3 scale = volume->getScale();
+            LLVector3 at_axis(0, 0, -scale.mV[2] * 0.5f);
+            at_axis *= quat;
 
-                LLVector3 origin = np - at_axis * dist;
+            LLVector3 np = center + at_axis;
+            at_axis.normVec();
 
-                LLMatrix4 mat(quat, LLVector4(origin, 1.f));
+            //get origin that has given fov for plane np, at_axis, and given scale
+            F32 dist = (scale.mV[1] * 0.5f) / tanf(fov * 0.5f);
 
-                view[i + 4] = glh::matrix4f((F32*)mat.mMatrix);
+            LLVector3 origin = np - at_axis * dist;
 
-                view[i + 4] = view[i + 4].inverse();
+            LLMatrix4 mat(quat, LLVector4(origin, 1.f));
 
-                //get perspective matrix
-                F32 near_clip = dist + 0.01f;
-                F32 width = scale.mV[VX];
-                F32 height = scale.mV[VY];
-                F32 far_clip = dist + volume->getLightRadius() * 1.5f;
+            view[i + 4] = glh::matrix4f((F32*)mat.mMatrix);
 
-                F32 fovy = fov * RAD_TO_DEG;
-                F32 aspect = width / height;
+            view[i + 4] = view[i + 4].inverse();
 
-                proj[i + 4] = gl_perspective(fovy, aspect, near_clip, far_clip);
+            //get perspective matrix
+            F32 near_clip = dist + 0.01f;
+            F32 width = scale.mV[VX];
+            F32 height = scale.mV[VY];
+            F32 far_clip = dist + volume->getLightRadius() * 1.5f;
 
-                //translate and scale to from [-1, 1] to [0, 1]
-                glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f,
-                    0.f, 0.5f, 0.f, 0.5f,
-                    0.f, 0.f, 0.5f, 0.5f,
-                    0.f, 0.f, 0.f, 1.f);
+            F32 fovy = fov * RAD_TO_DEG;
+            F32 aspect = width / height;
 
-                set_current_modelview(view[i + 4]);
-                set_current_projection(proj[i + 4]);
+            proj[i + 4] = gl_perspective(fovy, aspect, near_clip, far_clip);
 
-                mSunShadowMatrix[i + 4] = trans * proj[i + 4] * view[i + 4] * inv_view;
+            //translate and scale to from [-1, 1] to [0, 1]
+            glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f,
+                0.f, 0.5f, 0.f, 0.5f,
+                0.f, 0.f, 0.5f, 0.5f,
+                0.f, 0.f, 0.f, 1.f);
 
-                for (U32 j = 0; j < 16; j++)
-                {
-                    gGLLastModelView[j] = mShadowModelview[i + 4].m[j];
-                    gGLLastProjection[j] = mShadowProjection[i + 4].m[j];
-                }
+            set_current_modelview(view[i + 4]);
+            set_current_projection(proj[i + 4]);
 
-                mShadowModelview[i + 4] = view[i + 4];
-                mShadowProjection[i + 4] = proj[i + 4];
+            mSunShadowMatrix[i + 4] = trans * proj[i + 4] * view[i + 4] * inv_view;
 
+            for (U32 j = 0; j < 16; j++)
+            {
+                gGLLastModelView[j] = mShadowModelview[i + 4].m[j];
+                gGLLastProjection[j] = mShadowProjection[i + 4].m[j];
+            }
+
+            mShadowModelview[i + 4] = view[i + 4];
+            mShadowProjection[i + 4] = proj[i + 4];
+
+            if (!gCubeSnapshot) //skip updating spot shadow maps during cubemap updates
+            {
                 LLCamera shadow_cam = camera;
                 shadow_cam.setFar(far_clip);
                 shadow_cam.setOrigin(origin);
@@ -10863,15 +10894,17 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 
                 stop_glerror();
 
-                mRT->shadow[i + 4].bindTarget();
-                mRT->shadow[i + 4].getViewport(gGLViewport);
-                mRT->shadow[i + 4].clear();
+                //
+                
+                mSpotShadow[i].bindTarget();
+                mSpotShadow[i].getViewport(gGLViewport);
+                mSpotShadow[i].clear();
 
-                U32 target_width = mRT->shadow[i + 4].getWidth();
+                U32 target_width = mSpotShadow[i].getWidth();
 
                 static LLCullResult result[2];
 
-                LLViewerCamera::sCurCameraID = (LLViewerCamera::eCameraID)(LLViewerCamera::CAMERA_SHADOW0 + i + 4);
+                LLViewerCamera::sCurCameraID = (LLViewerCamera::eCameraID)(LLViewerCamera::CAMERA_SPOT_SHADOW0 + i);
 
                 RenderSpotLight = drawable;
 
@@ -10879,7 +10912,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 
                 RenderSpotLight = nullptr;
 
-                mRT->shadow[i + 4].flush();
+                mSpotShadow[i].flush();
             }
         }
 	}
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index f4c55bde7d0d69185041bc9a7bf81b5e4209ee7b..f05b7aec8efb5f101629d0d6a8ddf2b42b7e6149 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -218,8 +218,9 @@ class LLPipeline
 	U32         addObject(LLViewerObject *obj);
 
 	void		enableShadows(const bool enable_shadows);
-    void        releaseShadowTargets();
-    void        releaseShadowTarget(U32 index);
+    void        releaseSpotShadowTargets();
+    void        releaseSunShadowTargets();
+    void        releaseSunShadowTarget(U32 index);
 
 // 	void		setLocalLighting(const bool local_lighting);
 // 	bool		isLocalLightingEnabled() const;
@@ -304,7 +305,8 @@ class LLPipeline
 
 	void generateWaterReflection(LLCamera& camera);
 	void generateSunShadow(LLCamera& camera);
-    LLRenderTarget* getShadowTarget(U32 i);
+    LLRenderTarget* getSunShadowTarget(U32 i);
+    LLRenderTarget* getSpotShadowTarget(U32 i);
 
 	void generateHighlight(LLCamera& camera);
 	void renderHighlight(const LLViewerObject* obj, F32 fade);
@@ -660,12 +662,15 @@ class LLPipeline
         LLRenderTarget			deferredLight;
 
         //sun shadow map
-        LLRenderTarget			shadow[6];
-        LLRenderTarget			shadowOcclusion[6];
+        LLRenderTarget			shadow[4];
+        LLRenderTarget			shadowOcclusion[4];
     };
 
     RenderTargetPack* mRT;
 
+    LLRenderTarget          mSpotShadow[2];
+    LLRenderTarget          mSpotShadowOcclusion[2];
+
     LLRenderTarget			mHighlight;
     LLRenderTarget			mPhysicsDisplay;
 
@@ -688,6 +693,7 @@ class LLPipeline
 	LLVector3				mShadowFrustOrigin[4];
 	LLCamera				mShadowCamera[8];
 	LLVector3				mShadowExtents[4][2];
+    // TODO : separate Sun Shadow and Spot Shadow matrices
 	glh::matrix4f			mSunShadowMatrix[6];
 	glh::matrix4f			mShadowModelview[6];
 	glh::matrix4f			mShadowProjection[6];
diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
index d1e167df64435c6e87d0aedf1b80390cad139852..8e12a01c6f570e5c878806c1dd794f3abed61680 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
@@ -863,8 +863,42 @@
        name="2"
        value="2"/>
   </combo_box>
-  
-<!-- End of Advanced Settings block -->
+  <text
+    type="string"
+    length="1"
+    follows="left|top"
+    height="16"
+    layout="topleft"
+    left="480"
+    name="RenderReflectionDetailText"
+    text_readonly_color="LabelDisabledColor"
+    top_delta="16"
+    width="128">
+    Reflections:
+  </text>
+  <combo_box
+   control_name="RenderReflectionProbeDetail"
+   height="18"
+   layout="topleft"
+   left_delta="130"
+   top_delta="0"
+   name="ReflectionDetial"
+   width="150">
+    <combo_box.item
+      label="Static Only"
+      name="0"
+      value="0"/>
+    <combo_box.item
+      label="Static+Dynamic"
+      name="1"
+      value="1"/>
+    <combo_box.item
+      label="Realtime"
+      name="2"
+      value="2"/>
+  </combo_box>
+
+  <!-- End of Advanced Settings block -->
 	<view_border
       bevel_style="in"
       height="0"