summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/app_settings/settings.xml22
-rw-r--r--indra/newview/llheroprobemanager.cpp95
-rw-r--r--indra/newview/llheroprobemanager.h12
-rw-r--r--indra/newview/llviewerdisplay.cpp2
-rw-r--r--indra/newview/llviewerobject.h1
-rw-r--r--indra/newview/llvovolume.cpp9
-rw-r--r--indra/newview/pipeline.cpp16
-rw-r--r--indra/newview/pipeline.h2
8 files changed, 123 insertions, 36 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e7d947b85f..e707e1eb5e 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10390,6 +10390,28 @@
<key>Value</key>
<real>8</real>
</map>
+ <key>RenderHeroProbeUpdateRate</key>
+ <map>
+ <key>Comment</key>
+ <string>How many frames to wait for until it's time to render the probe. E.g., every other frame (1), every two frames (2), every three frames (3) etc.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>2</integer>
+ </map>
+ <key>RenderHeroProbeConservativeUpdateMultiplier</key>
+ <map>
+ <key>Comment</key>
+ <string>How many probe updates to wait until it's time to update faces that are not directly facing the camera. Acts as a multiplier. E.g., frames to the periphery of the camera updating once every 3 updates, vs ones directly facing the camera updating every update.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>16</integer>
+ </map>
<key>RenderReflectionProbeVolumes</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp
index 2a81919856..a00b6d6b5d 100644
--- a/indra/newview/llheroprobemanager.cpp
+++ b/indra/newview/llheroprobemanager.cpp
@@ -114,20 +114,13 @@ void LLHeroProbeManager::update()
for (auto vo : mHeroVOList)
{
- if (vo && !vo->isDead())
+ if (vo && !vo->isDead() && vo->mDrawable.notNull())
{
- if (vo->mDrawable.notNull())
+ float distance = (LLViewerCamera::instance().getOrigin() - vo->getPositionAgent()).magVec();
+ if (distance < last_distance)
{
- if (vo->mDrawable->mDistanceWRTCamera < last_distance)
- {
- mNearestHero = vo;
- last_distance = vo->mDrawable->mDistanceWRTCamera;
- }
- }
- else
- {
- // Valid drawables only please. Unregister this one.
- unregisterViewerObject(vo);
+ mNearestHero = vo;
+ last_distance = distance;
}
}
else
@@ -135,13 +128,13 @@ void LLHeroProbeManager::update()
unregisterViewerObject(vo);
}
}
-
+
if (mNearestHero != nullptr && !mNearestHero->isDead() && mNearestHero->mDrawable.notNull())
{
LLVector3 hero_pos = mNearestHero->getPositionAgent();
LLVector3 face_normal = LLVector3(0, 0, 1);
-
- face_normal *= mNearestHero->mDrawable->getXform()->getWorldRotation();
+
+ face_normal *= mNearestHero->mDrawable->getWorldRotation();
face_normal.normalize();
LLVector3 offset = camera_pos - hero_pos;
@@ -155,6 +148,32 @@ void LLHeroProbeManager::update()
probe_pos.load3(point.mV);
+
+ // Collect the list of faces that need updating based upon the camera's rotation.
+ LLVector3 cam_direction = LLVector3(0, 0, 1) * LLViewerCamera::instance().getQuaternion();
+
+ static LLVector3 cubeFaces[6] = {
+ LLVector3(1, 0, 0),
+ LLVector3(-1, 0, 0),
+ LLVector3(0, 1, 0),
+ LLVector3(0, -1, 0),
+ LLVector3(0, 0, 1),
+ LLVector3(0, 0, -1)
+ };
+
+ for (int i = 0; i < 6; i++)
+ {
+ float shouldUpdate = cam_direction * cubeFaces[i] * 0.5 + 0.5;
+
+ int updateRate = ceilf((1 - shouldUpdate) * gPipeline.RenderHeroProbeConservativeUpdateMultiplier);
+
+ // Chances are this is a face that's non-visible to the camera when it's being reflected.
+ // Set it to 0. It will be skipped below.
+ if (updateRate == gPipeline.RenderHeroProbeConservativeUpdateMultiplier)
+ updateRate = 0;
+
+ mFaceUpdateList[i] = updateRate;
+ }
}
else
{
@@ -185,13 +204,20 @@ void LLHeroProbeManager::update()
{
for (U32 i = 0; i < 6; ++i)
{
- updateProbeFace(mProbes[j], i, near_clip);
+ if (mFaceUpdateList[i] > 0 && mCurrentProbeUpdateFrame % mFaceUpdateList[i] == 0)
+ {
+ updateProbeFace(mProbes[j], i, near_clip);
+ mCurrentProbeUpdateFrame = 0;
+ }
}
+ generateRadiance(mProbes[j]);
}
mRenderingMirror = false;
gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass;
}
+
+ mCurrentProbeUpdateFrame++;
}
// Do the reflection map update render passes.
@@ -242,7 +268,7 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, F32 n
LLRenderTarget *screen_rt = &gPipeline.mHeroProbeRT.screen;
LLRenderTarget *depth_rt = &gPipeline.mHeroProbeRT.deferredScreen;
-
+
// perform a gaussian blur on the super sampled render before downsampling
{
gGaussianProgram.bind();
@@ -318,14 +344,25 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, F32 n
gGL.getTexUnit(diffuseChannel)->unbind(LLTexUnit::TT_TEXTURE);
gReflectionMipProgram.unbind();
}
+}
- if (face == 5)
+// Separate out radiance generation as a separate stage.
+// This is to better enable independent control over how we generate radiance vs. having it coupled with processing the final face of the probe.
+// Useful when we may not always be rendering a full set of faces of the probe.
+void LLHeroProbeManager::generateRadiance(LLReflectionMap* probe)
+{
+ S32 sourceIdx = mReflectionProbeCount;
+
+ // Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes.
+ sourceIdx += 1;
{
mMipChain[0].bindTarget();
static LLStaticHashedString sSourceIdx("sourceIdx");
{
- //generate radiance map (even if this is not the irradiance map, we need the mip chain for the irradiance map)
+
+
+ // generate radiance map (even if this is not the irradiance map, we need the mip chain for the irradiance map)
gHeroRadianceGenProgram.bind();
mVertexBuffer->setBuffer();
@@ -334,10 +371,10 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, F32 n
gHeroRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
gHeroRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
gHeroRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, mHeroProbeStrength);
-
+
U32 res = mMipChain[0].getWidth();
- for (int i = 0; i < mMipChain.size(); ++i)
+ for (int i = 0; i < mMipChain.size() / 4; ++i)
{
LL_PROFILE_GPU_ZONE("probe radiance gen");
static LLStaticHashedString sMipLevel("mipLevel");
@@ -351,7 +388,7 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, F32 n
gHeroRadianceGenProgram.uniform1f(sStrength, 1);
for (int cf = 0; cf < 6; ++cf)
- { // for each cube face
+ { // for each cube face
LLCoordFrame frame;
frame.lookAt(LLVector3(0, 0, 0), LLCubeMapArray::sClipToCubeLookVecs[cf], LLCubeMapArray::sClipToCubeUpVecs[cf]);
@@ -539,21 +576,25 @@ void LLHeroProbeManager::doOcclusion()
}
}
-void LLHeroProbeManager::registerViewerObject(LLVOVolume* drawablep)
+bool LLHeroProbeManager::registerViewerObject(LLVOVolume* drawablep)
{
llassert(drawablep != nullptr);
- if (mHeroVOList.find(drawablep) == mHeroVOList.end())
+ if (std::find(mHeroVOList.begin(), mHeroVOList.end(), drawablep) == mHeroVOList.end())
{
// Probe isn't in our list for consideration. Add it.
- mHeroVOList.insert(drawablep);
+ mHeroVOList.push_back(drawablep);
+ return true;
}
+
+ return false;
}
void LLHeroProbeManager::unregisterViewerObject(LLVOVolume* drawablep)
{
- if (mHeroVOList.find(drawablep) != mHeroVOList.end())
+ std::vector<LLVOVolume*>::iterator found_itr = std::find(mHeroVOList.begin(), mHeroVOList.end(), drawablep);
+ if (found_itr != mHeroVOList.end())
{
- mHeroVOList.erase(drawablep);
+ mHeroVOList.erase(found_itr);
}
}
diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h
index 552c5dcaab..e430cae203 100644
--- a/indra/newview/llheroprobemanager.h
+++ b/indra/newview/llheroprobemanager.h
@@ -68,7 +68,7 @@ public:
// perform occlusion culling on all active reflection probes
void doOcclusion();
- void registerViewerObject(LLVOVolume *drawablep);
+ bool registerViewerObject(LLVOVolume *drawablep);
void unregisterViewerObject(LLVOVolume* drawablep);
bool isMirrorPass() const { return mRenderingMirror; }
@@ -104,9 +104,10 @@ private:
// update the specified face of the specified probe
void updateProbeFace(LLReflectionMap* probe, U32 face, F32 near_clip);
+ void generateRadiance(LLReflectionMap *probe);
// list of active reflection maps
- std::vector<LLPointer<LLReflectionMap> > mProbes;
+ std::vector<LLPointer<LLReflectionMap>> mProbes;
// handle to UBO
U32 mUBO = 0;
@@ -134,8 +135,11 @@ private:
bool mReset = false;
bool mRenderingMirror = false;
+ std::map<int, int> mFaceUpdateList;
- std::set<LLPointer<LLVOVolume>> mHeroVOList;
- LLPointer<LLVOVolume> mNearestHero;
+ U32 mCurrentProbeUpdateFrame = 0;
+
+ std::vector<LLVOVolume*> mHeroVOList;
+ LLVOVolume* mNearestHero;
};
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index e6d9aed1a3..2a77146101 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -658,7 +658,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
{
// Render mirrors and associated hero probes before we render the rest of the scene.
// This ensures the scene state in the hero probes are exactly the same as the rest of the scene before we render it.
- if (gPipeline.RenderMirrors && !gSnapshot)
+ if (gPipeline.RenderMirrors && !gSnapshot && (gPipeline.RenderHeroProbeUpdateRate == 0 || (gFrameCount % gPipeline.RenderHeroProbeUpdateRate) == 0))
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Update hero probes");
gPipeline.mHeroProbeManager.update();
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 80da7b2f73..0c4d958aed 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -944,6 +944,7 @@ public:
// reflection probe state
bool mIsReflectionProbe = false; // if true, this object should register itself with LLReflectionProbeManager
LLPointer<LLReflectionMap> mReflectionProbe = nullptr; // reflection probe coupled to this viewer object. If not null, should be deregistered when this object is destroyed
+ bool mIsHeroProbe = false; // This is a special case for mirrors and other high resolution probes.
// the amount of GPU time (in ms) it took to render this object according to LLPipeline::profileAvatar
// -1.f if no profile data available
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 27d2e6af71..d7af7a42d1 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -296,6 +296,11 @@ void LLVOVolume::markDead()
{
mLightTexture->removeVolume(LLRender::LIGHT_TEX, this);
}
+
+ if (mIsHeroProbe)
+ {
+ gPipeline.mHeroProbeManager.unregisterViewerObject(this);
+ }
}
LLViewerObject::markDead();
@@ -4411,7 +4416,9 @@ void LLVOVolume::updateReflectionProbePtr()
{
// Geenz: This is a special case - what we want here is a hero probe.
// What we want to do here is instantiate a hero probe from the hero probe manager.
- gPipeline.mHeroProbeManager.registerViewerObject(this);
+
+ if (!mIsHeroProbe)
+ mIsHeroProbe = gPipeline.mHeroProbeManager.registerViewerObject(this);
}
}
else if (mReflectionProbe.notNull() || getReflectionProbeIsMirror())
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 6ceaca731b..94ec5c0817 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -199,6 +199,8 @@ F32 LLPipeline::RenderScreenSpaceReflectionAdaptiveStepMultiplier;
S32 LLPipeline::RenderScreenSpaceReflectionGlossySamples;
S32 LLPipeline::RenderBufferVisualization;
bool LLPipeline::RenderMirrors;
+S32 LLPipeline::RenderHeroProbeUpdateRate;
+S32 LLPipeline::RenderHeroProbeConservativeUpdateMultiplier;
LLTrace::EventStatHandle<S64> LLPipeline::sStatBatchSize("renderbatchsize");
const U32 LLPipeline::MAX_BAKE_WIDTH = 512;
@@ -559,6 +561,8 @@ void LLPipeline::init()
connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionGlossySamples");
connectRefreshCachedSettingsSafe("RenderBufferVisualization");
connectRefreshCachedSettingsSafe("RenderMirrors");
+ connectRefreshCachedSettingsSafe("RenderHeroProbeUpdateRate");
+ connectRefreshCachedSettingsSafe("RenderHeroProbeConservativeUpdateMultiplier");
gSavedSettings.getControl("RenderAutoHideSurfaceAreaLimit")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings));
}
@@ -786,9 +790,12 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
U32 res = mReflectionMapManager.mProbeResolution * 4; //multiply by 4 because probes will be 16x super sampled
allocateScreenBuffer(res, res, samples);
- res = mHeroProbeManager.mProbeResolution; // We also scale the hero probe RT to the probe res since we don't super sample it.
- mRT = &mHeroProbeRT;
- allocateScreenBuffer(res, res, samples);
+ if (RenderMirrors)
+ {
+ res = mHeroProbeManager.mProbeResolution; // We also scale the hero probe RT to the probe res since we don't super sample it.
+ mRT = &mHeroProbeRT;
+ allocateScreenBuffer(res, res, samples);
+ }
mRT = &mMainRT;
gCubeSnapshot = FALSE;
@@ -1068,6 +1075,9 @@ void LLPipeline::refreshCachedSettings()
LLViewerShaderMgr::instance()->clearShaderCache();
LLViewerShaderMgr::instance()->setShaders();
}
+ RenderHeroProbeUpdateRate = gSavedSettings.getS32("RenderHeroProbeUpdateRate");
+ RenderHeroProbeConservativeUpdateMultiplier = gSavedSettings.getS32("RenderHeroProbeConservativeUpdateMultiplier");
+
sReflectionProbesEnabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderReflectionsEnabled") && gSavedSettings.getBOOL("RenderReflectionsEnabled");
RenderSpotLight = nullptr;
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 259dc3f9d0..817750b4a1 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -1056,6 +1056,8 @@ public:
static S32 RenderScreenSpaceReflectionGlossySamples;
static S32 RenderBufferVisualization;
static bool RenderMirrors;
+ static S32 RenderHeroProbeUpdateRate;
+ static S32 RenderHeroProbeConservativeUpdateMultiplier;
};
void render_bbox(const LLVector3 &min, const LLVector3 &max);