From 3e4e414011b032e64b5fd477f2c561fc2b4553f7 Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Fri, 16 Feb 2024 09:49:45 -0800
Subject: secondlife/viewer-issues#72: Material preview shouldRender should
 return false if no render needed

---
 indra/newview/lldynamictexture.cpp         |  2 ++
 indra/newview/llgltfmaterialpreviewmgr.cpp | 20 +++++++++++++++++++-
 indra/newview/llgltfmaterialpreviewmgr.h   |  8 ++------
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index a66c3876fc..f8a6195bdd 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -118,6 +118,8 @@ BOOL LLViewerDynamicTexture::render()
 //-----------------------------------------------------------------------------
 void LLViewerDynamicTexture::preRender(BOOL clear_depth)
 {
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
      //use the bottom left corner
 	mOrigin.set(0, 0);
 
diff --git a/indra/newview/llgltfmaterialpreviewmgr.cpp b/indra/newview/llgltfmaterialpreviewmgr.cpp
index 901db87eed..36bd552c31 100644
--- a/indra/newview/llgltfmaterialpreviewmgr.cpp
+++ b/indra/newview/llgltfmaterialpreviewmgr.cpp
@@ -58,6 +58,15 @@ LLGLTFPreviewTexture::MaterialLoadLevels::MaterialLoadLevels()
     }
 }
 
+bool LLGLTFPreviewTexture::MaterialLoadLevels::isFullyLoaded()
+{
+    for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+    {
+        if (levels[i] != FULLY_LOADED) { return false; }
+    }
+    return true;
+}
+
 S32& LLGLTFPreviewTexture::MaterialLoadLevels::operator[](size_t i)
 {
     llassert(i >= 0 && i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT);
@@ -187,17 +196,26 @@ LLPointer<LLGLTFPreviewTexture> LLGLTFPreviewTexture::create(LLPointer<LLFetched
     return new LLGLTFPreviewTexture(material, LLPipeline::MAX_BAKE_WIDTH);
 }
 
-void LLGLTFPreviewTexture::preRender(BOOL clear_depth)
+BOOL LLGLTFPreviewTexture::needsRender()
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
 
+    if (!mShouldRender && mBestLoad.isFullyLoaded()) { return false; }
     MaterialLoadLevels current_load = get_material_load_levels(*mGLTFMaterial.get());
     if (current_load < mBestLoad)
     {
         mShouldRender = true;
         mBestLoad = current_load;
+        return true;
     }
+    return false;
+}
+
+void LLGLTFPreviewTexture::preRender(BOOL clear_depth)
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
 
+    llassert(mShouldRender);
     if (!mShouldRender) { return; }
 
     LLViewerDynamicTexture::preRender(clear_depth);
diff --git a/indra/newview/llgltfmaterialpreviewmgr.h b/indra/newview/llgltfmaterialpreviewmgr.h
index cc40a6f2e2..981c8b0592 100644
--- a/indra/newview/llgltfmaterialpreviewmgr.h
+++ b/indra/newview/llgltfmaterialpreviewmgr.h
@@ -40,7 +40,7 @@ public:
     // Width scales with size of material's textures
     static LLPointer<LLGLTFPreviewTexture> create(LLPointer<LLFetchedGLTFMaterial> material);
 
-    BOOL needsRender() override { return mNeedsRender; }
+    BOOL needsRender() override;
     void preRender(BOOL clear_depth = TRUE) override;
     BOOL render() override;
     void postRender(BOOL success) override;
@@ -50,15 +50,12 @@ public:
         S32 levels[LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT];
 
         MaterialLoadLevels();
-
+        bool isFullyLoaded();
         S32& operator[](size_t i);
-
         const S32& operator[](size_t i) const;
-
         // Less is better
         // Returns false if lhs is not strictly less or equal for all levels
         bool operator<(const MaterialLoadLevels& other) const;
-
         // Less is better
         // Returns false if lhs is not strictly greater or equal for all levels
         bool operator>(const MaterialLoadLevels& other) const;
@@ -66,7 +63,6 @@ public:
 
 private:
     LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial;
-    bool mNeedsRender = true;
     bool mShouldRender = true;
     MaterialLoadLevels mBestLoad;
 };
-- 
cgit v1.2.3


From ab9ec50df9dda3ac52752d44b7d5529ed26db7bf Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Fri, 16 Feb 2024 12:52:50 -0800
Subject: secondlife/viewer-issues#72: Don't dirty depth in
 LLViewerDynamicTexture::updateAllInstances

---
 indra/newview/lldynamictexture.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index f8a6195bdd..24818241ab 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -221,7 +221,6 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
                 llassert(dynamicTexture->getFullHeight() <= LLPipeline::MAX_BAKE_WIDTH);
 
 				glClear(GL_DEPTH_BUFFER_BIT);
-				gDepthDirty = TRUE;
 								
 				gGL.color4f(1,1,1,1);
                 dynamicTexture->setBoundTarget(&bake_target);
-- 
cgit v1.2.3


From bbc7d63a79b9744acaff51315e5e9a9d7299bd12 Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Tue, 20 Feb 2024 16:10:05 -0800
Subject: secondlife/viewer-issues#72: Clean up material preview when hidden or
 floater closed

---
 indra/newview/lltexturectrl.cpp | 15 +++++++++++++++
 indra/newview/lltexturectrl.h   |  2 ++
 2 files changed, 17 insertions(+)

diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 5415f9bd9a..d9001bc16a 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -538,6 +538,8 @@ void LLFloaterTexturePicker::onClose(bool app_quitting)
 	}
 	stopUsingPipette();
     sLastPickerMode = mModeSelector->getValue().asInteger();
+    // *NOTE: Vertex buffer for sphere preview is still cached
+    mGLTFPreview = nullptr;
 }
 
 // virtual
@@ -1760,6 +1762,19 @@ void LLTextureCtrl::setFilterPermissionMasks(PermissionMask mask)
     setDnDFilterPermMask(mask);
 }
 
+void LLTextureCtrl::onVisibilityChange(BOOL new_visibility)
+{
+    if (!new_visibility)
+    {
+        // *NOTE: Vertex buffer for sphere preview is still cached
+        mGLTFPreview = nullptr;
+    }
+    else
+    {
+        llassert(!mGLTFPreview);
+    }
+}
+
 void LLTextureCtrl::setVisible( BOOL visible ) 
 {
 	if( !visible )
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index 05ea185b1b..a19c8b6e3d 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -160,6 +160,8 @@ public:
 	virtual void	setVisible( BOOL visible );
 	virtual void	setEnabled( BOOL enabled );
 
+	void onVisibilityChange(BOOL new_visibility) override;
+
 	void			setValid(BOOL valid);
 
 	// LLUICtrl interface
-- 
cgit v1.2.3


From 589910f445efa9690b543a37b9e2e14792a0e133 Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Wed, 21 Feb 2024 12:53:10 -0800
Subject: secondlife/viewer-issues#72: Fix material preview affecting exposure
 on main screen

---
 indra/llrender/llrendertarget.cpp                  | 26 ++++++++
 indra/llrender/llrendertarget.h                    |  3 +
 .../shaders/class1/deferred/exposureF.glsl         |  6 +-
 indra/newview/llgltfmaterialpreviewmgr.cpp         |  8 ++-
 indra/newview/llviewershadermgr.cpp                | 17 ++++++
 indra/newview/llviewershadermgr.h                  |  1 +
 indra/newview/pipeline.cpp                         | 71 ++++++++++++++--------
 indra/newview/pipeline.h                           |  2 +-
 8 files changed, 104 insertions(+), 30 deletions(-)

diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index 88c48e5166..3dccb9c8fd 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -567,4 +567,30 @@ bool LLRenderTarget::isBoundInStack() const
     return cur == this;
 }
 
+void LLRenderTarget::swapFBORefs(LLRenderTarget& other)
+{
+    // Must be initialized
+    llassert(mFBO);
+    llassert(other.mFBO);
 
+    // Must be unbound
+    // *NOTE: mPreviousRT can be non-null even if this target is unbound - presumably for debugging purposes?
+    llassert(sCurFBO != mFBO);
+    llassert(sCurFBO != other.mFBO);
+    llassert(!isBoundInStack());
+    llassert(!other.isBoundInStack());
+
+    // Must be same type
+    llassert(sUseFBO == other.sUseFBO);
+    llassert(mResX == other.mResX);
+    llassert(mResY == other.mResY);
+    llassert(mInternalFormat == other.mInternalFormat);
+    llassert(mTex.size() == other.mTex.size());
+    llassert(mDepth == other.mDepth);
+    llassert(mUseDepth == other.mUseDepth);
+    llassert(mGenerateMipMaps == other.mGenerateMipMaps);
+    llassert(mMipLevels == other.mMipLevels);
+    llassert(mUsage == other.mUsage);
+
+    std::swap(mFBO, other.mFBO);
+}
diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h
index 9fcea35e3d..da401572d1 100644
--- a/indra/llrender/llrendertarget.h
+++ b/indra/llrender/llrendertarget.h
@@ -169,6 +169,9 @@ public:
 
 	static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; }
 
+	// *HACK
+	void swapFBORefs(LLRenderTarget& other);
+
 protected:
 	U32 mResX;
 	U32 mResY;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl b/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
index 9ac4ceb37e..eff7221ae7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
@@ -28,7 +28,9 @@
 out vec4 frag_color;
 
 uniform sampler2D emissiveRect;
+#ifdef USE_LAST_EXPOSURE
 uniform sampler2D exposureMap;
+#endif
 
 uniform float dt;
 uniform vec2 noiseVec;
@@ -51,10 +53,12 @@ void main()
     L /= max_L;
     L = pow(L, 2.0);
     float s = mix(dynamic_exposure_params.z, dynamic_exposure_params.y, L);
-
+    
+#ifdef USE_LAST_EXPOSURE
     float prev = texture(exposureMap, vec2(0.5,0.5)).r;
 
     s = mix(prev, s, min(dt*2.0*abs(prev-s), 0.04));
+#endif
     
     frag_color = max(vec4(s, s, s, dt), vec4(0.0));
 }
diff --git a/indra/newview/llgltfmaterialpreviewmgr.cpp b/indra/newview/llgltfmaterialpreviewmgr.cpp
index 36bd552c31..1c39f1ae7d 100644
--- a/indra/newview/llgltfmaterialpreviewmgr.cpp
+++ b/indra/newview/llgltfmaterialpreviewmgr.cpp
@@ -517,9 +517,12 @@ BOOL LLGLTFPreviewTexture::render()
         screen.flush();
     }
 
+    // *HACK: Hide mExposureMap from generateExposure
+    gPipeline.mExposureMap.swapFBORefs(gPipeline.mLastExposure);
+
     gPipeline.copyScreenSpaceReflections(&screen, &gPipeline.mSceneMap);
     gPipeline.generateLuminance(&screen, &gPipeline.mLuminanceMap);
-    gPipeline.generateExposure(&gPipeline.mLuminanceMap, &gPipeline.mExposureMap);
+    gPipeline.generateExposure(&gPipeline.mLuminanceMap, &gPipeline.mExposureMap, /*use_history = */ false);
     gPipeline.gammaCorrect(&screen, &gPipeline.mPostMap);
     LLVertexBuffer::unbind();
     gPipeline.generateGlow(&gPipeline.mPostMap);
@@ -527,6 +530,9 @@ BOOL LLGLTFPreviewTexture::render()
 	gPipeline.renderDoF(&screen, &gPipeline.mPostMap);
 	gPipeline.applyFXAA(&gPipeline.mPostMap, &screen);
 
+    // *HACK: Restore mExposureMap (it will be consumed by generateExposure next frame)
+    gPipeline.mExposureMap.swapFBORefs(gPipeline.mLastExposure);
+
     // Final render
 
 	gDeferredPostNoDoFProgram.bind();
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 4eb934114d..354cc79036 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -196,6 +196,7 @@ LLGLSLShader			gDeferredPostGammaCorrectProgram;
 LLGLSLShader            gNoPostGammaCorrectProgram;
 LLGLSLShader            gLegacyPostGammaCorrectProgram;
 LLGLSLShader			gExposureProgram;
+LLGLSLShader			gExposureProgramNoFade;
 LLGLSLShader			gLuminanceProgram;
 LLGLSLShader			gFXAAProgram;
 LLGLSLShader			gDeferredPostNoDoFProgram;
@@ -980,6 +981,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredCoFProgram.unload();		
 		gDeferredDoFCombineProgram.unload();
         gExposureProgram.unload();
+        gExposureProgramNoFade.unload();
         gLuminanceProgram.unload();
 		gDeferredPostGammaCorrectProgram.unload();
         gNoPostGammaCorrectProgram.unload();
@@ -2127,6 +2129,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
         gExposureProgram.mFeatures.isDeferred = true;
         gExposureProgram.mShaderFiles.clear();
         gExposureProgram.clearPermutations();
+        gExposureProgram.addPermutation("USE_LAST_EXPOSURE", "1");
         gExposureProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
         gExposureProgram.mShaderFiles.push_back(make_pair("deferred/exposureF.glsl", GL_FRAGMENT_SHADER));
         gExposureProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
@@ -2134,6 +2137,20 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
         llassert(success);
     }
 
+    if (success)
+    {
+        gExposureProgramNoFade.mName = "Exposure (no fade)";
+        gExposureProgramNoFade.mFeatures.hasSrgb = true;
+        gExposureProgramNoFade.mFeatures.isDeferred = true;
+        gExposureProgramNoFade.mShaderFiles.clear();
+        gExposureProgramNoFade.clearPermutations();
+        gExposureProgramNoFade.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
+        gExposureProgramNoFade.mShaderFiles.push_back(make_pair("deferred/exposureF.glsl", GL_FRAGMENT_SHADER));
+        gExposureProgramNoFade.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        success = gExposureProgramNoFade.createShader(NULL, NULL);
+        llassert(success);
+    }
+
     if (success)
     {
         gLuminanceProgram.mName = "Luminance";
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 57fc66ce0d..2502be6bb1 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -249,6 +249,7 @@ extern LLGLSLShader			gDeferredPostGammaCorrectProgram;
 extern LLGLSLShader         gNoPostGammaCorrectProgram;
 extern LLGLSLShader         gLegacyPostGammaCorrectProgram;
 extern LLGLSLShader			gExposureProgram;
+extern LLGLSLShader			gExposureProgramNoFade;
 extern LLGLSLShader			gLuminanceProgram;
 extern LLGLSLShader			gDeferredAvatarShadowProgram;
 extern LLGLSLShader			gDeferredAvatarAlphaShadowProgram;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 9d90002eb9..6ceaca731b 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -6772,11 +6772,12 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst)
 	}
 }
 
-void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) {
+void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool use_history) {
 	// exposure sample
 	{
 		LL_PROFILE_GPU_ZONE("exposure sample");
 
+		if (use_history)
 		{
 			// copy last frame's exposure into mLastExposure
 			mLastExposure.bindTarget();
@@ -6793,51 +6794,67 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) {
 
 		LLGLDepthTest depth(GL_FALSE, GL_FALSE);
 
-		gExposureProgram.bind();
+		LLGLSLShader* shader;
+		if (use_history)
+		{
+			shader = &gExposureProgram;
+		}
+		else
+		{
+			shader = &gExposureProgramNoFade;
+		}
+
+		shader->bind();
 
-		S32 channel = gExposureProgram.enableTexture(LLShaderMgr::DEFERRED_EMISSIVE);
+		S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_EMISSIVE);
 		if (channel > -1)
 		{
-			mLuminanceMap.bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR);
+			src->bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR);
 		}
 
-		channel = gExposureProgram.enableTexture(LLShaderMgr::EXPOSURE_MAP);
-		if (channel > -1)
+		if (use_history)
 		{
-			mLastExposure.bindTexture(0, channel);
+			channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP);
+			if (channel > -1)
+			{
+				mLastExposure.bindTexture(0, channel);
+			}
 		}
 
 		static LLStaticHashedString dt("dt");
 		static LLStaticHashedString noiseVec("noiseVec");
 		static LLStaticHashedString dynamic_exposure_params("dynamic_exposure_params");
 		static LLCachedControl<F32> dynamic_exposure_coefficient(gSavedSettings, "RenderDynamicExposureCoefficient", 0.175f);
-        static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true);
+		static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true);
 
-        LLSettingsSky::ptr_t sky = LLEnvironment::instance().getCurrentSky();
+		LLSettingsSky::ptr_t sky = LLEnvironment::instance().getCurrentSky();
 
-        F32 probe_ambiance = LLEnvironment::instance().getCurrentSky()->getReflectionProbeAmbiance(should_auto_adjust);
-        F32 exp_min = 1.f;
-        F32 exp_max = 1.f;
-                
-        if (probe_ambiance > 0.f)
-        {
-            F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma())*2.f;
+		F32 probe_ambiance = LLEnvironment::instance().getCurrentSky()->getReflectionProbeAmbiance(should_auto_adjust);
+		F32 exp_min = 1.f;
+		F32 exp_max = 1.f;
 
-            if (hdr_scale > 1.f)
-            {
-                exp_min = 1.f / hdr_scale;
-                exp_max = hdr_scale;
-            }
-        }
-		gExposureProgram.uniform1f(dt, gFrameIntervalSeconds);
-		gExposureProgram.uniform2f(noiseVec, ll_frand() * 2.0 - 1.0, ll_frand() * 2.0 - 1.0);
-		gExposureProgram.uniform3f(dynamic_exposure_params, dynamic_exposure_coefficient, exp_min, exp_max);
+		if (probe_ambiance > 0.f)
+		{
+			F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma()) * 2.f;
+
+			if (hdr_scale > 1.f)
+			{
+				exp_min = 1.f / hdr_scale;
+				exp_max = hdr_scale;
+			}
+		}
+		shader->uniform1f(dt, gFrameIntervalSeconds);
+		shader->uniform2f(noiseVec, ll_frand() * 2.0 - 1.0, ll_frand() * 2.0 - 1.0);
+		shader->uniform3f(dynamic_exposure_params, dynamic_exposure_coefficient, exp_min, exp_max);
 
 		mScreenTriangleVB->setBuffer();
 		mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
 
-		gGL.getTexUnit(channel)->unbind(mLastExposure.getUsage());
-		gExposureProgram.unbind();
+		if (use_history)
+		{
+			gGL.getTexUnit(channel)->unbind(mLastExposure.getUsage());
+		}
+		shader->unbind();
 		dst->flush();
 	}
 }
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index e8c6da1473..259dc3f9d0 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -154,7 +154,7 @@ public:
 	void renderFinalize();
 	void copyScreenSpaceReflections(LLRenderTarget* src, LLRenderTarget* dst);
 	void generateLuminance(LLRenderTarget* src, LLRenderTarget* dst);
-	void generateExposure(LLRenderTarget* src, LLRenderTarget* dst);
+	void generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool use_history = true);
 	void gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst);
 	void generateGlow(LLRenderTarget* src);
 	void applyFXAA(LLRenderTarget* src, LLRenderTarget* dst);
-- 
cgit v1.2.3


From 97f049db3b396c7a12d723f3305084570866a475 Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Wed, 21 Feb 2024 13:00:53 -0800
Subject: secondlife/viewer-issues#72: Fix clang warnings (virtual override)

---
 indra/newview/lltexturectrl.h | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index a19c8b6e3d..f193b0c083 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -148,28 +148,28 @@ public:
 
 	// LLView interface
 
-	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleDragAndDrop(S32 x, S32 y, MASK mask,
-						BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
-						EAcceptance *accept,
-						std::string& tooltip_msg);
-	virtual BOOL	handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleUnicodeCharHere(llwchar uni_char);
+    BOOL handleMouseDown(S32 x, S32 y, MASK mask) override;
+    BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+        BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+        EAcceptance *accept,
+        std::string& tooltip_msg) override;
+    BOOL handleHover(S32 x, S32 y, MASK mask) override;
+    BOOL handleUnicodeCharHere(llwchar uni_char) override;
 
-	virtual void	draw();
-	virtual void	setVisible( BOOL visible );
-	virtual void	setEnabled( BOOL enabled );
+    void draw() override;
+    void setVisible( BOOL visible ) override;
+    void setEnabled( BOOL enabled ) override;
 
-	void onVisibilityChange(BOOL new_visibility) override;
+    void onVisibilityChange(BOOL new_visibility) override;
 
-	void			setValid(BOOL valid);
+    void setValid(BOOL valid);
 
-	// LLUICtrl interface
-	virtual void	clear();
+    // LLUICtrl interface
+    void clear() override;
 
-	// Takes a UUID, wraps get/setImageAssetID
-	virtual void	setValue(const LLSD& value);
-	virtual LLSD	getValue() const;
+    // Takes a UUID, wraps get/setImageAssetID
+    void setValue(const LLSD& value) override;
+    LLSD getValue() const override;
 
 	// LLTextureCtrl interface
 	void			showPicker(BOOL take_focus);
-- 
cgit v1.2.3


From ce55ff2e77db649f71107230609fc20fc86a0af3 Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Wed, 21 Feb 2024 15:02:03 -0800
Subject: secondlife/viewer-issues#72: Add test plan

---
 doc/testplans/material_preview.md | 81 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 doc/testplans/material_preview.md

diff --git a/doc/testplans/material_preview.md b/doc/testplans/material_preview.md
new file mode 100644
index 0000000000..d653fc45ca
--- /dev/null
+++ b/doc/testplans/material_preview.md
@@ -0,0 +1,81 @@
+# Material Preview
+
+## Overview
+
+Material preview is a UI feature which displays a lit spherical preview of a PBR material. It can be found in the following UIs:
+
+- The material picker swatch
+    - In the build floater, in the Texture tab, when applying a PBR material
+    - (If the feature is enabled) In the Region/Estate floater, in the Terrain tab, when applying PBR materials to terrain
+- In the floater to select a material from inventory, which can be opened by clicking the material picker swatch
+
+## Known Issues
+
+These are known issues that the current implementation of this feature does not address:
+
+- Material swatch preview is a preview of the base material ID only, and ignores other properties on the prim face like material overrides
+- Alpha mask previews as alpha blend
+- Double-sided previews as single-sided
+- Material swatch preview inherits some of its lighting from the current environment, and reflections from the default reflection probe
+
+## General Regression Testing
+
+- Check that the material preview swatch looks OK with different materials selected
+- Check that the material preview swatch runs reasonably well on different systems, especially when the select material from inventory floater is also open
+    - In particular: AMD, MacOS, minimum spec machines
+- Watch out for regressions in rendering caused by opening a floater with a material preview swatch
+
+## Bug Fixes
+
+### Disappearing Objects Fix Test
+
+This test is recommended for verifying that secondlife/viewer-issues#72 is fixed.
+
+#### Symptoms
+
+The following types of objects could randomly disappear until relog in the main view when the bug occured:
+
+- Objects
+- Water level in current region
+- Adjacent region/void water
+
+Note: Disappearing objects in reflections have a different root cause and are not covered by the fix.
+
+#### Bug Reproduction Steps
+
+Verify the disappearing objects bug does not reproduce, given the following steps:
+
+- Runtime prerequisites: Material preview swatch is enabled ("UIPreviewMaterial") by visiting a test region with the feature enabled, or by manually enabling "UIPreviewMaterial" in the advanced settings for the current session
+- Region prerequisites: Unknown, but a region with lots of stuff in it (like Rumpus Room 2048 on Aditi) seems to increase repro rate
+- Right click an object and select, "Edit item"
+- Go to texture tab, select PBR Metallic Roughness from dropdown, and click the button to select material from inventory
+- Ensure "Apply now" is checked in the inventory selection floater
+- Alternate between different materials from the inventory selection floater
+- Look around the world and check for permanently disappeared objects.
+
+### Dynamic Exposure Influence Fix Test
+
+This test is recommended for verifying that secondlife/viewer-issues#72 is fixed.
+
+#### Symptoms
+
+Dynamic exposure in the world could be influenced by the material preview being displayed. If a material preview was being generated in a given frame, then, depending on the current environment, the user would observe an unpleasant flashing effect in the environment:
+
+- The world view could suddenly get darker and then fade back to normal exposure levels
+- The world view could suddenly get brighter and then fade back to normal exposure levels
+
+#### Bug Reproduction Steps
+
+Verify the dynamic exposure influence bug does not reproduce. Test using a few environment presets such as Default Midday, Sunset, and Midnight.
+
+- Right click an object and select, "Edit item"
+- Go to texture tab, select PBR Metallic Roughness from dropdown, and click the button to select material from inventory
+- Alternate between different materials from the inventory selection floater
+
+#### Regression Testing
+
+Dynamic exposure in the world should continue to work correctly. In particular:
+
+- Exposure should fade gradually from high exposure to low exposure and back as needed
+- Exposure should decrease in brighter environments
+- Exposure should increase in darker environments
-- 
cgit v1.2.3


From fc7abe8cb9ef737cb1ad4431e30c42f08d6e8899 Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Wed, 21 Feb 2024 16:06:11 -0800
Subject: secondlife/viewer-issues#72: More correct interpretation of
 swapFBORefs

---
 indra/llrender/llrendertarget.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index 3dccb9c8fd..9cd7527d3e 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -593,4 +593,5 @@ void LLRenderTarget::swapFBORefs(LLRenderTarget& other)
     llassert(mUsage == other.mUsage);
 
     std::swap(mFBO, other.mFBO);
+    std::swap(mTex, other.mTex);
 }
-- 
cgit v1.2.3