Merge pull request #891 from secondlife/geenz/mirrors-optimization-pass-1

#682  First mirrors optimization pass
master
Jonathan "Geenz" Goodman 2024-02-27 11:09:38 -08:00 committed by GitHub
commit 7d1e3e08fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 123 additions and 36 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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