Merge branch 'release/materials_featurette' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/newview/lltexturectrl.cpp
master
Ansariel 2024-02-22 20:17:20 +01:00
commit eb98cb3864
13 changed files with 245 additions and 55 deletions

View File

@ -0,0 +1,83 @@
# 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:
- The material preview in the build floater is a preview of the base material ID only, and ignores other properties on the prim face like material overrides (https://github.com/secondlife/viewer/issues/865)
- Alpha mask previews as alpha blend (https://github.com/secondlife/viewer/issues/866)
- Double-sided previews as single-sided (https://github.com/secondlife/viewer/issues/867)
- Material preview inherits some of its lighting from the current environment, and reflections from the default reflection probe (https://github.com/secondlife/viewer/issues/868)
## 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 https://github.com/secondlife/viewer-issues/issues/72 is fixed.
#### Symptoms
When the bug occurs, one or more of following types of objects could randomly disappear in the world, permanently until relog:
- 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 may not be available in your viewer or region if this feature is still behind a feature flag. It is safe to enable this feature manually by setting, "UIPreviewMaterial" to True in the advanced settings. The setting will persist for the current session.
- Region prerequisites: Unknown, but a region with lots of objects in it seems to increase repro rate. The following locations have been known to easily reproduce the bug, as of 2024-02-16:
- http://maps.secondlife.com/secondlife/LindenWorld%20B/161/75/47
- [secondlife://Aditi/secondlife/Rumpus%20Room%202048/128/128/24](secondlife://Aditi/secondlife/Rumpus%20Room%202048/128/128/24)
- 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 https://github.com/secondlife/viewer-issues/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

View File

@ -567,4 +567,31 @@ 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);
std::swap(mTex, other.mTex);
}

View File

@ -169,6 +169,9 @@ public:
static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; }
// *HACK
void swapFBORefs(LLRenderTarget& other);
protected:
U32 mResX;
U32 mResY;

View File

@ -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));
}

View File

@ -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);
@ -222,7 +224,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);

View File

@ -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);
@ -499,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);
@ -509,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();

View File

@ -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;
};

View File

@ -593,6 +593,8 @@ void LLFloaterTexturePicker::onClose(bool app_quitting)
// <FS:Ansariel> FIRE-30431: Keep radio button mode selection in texture selection
//sLastPickerMode = mModeSelector->getValue().asInteger();
sLastPickerMode = mModeSelector->getSelectedIndex();
// *NOTE: Vertex buffer for sphere preview is still cached
mGLTFPreview = nullptr;
}
// virtual
@ -1929,6 +1931,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 )

View File

@ -156,26 +156,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 setValid(BOOL valid);
void onVisibilityChange(BOOL new_visibility) override;
// LLUICtrl interface
virtual void clear();
void setValid(BOOL valid);
// Takes a UUID, wraps get/setImageAssetID
virtual void setValue(const LLSD& value);
virtual LLSD getValue() const;
// LLUICtrl interface
void clear() override;
// Takes a UUID, wraps get/setImageAssetID
void setValue(const LLSD& value) override;
LLSD getValue() const override;
// LLTextureCtrl interface
void showPicker(BOOL take_focus);

View File

@ -197,6 +197,7 @@ LLGLSLShader gDeferredPostGammaCorrectProgram;
LLGLSLShader gNoPostGammaCorrectProgram;
LLGLSLShader gLegacyPostGammaCorrectProgram;
LLGLSLShader gExposureProgram;
LLGLSLShader gExposureProgramNoFade;
LLGLSLShader gLuminanceProgram;
LLGLSLShader gFXAAProgram;
LLGLSLShader gDeferredPostNoDoFProgram;
@ -997,6 +998,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredCoFProgram.unload();
gDeferredDoFCombineProgram.unload();
gExposureProgram.unload();
gExposureProgramNoFade.unload();
gLuminanceProgram.unload();
gDeferredPostGammaCorrectProgram.unload();
gNoPostGammaCorrectProgram.unload();
@ -2148,6 +2150,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];
@ -2155,6 +2158,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";

View File

@ -250,6 +250,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;

View File

@ -6911,11 +6911,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();
@ -6932,51 +6933,67 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) {
LLGLDepthTest depth(GL_FALSE, GL_FALSE);
gExposureProgram.bind();
S32 channel = gExposureProgram.enableTexture(LLShaderMgr::DEFERRED_EMISSIVE);
if (channel > -1)
LLGLSLShader* shader;
if (use_history)
{
mLuminanceMap.bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR);
shader = &gExposureProgram;
}
else
{
shader = &gExposureProgramNoFade;
}
channel = gExposureProgram.enableTexture(LLShaderMgr::EXPOSURE_MAP);
shader->bind();
S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_EMISSIVE);
if (channel > -1)
{
mLastExposure.bindTexture(0, channel);
src->bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR);
}
if (use_history)
{
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();
}
}

View File

@ -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);