summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/llheroprobemanager.cpp1333
-rw-r--r--indra/newview/llheroprobemanager.h208
-rw-r--r--indra/newview/llreflectionmapmanager.cpp168
-rw-r--r--indra/newview/llreflectionmapmanager.h25
-rw-r--r--indra/newview/pipeline.cpp3
6 files changed, 1594 insertions, 145 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index c6d82ea260..7a57fe8fef 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -515,6 +515,7 @@ set(viewer_SOURCE_FILES
llrecentpeople.cpp
llreflectionmap.cpp
llreflectionmapmanager.cpp
+ llheroprobemanager.cpp
llregioninfomodel.cpp
llregionposition.cpp
llremoteparcelrequest.cpp
@@ -1149,6 +1150,7 @@ set(viewer_HEADER_FILES
llrecentpeople.h
llreflectionmap.h
llreflectionmapmanager.h
+ llheroprobemanager.h
llregioninfomodel.h
llregionposition.h
llremoteparcelrequest.h
diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp
new file mode 100644
index 0000000000..54a61d000e
--- /dev/null
+++ b/indra/newview/llheroprobemanager.cpp
@@ -0,0 +1,1333 @@
+/**
+ * @file LLHeroProbeManager.cpp
+ * @brief LLHeroProbeManager class implementation
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llheroprobemanager.h"
+#include "llreflectionmapmanager.h"
+#include "llviewercamera.h"
+#include "llspatialpartition.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+#include "llviewershadermgr.h"
+#include "llviewercontrol.h"
+#include "llenvironment.h"
+#include "llstartup.h"
+
+extern BOOL gCubeSnapshot;
+extern BOOL gTeleportDisplay;
+
+// get the next highest power of two of v (or v if v is already a power of two)
+//defined in llvertexbuffer.cpp
+extern U32 nhpo2(U32 v);
+
+static void touch_default_probe(LLReflectionMap* probe)
+{
+ if (LLViewerCamera::getInstance())
+ {
+ LLVector3 origin = LLViewerCamera::getInstance()->getOrigin();
+ origin.mV[2] += 64.f;
+
+ probe->mOrigin.load3(origin.mV);
+ }
+}
+
+LLHeroProbeManager::LLHeroProbeManager()
+{
+ initCubeFree();
+}
+
+void LLHeroProbeManager::initCubeFree()
+{
+ // start at 1 because index 0 is reserved for mDefaultProbe
+ for (int i = 1; i < LL_MAX_REFLECTION_PROBE_COUNT; ++i)
+ {
+ mCubeFree.push_back(i);
+ }
+}
+
+struct CompareProbeDistance
+{
+ LLReflectionMap* mDefaultProbe;
+
+ bool operator()(const LLPointer<LLReflectionMap>& lhs, const LLPointer<LLReflectionMap>& rhs)
+ {
+ return lhs->mDistance < rhs->mDistance;
+ }
+};
+
+static F32 update_score(LLReflectionMap* p)
+{
+ return gFrameTimeSeconds - p->mLastUpdateTime - p->mDistance*0.1f;
+}
+
+// return true if a is higher priority for an update than b
+static bool check_priority(LLReflectionMap* a, LLReflectionMap* b)
+{
+ if (a->mCubeIndex == -1)
+ { // not a candidate for updating
+ return false;
+ }
+ else if (b->mCubeIndex == -1)
+ { // certainly higher priority than b
+ return true;
+ }
+ else if (!a->mComplete && !b->mComplete)
+ { //neither probe is complete, use distance
+ return a->mDistance < b->mDistance;
+ }
+ else if (a->mComplete && b->mComplete)
+ { //both probes are complete, use update_score metric
+ return update_score(a) > update_score(b);
+ }
+
+ // one of these probes is not complete, if b is complete, a is higher priority
+ return b->mComplete;
+}
+
+// helper class to seed octree with probes
+void LLHeroProbeManager::update()
+{
+ if (!LLPipeline::sReflectionProbesEnabled || gTeleportDisplay || LLStartUp::getStartupState() < STATE_PRECACHE)
+ {
+ return;
+ }
+
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+ llassert(!gCubeSnapshot); // assert a snapshot is not in progress
+ if (LLAppViewer::instance()->logoutRequestSent())
+ {
+ return;
+ }
+
+ initReflectionMaps();
+
+ if (!mRenderTarget.isComplete())
+ {
+ U32 color_fmt = GL_RGB16F;
+ U32 targetRes = mProbeResolution * 4; // super sample
+ mRenderTarget.allocate(targetRes, targetRes, color_fmt, true);
+ }
+
+ if (!mHeroRenderTarget.isComplete())
+ {
+ U32 color_fmt = GL_RGB16F;
+ U32 targetRes = mHeroProbeResolution * 2;
+ mHeroRenderTarget.allocate(targetRes, targetRes, color_fmt, true);
+ }
+
+ if (mMipChain.empty())
+ {
+ U32 res = mHeroProbeResolution;
+ U32 count = log2((F32)res) + 0.5f;
+
+ mMipChain.resize(count);
+ for (int i = 0; i < count; ++i)
+ {
+ mMipChain[i].allocate(res, res, GL_RGB16F);
+ res /= 2;
+ }
+ }
+
+ llassert(mProbes[0] == mDefaultProbe);
+
+ LLVector4a camera_pos;
+ camera_pos.load3(LLViewerCamera::instance().getOrigin().mV);
+
+ // process kill list
+ for (auto& probe : mKillList)
+ {
+ auto const & iter = std::find(mProbes.begin(), mProbes.end(), probe);
+ if (iter != mProbes.end())
+ {
+ deleteProbe(iter - mProbes.begin());
+ }
+ }
+
+ mKillList.clear();
+
+ // process create list
+ for (auto& probe : mCreateList)
+ {
+ mProbes.push_back(probe);
+ }
+
+ mCreateList.clear();
+
+ if (mProbes.empty())
+ {
+ return;
+ }
+
+
+ bool did_update = false;
+
+ static LLCachedControl<S32> sDetail(gSavedSettings, "RenderReflectionProbeDetail", -1);
+ static LLCachedControl<S32> sLevel(gSavedSettings, "RenderReflectionProbeLevel", 3);
+
+ bool realtime = sDetail >= (S32)LLHeroProbeManager::DetailLevel::REALTIME;
+
+ LLReflectionMap* closestDynamic = nullptr;
+
+ LLReflectionMap* oldestProbe = nullptr;
+ LLReflectionMap* oldestOccluded = nullptr;
+
+ if (mUpdatingProbe != nullptr)
+ {
+ did_update = true;
+ doProbeUpdate();
+ }
+
+ // update distance to camera for all probes
+ std::sort(mProbes.begin()+1, mProbes.end(), CompareProbeDistance());
+ llassert(mProbes[0] == mDefaultProbe);
+ llassert(mProbes[0]->mCubeArray == mTexture);
+ llassert(mProbes[0]->mCubeIndex == 0);
+
+ // make sure we're assigning cube slots to the closest probes
+
+ // first free any cube indices for distant probes
+ for (U32 i = mReflectionProbeCount; i < mProbes.size(); ++i)
+ {
+ LLReflectionMap* probe = mProbes[i];
+ llassert(probe != nullptr);
+
+ if (probe->mCubeIndex != -1 && mUpdatingProbe != probe)
+ { // free this index
+ mCubeFree.push_back(probe->mCubeIndex);
+
+ probe->mCubeArray = nullptr;
+ probe->mCubeIndex = -1;
+ probe->mComplete = false;
+ }
+ }
+
+ // next distribute the free indices
+ U32 count = llmin(mReflectionProbeCount, (U32)mProbes.size());
+
+ for (S32 i = 1; i < count && !mCubeFree.empty(); ++i)
+ {
+ // find the closest probe that needs a cube index
+ LLReflectionMap* probe = mProbes[i];
+
+ if (probe->mCubeIndex == -1)
+ {
+ S32 idx = allocateCubeIndex();
+ llassert(idx > 0); //if we're still in this loop, mCubeFree should not be empty and allocateCubeIndex should be returning good indices
+ probe->mCubeArray = mTexture;
+ probe->mCubeIndex = idx;
+ }
+ }
+
+ for (int i = 0; i < mProbes.size(); ++i)
+ {
+ LLReflectionMap* probe = mProbes[i];
+ if (probe->getNumRefs() == 1)
+ { // no references held outside manager, delete this probe
+ deleteProbe(i);
+ --i;
+ continue;
+ }
+
+ if (probe != mDefaultProbe && !probe->isRelevant())
+ {
+ continue;
+ }
+
+ LLVector4a d;
+
+ if (probe != mDefaultProbe)
+ {
+ if (probe->mViewerObject) //make sure probes track the viewer objects they are attached to
+ {
+ probe->mOrigin.load3(probe->mViewerObject->getPositionAgent().mV);
+ }
+ d.setSub(camera_pos, probe->mOrigin);
+ probe->mDistance = d.getLength3().getF32() - probe->mRadius;
+ }
+ else if (probe->mComplete)
+ {
+ // make default probe have a distance of 64m for the purposes of prioritization (if it's already been generated once)
+ probe->mDistance = 64.f;
+ }
+ else
+ {
+ probe->mDistance = -4096.f; //boost priority of default probe when it's not complete
+ }
+
+ if (probe->mComplete)
+ {
+ probe->autoAdjustOrigin();
+ probe->mFadeIn = llmin((F32) (probe->mFadeIn + gFrameIntervalSeconds), 1.f);
+ }
+ if (probe->mOccluded && probe->mComplete)
+ {
+ if (oldestOccluded == nullptr)
+ {
+ oldestOccluded = probe;
+ }
+ else if (probe->mLastUpdateTime < oldestOccluded->mLastUpdateTime)
+ {
+ oldestOccluded = probe;
+ }
+ }
+ else
+ {
+ if (!did_update &&
+ i < mReflectionProbeCount &&
+ (oldestProbe == nullptr ||
+ check_priority(probe, oldestProbe)))
+ {
+ oldestProbe = probe;
+ }
+ }
+
+ if (realtime &&
+ closestDynamic == nullptr &&
+ probe->mCubeIndex != -1 &&
+ probe->getIsDynamic())
+ {
+ closestDynamic = probe;
+ }
+ }
+
+ if (realtime && closestDynamic != nullptr)
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmu - realtime");
+ // update the closest dynamic probe realtime
+ // should do a full irradiance pass on "odd" frames and a radiance pass on "even" frames
+ closestDynamic->autoAdjustOrigin();
+
+ // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set
+ // lighting values etc
+ bool radiance_pass = isRadiancePass();
+ mRadiancePass = mRealtimeRadiancePass;
+ for (U32 i = 0; i < 6; ++i)
+ {
+ updateProbeFace(closestDynamic, i, mProbeResolution, mTexture, mMipChain, mReflectionProbeCount);
+ }
+ mRealtimeRadiancePass = !mRealtimeRadiancePass;
+
+ // restore "isRadiancePass"
+ mRadiancePass = radiance_pass;
+ }
+
+
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmu - realtime");
+
+ mHeroProbe->mOrigin.load3(LLViewerCamera::instance().mOrigin.mV);
+ bool radiance_pass = isRadiancePass();
+ mRadiancePass = true;
+ for (U32 i = 0; i < 6; ++i)
+ {
+ updateProbeFace(mHeroProbe, i, mHeroProbeResolution, mHeroArray, mMipChain, mHeroProbeCount);
+ }
+
+ // restore "isRadiancePass"
+ mRadiancePass = radiance_pass;
+ }
+
+ static LLCachedControl<F32> sUpdatePeriod(gSavedSettings, "RenderDefaultProbeUpdatePeriod", 2.f);
+ if ((gFrameTimeSeconds - mDefaultProbe->mLastUpdateTime) < sUpdatePeriod)
+ {
+ if (sLevel == 0)
+ { // when probes are disabled don't update the default probe more often than the prescribed update period
+ oldestProbe = nullptr;
+ }
+ }
+ else if (sLevel > 0)
+ { // when probes are enabled don't update the default probe less often than the prescribed update period
+ oldestProbe = mDefaultProbe;
+ }
+
+ // switch to updating the next oldest probe
+ if (!did_update && oldestProbe != nullptr)
+ {
+ LLReflectionMap* probe = oldestProbe;
+ llassert(probe->mCubeIndex != -1);
+
+ probe->autoAdjustOrigin();
+
+ mUpdatingProbe = probe;
+ doProbeUpdate();
+ }
+
+ if (oldestOccluded)
+ {
+ // as far as this occluded probe is concerned, an origin/radius update is as good as a full update
+ oldestOccluded->autoAdjustOrigin();
+ oldestOccluded->mLastUpdateTime = gFrameTimeSeconds;
+ }
+}
+
+LLReflectionMap* LLHeroProbeManager::addProbe(LLSpatialGroup* group)
+{
+ LLReflectionMap* probe = new LLReflectionMap();
+ probe->mGroup = group;
+
+ if (mDefaultProbe.isNull())
+ { //safety check to make sure default probe is always first probe added
+ mDefaultProbe = new LLReflectionMap();
+ mProbes.push_back(mDefaultProbe);
+ }
+
+ llassert(mProbes[0] == mDefaultProbe);
+
+ if (group)
+ {
+ probe->mOrigin = group->getOctreeNode()->getCenter();
+ }
+
+ if (gCubeSnapshot)
+ { //snapshot is in progress, mProbes is being iterated over, defer insertion until next update
+ mCreateList.push_back(probe);
+ }
+ else
+ {
+ mProbes.push_back(probe);
+ }
+
+ return probe;
+}
+
+struct CompareProbeDepth
+{
+ bool operator()(const LLReflectionMap* lhs, const LLReflectionMap* rhs)
+ {
+ return lhs->mMinDepth < rhs->mMinDepth;
+ }
+};
+
+void LLHeroProbeManager::getReflectionMaps(std::vector<LLReflectionMap*>& maps)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+ LLVector4a oa; // scratch space for transformed origin
+
+ U32 count = 0;
+ U32 lastIdx = 0;
+ for (U32 i = 0; count < maps.size() && i < mProbes.size(); ++i)
+ {
+ mProbes[i]->mLastBindTime = gFrameTimeSeconds; // something wants to use this probe, indicate it's been requested
+ if (mProbes[i]->mCubeIndex != -1)
+ {
+ if (!mProbes[i]->mOccluded && mProbes[i]->mComplete)
+ {
+ maps[count++] = mProbes[i];
+ modelview.affineTransform(mProbes[i]->mOrigin, oa);
+ mProbes[i]->mMinDepth = -oa.getF32ptr()[2] - mProbes[i]->mRadius;
+ mProbes[i]->mMaxDepth = -oa.getF32ptr()[2] + mProbes[i]->mRadius;
+ }
+ }
+ else
+ {
+ mProbes[i]->mProbeIndex = -1;
+ }
+ lastIdx = i;
+ }
+
+ // set remaining probe indices to -1
+ for (U32 i = lastIdx+1; i < mProbes.size(); ++i)
+ {
+ mProbes[i]->mProbeIndex = -1;
+ }
+
+ if (count > 1)
+ {
+ std::sort(maps.begin(), maps.begin() + count, CompareProbeDepth());
+ }
+
+ for (U32 i = 0; i < count; ++i)
+ {
+ maps[i]->mProbeIndex = i;
+ }
+
+ // null terminate list
+ if (count < maps.size())
+ {
+ maps[count] = nullptr;
+ }
+}
+
+LLReflectionMap* LLHeroProbeManager::registerSpatialGroup(LLSpatialGroup* group)
+{
+ if (group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_VOLUME)
+ {
+ OctreeNode* node = group->getOctreeNode();
+ F32 size = node->getSize().getF32ptr()[0];
+ if (size >= 15.f && size <= 17.f)
+ {
+ return addProbe(group);
+ }
+ }
+
+ return nullptr;
+}
+
+LLReflectionMap* LLHeroProbeManager::registerViewerObject(LLViewerObject* vobj)
+{
+ llassert(vobj != nullptr);
+
+ LLReflectionMap* probe = new LLReflectionMap();
+ probe->mViewerObject = vobj;
+ probe->mOrigin.load3(vobj->getPositionAgent().mV);
+
+ if (gCubeSnapshot)
+ { //snapshot is in progress, mProbes is being iterated over, defer insertion until next update
+ mCreateList.push_back(probe);
+ }
+ else
+ {
+ mProbes.push_back(probe);
+ }
+
+ return probe;
+}
+
+S32 LLHeroProbeManager::allocateCubeIndex()
+{
+ if (!mCubeFree.empty())
+ {
+ S32 ret = mCubeFree.front();
+ mCubeFree.pop_front();
+ return ret;
+ }
+
+ return -1;
+}
+
+void LLHeroProbeManager::deleteProbe(U32 i)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+ LLReflectionMap* probe = mProbes[i];
+
+ llassert(probe != mDefaultProbe);
+
+ if (probe->mCubeIndex != -1)
+ { // mark the cube index used by this probe as being free
+ mCubeFree.push_back(probe->mCubeIndex);
+ }
+ if (mUpdatingProbe == probe)
+ {
+ mUpdatingProbe = nullptr;
+ mUpdatingFace = 0;
+ }
+
+ // remove from any Neighbors lists
+ for (auto& other : probe->mNeighbors)
+ {
+ auto const & iter = std::find(other->mNeighbors.begin(), other->mNeighbors.end(), probe);
+ llassert(iter != other->mNeighbors.end());
+ other->mNeighbors.erase(iter);
+ }
+
+ mProbes.erase(mProbes.begin() + i);
+}
+
+
+void LLHeroProbeManager::doProbeUpdate()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+ llassert(mUpdatingProbe != nullptr);
+
+ updateProbeFace(mUpdatingProbe, mUpdatingFace, mProbeResolution, mTexture, mMipChain, mReflectionProbeCount);
+
+ if (++mUpdatingFace == 6)
+ {
+ updateNeighbors(mUpdatingProbe);
+ mUpdatingFace = 0;
+ if (isRadiancePass())
+ {
+ mUpdatingProbe->mComplete = true;
+ mUpdatingProbe = nullptr;
+ mRadiancePass = false;
+ }
+ else
+ {
+ mRadiancePass = true;
+ }
+ }
+}
+
+// Do the reflection map update render passes.
+// For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated
+// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate
+// a simple mip chain (not convolution filter).
+// At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe
+// The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain.
+// At the end of these passes, a radiance map is generated for this probe and placed into the radiance cube map array at the index for this probe.
+// In effect this simulates single-bounce lighting.
+void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, U32 probeResolution, LLPointer<LLCubeMapArray> cubeArray, std::vector<LLRenderTarget> &mipChain, U32 probeCount)
+{
+ // hacky hot-swap of camera specific render targets
+ gPipeline.mRT = &gPipeline.mAuxillaryRT;
+
+ LLRenderTarget* target = &mRenderTarget;
+
+ S32 sourceIdx = probeCount;
+
+ if (probeResolution == mHeroProbeResolution)
+ {
+ sourceIdx = 0;
+ target = &mHeroRenderTarget;
+ }
+
+ mLightScale = 1.f;
+ static LLCachedControl<F32> max_local_light_ambiance(gSavedSettings, "RenderReflectionProbeMaxLocalLightAmbiance", 8.f);
+ if (!isRadiancePass() && probe->getAmbiance() > max_local_light_ambiance)
+ {
+ mLightScale = max_local_light_ambiance / probe->getAmbiance();
+ }
+
+ if (probe == mDefaultProbe)
+ {
+ touch_default_probe(probe);
+
+ gPipeline.pushRenderTypeMask();
+
+ //only render sky, water, terrain, and clouds
+ gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, LLPipeline::RENDER_TYPE_WL_SKY,
+ LLPipeline::RENDER_TYPE_WATER, LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_CLOUDS, LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::END_RENDER_TYPES);
+
+ probe->update(target->getWidth(), face);
+
+ gPipeline.popRenderTypeMask();
+ }
+ else
+ {
+ probe->update(target->getWidth(), face);
+ }
+
+ gPipeline.mRT = &gPipeline.mMainRT;
+
+ if (probe != mUpdatingProbe && probe->mType != LLReflectionMap::ProbeType::REFLECTION)
+ { // this is the "realtime" probe that's updating every frame, use the secondary scratch space channel
+ sourceIdx += 1;
+ }
+
+ gGL.setColorMask(true, true);
+ LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+ LLGLDisable cull(GL_CULL_FACE);
+ LLGLDisable blend(GL_BLEND);
+
+ // downsample to placeholder map
+ {
+ gGL.matrixMode(gGL.MM_MODELVIEW);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+
+ gGL.matrixMode(gGL.MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+
+ gGL.flush();
+ U32 res = probeResolution * 2;
+
+ static LLStaticHashedString resScale("resScale");
+ static LLStaticHashedString direction("direction");
+ static LLStaticHashedString znear("znear");
+ static LLStaticHashedString zfar("zfar");
+
+ LLRenderTarget* screen_rt = &gPipeline.mAuxillaryRT.screen;
+
+ // perform a gaussian blur on the super sampled render before downsampling
+ {
+ gGaussianProgram.bind();
+ gGaussianProgram.uniform1f(resScale, 1.f / (probeResolution * 2));
+ S32 diffuseChannel = gGaussianProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
+
+ // horizontal
+ gGaussianProgram.uniform2f(direction, 1.f, 0.f);
+ gGL.getTexUnit(diffuseChannel)->bind(screen_rt);
+ target->bindTarget();
+ gPipeline.mScreenTriangleVB->setBuffer();
+ gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+ target->flush();
+
+ // vertical
+ gGaussianProgram.uniform2f(direction, 0.f, 1.f);
+ gGL.getTexUnit(diffuseChannel)->bind(target);
+ screen_rt->bindTarget();
+ gPipeline.mScreenTriangleVB->setBuffer();
+ gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+ screen_rt->flush();
+ }
+
+
+ S32 mips = log2((F32)probeResolution) + 0.5f;
+
+ gReflectionMipProgram.bind();
+ S32 diffuseChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
+
+ for (int i = 0; i < mMipChain.size(); ++i)
+ {
+ LL_PROFILE_GPU_ZONE("probe mip");
+ mMipChain[i].bindTarget();
+ if (i == 0)
+ {
+ gGL.getTexUnit(diffuseChannel)->bind(screen_rt);
+ }
+ else
+ {
+ gGL.getTexUnit(diffuseChannel)->bind(&(mMipChain[i - 1]));
+ }
+
+
+ gReflectionMipProgram.uniform1f(resScale, 1.f/(probeResolution*2));
+
+ gPipeline.mScreenTriangleVB->setBuffer();
+ gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+
+ res /= 2;
+
+ S32 mip = i - (mMipChain.size() - mips);
+
+ if (mip >= 0)
+ {
+ LL_PROFILE_GPU_ZONE("probe mip copy");
+ cubeArray->bind(0);
+
+ glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, sourceIdx * 6 + face, 0, 0, res, res);
+
+ cubeArray->unbind();
+ }
+ mMipChain[i].flush();
+ }
+
+ gGL.popMatrix();
+ gGL.matrixMode(gGL.MM_MODELVIEW);
+ gGL.popMatrix();
+
+ gGL.getTexUnit(diffuseChannel)->unbind(LLTexUnit::TT_TEXTURE);
+ gReflectionMipProgram.unbind();
+ }
+
+ if (face == 5)
+ {
+ mMipChain[0].bindTarget();
+ static LLStaticHashedString sSourceIdx("sourceIdx");
+
+ if (isRadiancePass())
+ {
+ //generate radiance map (even if this is not the irradiance map, we need the mip chain for the irradiance map)
+ gRadianceGenProgram.bind();
+ mVertexBuffer->setBuffer();
+
+ S32 channel = gRadianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
+ cubeArray->bind(channel);
+ gRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
+ gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
+
+ U32 res = mMipChain[0].getWidth();
+
+ for (int i = 0; i < mMipChain.size(); ++i)
+ {
+ LL_PROFILE_GPU_ZONE("probe radiance gen");
+ static LLStaticHashedString sMipLevel("mipLevel");
+ static LLStaticHashedString sRoughness("roughness");
+ static LLStaticHashedString sWidth("u_width");
+
+ gRadianceGenProgram.uniform1f(sRoughness, (F32)i / (F32)(mMipChain.size() - 1));
+ gRadianceGenProgram.uniform1f(sMipLevel, i);
+ gRadianceGenProgram.uniform1i(sWidth, probeResolution);
+
+ for (int cf = 0; cf < 6; ++cf)
+ { // for each cube face
+ LLCoordFrame frame;
+ frame.lookAt(LLVector3(0, 0, 0), LLCubeMapArray::sClipToCubeLookVecs[cf], LLCubeMapArray::sClipToCubeUpVecs[cf]);
+
+ F32 mat[16];
+ frame.getOpenGLRotation(mat);
+ gGL.loadMatrix(mat);
+
+ mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4);
+
+ glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i, 0, 0, probe->mCubeIndex * 6 + cf, 0, 0, res, res);
+ }
+
+ if (i != mMipChain.size() - 1)
+ {
+ res /= 2;
+ glViewport(0, 0, res, res);
+ }
+ }
+
+ gRadianceGenProgram.unbind();
+ }
+ else
+ {
+ //generate irradiance map
+ gIrradianceGenProgram.bind();
+ S32 channel = gIrradianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
+ cubeArray->bind(channel);
+
+ gIrradianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
+ gIrradianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
+
+ mVertexBuffer->setBuffer();
+ int start_mip = 0;
+ // find the mip target to start with based on irradiance map resolution
+ for (start_mip = 0; start_mip < mMipChain.size(); ++start_mip)
+ {
+ if (mMipChain[start_mip].getWidth() == LL_IRRADIANCE_MAP_RESOLUTION)
+ {
+ break;
+ }
+ }
+
+ //for (int i = start_mip; i < mMipChain.size(); ++i)
+ {
+ int i = start_mip;
+ LL_PROFILE_GPU_ZONE("probe irradiance gen");
+ glViewport(0, 0, mMipChain[i].getWidth(), mMipChain[i].getHeight());
+ for (int cf = 0; cf < 6; ++cf)
+ { // for each cube face
+ LLCoordFrame frame;
+ frame.lookAt(LLVector3(0, 0, 0), LLCubeMapArray::sClipToCubeLookVecs[cf], LLCubeMapArray::sClipToCubeUpVecs[cf]);
+
+ F32 mat[16];
+ frame.getOpenGLRotation(mat);
+ gGL.loadMatrix(mat);
+
+ mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4);
+
+ S32 res = mMipChain[i].getWidth();
+ mIrradianceMaps->bind(channel);
+ glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i - start_mip, 0, 0, probe->mCubeIndex * 6 + cf, 0, 0, res, res);
+ cubeArray->bind(channel);
+ }
+ }
+ }
+
+ mMipChain[0].flush();
+
+ gIrradianceGenProgram.unbind();
+ }
+}
+
+void LLHeroProbeManager::reset()
+{
+ mReset = true;
+}
+
+void LLHeroProbeManager::shift(const LLVector4a& offset)
+{
+ for (auto& probe : mProbes)
+ {
+ probe->mOrigin.add(offset);
+ }
+}
+
+void LLHeroProbeManager::updateNeighbors(LLReflectionMap* probe)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+ if (mDefaultProbe == probe)
+ {
+ return;
+ }
+
+ //remove from existing neighbors
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmun - clear");
+
+ for (auto& other : probe->mNeighbors)
+ {
+ auto const & iter = std::find(other->mNeighbors.begin(), other->mNeighbors.end(), probe);
+ llassert(iter != other->mNeighbors.end()); // <--- bug davep if this ever happens, something broke badly
+ other->mNeighbors.erase(iter);
+ }
+
+ probe->mNeighbors.clear();
+ }
+
+ // search for new neighbors
+ if (probe->isRelevant())
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmun - search");
+ for (auto& other : mProbes)
+ {
+ if (other != mDefaultProbe && other != probe)
+ {
+ if (other->isRelevant() && probe->intersects(other))
+ {
+ probe->mNeighbors.push_back(other);
+ other->mNeighbors.push_back(probe);
+ }
+ }
+ }
+ }
+}
+
+void LLHeroProbeManager::updateUniforms()
+{
+ if (!LLPipeline::sReflectionProbesEnabled)
+ {
+ return;
+ }
+
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+
+ // structure for packing uniform buffer object
+ // see class3/deferred/reflectionProbeF.glsl
+ struct ReflectionProbeData
+ {
+ // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of
+ // the box probe
+ LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT];
+
+ // for sphere probes, origin (xyz) and radius (w) of refmaps in clip space
+ LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
+
+ // extra parameters
+ // x - irradiance scale
+ // y - radiance scale
+ // z - fade in
+ // w - znear
+ LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT];
+
+ // indices used by probe:
+ // [i][0] - cubemap array index for this probe
+ // [i][1] - index into "refNeighbor" for probes that intersect this probe
+ // [i][2] - number of probes that intersect this probe, or -1 for no neighbors
+ // [i][3] - priority (probe type stored in sign bit - positive for spheres, negative for boxes)
+ GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4];
+
+ // list of neighbor indices
+ GLint refNeighbor[4096];
+
+ GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth
+ // numbrer of active refmaps
+ GLint refmapCount;
+ };
+
+ mReflectionMaps.resize(mReflectionProbeCount);
+ getReflectionMaps(mReflectionMaps);
+
+ ReflectionProbeData rpd;
+
+ F32 minDepth[256];
+
+ for (int i = 0; i < 256; ++i)
+ {
+ rpd.refBucket[i][0] = mReflectionProbeCount;
+ rpd.refBucket[i][1] = mReflectionProbeCount;
+ rpd.refBucket[i][2] = mReflectionProbeCount;
+ rpd.refBucket[i][3] = mReflectionProbeCount;
+ minDepth[i] = FLT_MAX;
+ }
+
+ // load modelview matrix into matrix 4a
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+ LLVector4a oa; // scratch space for transformed origin
+
+ S32 count = 0;
+ U32 nc = 0; // neighbor "cursor" - index into refNeighbor to start writing the next probe's list of neighbors
+
+ LLEnvironment& environment = LLEnvironment::instance();
+ LLSettingsSky::ptr_t psky = environment.getCurrentSky();
+
+ static LLCachedControl<F32> cloud_shadow_scale(gSavedSettings, "RenderCloudShadowAmbianceFactor", 0.125f);
+ static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true);
+ F32 minimum_ambiance = psky->getTotalReflectionProbeAmbiance(cloud_shadow_scale, should_auto_adjust);
+
+ bool is_ambiance_pass = gCubeSnapshot && !isRadiancePass();
+ F32 ambscale = is_ambiance_pass ? 0.f : 1.f;
+ F32 radscale = is_ambiance_pass ? 0.5f : 1.f;
+
+ for (auto* refmap : mReflectionMaps)
+ {
+ if (refmap == nullptr)
+ {
+ break;
+ }
+
+ if (refmap != mDefaultProbe)
+ {
+ // bucket search data
+ // theory of operation:
+ // 1. Determine minimum and maximum depth of each influence volume and store in mDepth (done in getReflectionMaps)
+ // 2. Sort by minimum depth
+ // 3. Prepare a bucket for each 1m of depth out to 256m
+ // 4. For each bucket, store the index of the nearest probe that might influence pixels in that bucket
+ // 5. In the shader, lookup the bucket for the pixel depth to get the index of the first probe that could possibly influence
+ // the current pixel.
+ int depth_min = llclamp(llfloor(refmap->mMinDepth), 0, 255);
+ int depth_max = llclamp(llfloor(refmap->mMaxDepth), 0, 255);
+ for (U32 i = depth_min; i <= depth_max; ++i)
+ {
+ if (refmap->mMinDepth < minDepth[i])
+ {
+ minDepth[i] = refmap->mMinDepth;
+ rpd.refBucket[i][0] = refmap->mProbeIndex;
+ }
+ }
+ }
+
+ llassert(refmap->mProbeIndex == count);
+ llassert(mReflectionMaps[refmap->mProbeIndex] == refmap);
+
+ llassert(refmap->mCubeIndex >= 0); // should always be true, if not, getReflectionMaps is bugged
+
+ {
+ if (refmap->mViewerObject)
+ { // have active manual probes live-track the object they're associated with
+ refmap->mOrigin.load3(refmap->mViewerObject->getPositionAgent().mV);
+ refmap->mRadius = refmap->mViewerObject->getScale().mV[0] * 0.5f;
+
+ }
+ modelview.affineTransform(refmap->mOrigin, oa);
+ rpd.refSphere[count].set(oa.getF32ptr());
+ rpd.refSphere[count].mV[3] = refmap->mRadius;
+ }
+
+ rpd.refIndex[count][0] = refmap->mCubeIndex;
+ llassert(nc % 4 == 0);
+ rpd.refIndex[count][1] = nc / 4;
+ rpd.refIndex[count][3] = refmap->mPriority;
+
+ // for objects that are reflection probes, use the volume as the influence volume of the probe
+ // only possibile influence volumes are boxes and spheres, so detect boxes and treat everything else as spheres
+ if (refmap->getBox(rpd.refBox[count]))
+ { // negate priority to indicate this probe has a box influence volume
+ rpd.refIndex[count][3] = -rpd.refIndex[count][3];
+ }
+
+ rpd.refParams[count].set(
+ llmax(minimum_ambiance, refmap->getAmbiance())*ambscale, // ambiance scale
+ radscale, // radiance scale
+ refmap->mFadeIn, // fade in weight
+ oa.getF32ptr()[2] - refmap->mRadius); // z near
+
+ S32 ni = nc; // neighbor ("index") - index into refNeighbor to write indices for current reflection probe's neighbors
+ {
+ //LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - refNeighbors");
+ //pack neghbor list
+ const U32 max_neighbors = 64;
+ U32 neighbor_count = 0;
+
+ for (auto& neighbor : refmap->mNeighbors)
+ {
+ if (ni >= 4096)
+ { // out of space
+ break;
+ }
+
+ GLint idx = neighbor->mProbeIndex;
+ if (idx == -1 || neighbor->mOccluded || neighbor->mCubeIndex == -1)
+ {
+ continue;
+ }
+
+ // this neighbor may be sampled
+ rpd.refNeighbor[ni++] = idx;
+
+ neighbor_count++;
+ if (neighbor_count >= max_neighbors)
+ {
+ break;
+ }
+ }
+ }
+
+ if (nc == ni)
+ {
+ //no neighbors, tag as empty
+ rpd.refIndex[count][1] = -1;
+ }
+ else
+ {
+ rpd.refIndex[count][2] = ni - nc;
+
+ // move the cursor forward
+ nc = ni;
+ if (nc % 4 != 0)
+ { // jump to next power of 4 for compatibility with ivec4
+ nc += 4 - (nc % 4);
+ }
+ }
+
+
+ count++;
+ }
+
+#if 0
+ {
+ // fill in gaps in refBucket
+ S32 probe_idx = mReflectionProbeCount;
+
+ for (int i = 0; i < 256; ++i)
+ {
+ if (i < count)
+ { // for debugging, store depth of mReflectionsMaps[i]
+ rpd.refBucket[i][1] = (S32) (mReflectionMaps[i]->mDepth * 10);
+ }
+
+ if (rpd.refBucket[i][0] == mReflectionProbeCount)
+ {
+ rpd.refBucket[i][0] = probe_idx;
+ }
+ else
+ {
+ probe_idx = rpd.refBucket[i][0];
+ }
+ }
+ }
+#endif
+
+ rpd.refmapCount = count;
+
+ //copy rpd into uniform buffer object
+ if (mUBO == 0)
+ {
+ glGenBuffers(1, &mUBO);
+ }
+
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer");
+ glBindBuffer(GL_UNIFORM_BUFFER, mUBO);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(ReflectionProbeData), &rpd, GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ }
+
+ struct HeroProbeData
+ {
+ LLVector4 heroPosition[1];
+ GLint heroProbeCount = 1;
+ };
+
+ HeroProbeData hpd;
+
+ modelview.loadu(gGLModelView);
+
+ oa.set(0, 0, 0, 0);
+ hpd.heroProbeCount = 1;
+ modelview.affineTransform(mHeroProbe->mOrigin, oa);
+ hpd.heroPosition[0].set(oa.getF32ptr());
+
+ //copy rpd into uniform buffer object
+ if (mUBO == 0)
+ {
+ glGenBuffers(1, &mHeroUBO);
+ }
+
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer");
+ glBindBuffer(GL_UNIFORM_BUFFER, mHeroUBO);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(HeroProbeData), &hpd, GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ }
+
+#if 0
+ if (!gCubeSnapshot)
+ {
+ for (auto& probe : mProbes)
+ {
+ LLViewerObject* vobj = probe->mViewerObject;
+ if (vobj)
+ {
+ F32 time = (F32)gFrameTimeSeconds - probe->mLastUpdateTime;
+ vobj->setDebugText(llformat("%d/%d/%d/%.1f - %.1f/%.1f", probe->mCubeIndex, probe->mProbeIndex, (U32) probe->mNeighbors.size(), probe->mMinDepth, probe->mMaxDepth, time), time > 1.f ? LLColor4::white : LLColor4::green);
+ }
+ }
+ }
+#endif
+}
+
+void LLHeroProbeManager::setUniforms()
+{
+ if (!LLPipeline::sReflectionProbesEnabled)
+ {
+ return;
+ }
+
+ if (mUBO == 0)
+ {
+ updateUniforms();
+ }
+ glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
+}
+
+void LLHeroProbeManager::renderDebug()
+{
+ gDebugProgram.bind();
+
+ for (auto& probe : mProbes)
+ {
+ renderReflectionProbe(probe);
+ }
+
+ gDebugProgram.unbind();
+}
+
+void LLHeroProbeManager::initReflectionMaps()
+{
+ U32 count = LL_MAX_REFLECTION_PROBE_COUNT;
+
+ if (mTexture.isNull() || mReflectionProbeCount != count || mReset || mHeroArray.isNull())
+ {
+ mReset = false;
+ mReflectionProbeCount = count;
+ mProbeResolution = nhpo2(llclamp(gSavedSettings.getU32("RenderReflectionProbeResolution"), (U32)64, (U32)512));
+ mMaxProbeLOD = log2f(mProbeResolution) - 1.f; // number of mips - 1
+
+ mTexture = new LLCubeMapArray();
+
+ // store mReflectionProbeCount+2 cube maps, final two cube maps are used for render target and radiance map generation source)
+ mTexture->allocate(mProbeResolution, 3, mReflectionProbeCount + 2);
+
+ mIrradianceMaps = new LLCubeMapArray();
+ mIrradianceMaps->allocate(LL_IRRADIANCE_MAP_RESOLUTION, 3, mReflectionProbeCount, FALSE);
+
+ // reset probe state
+ mUpdatingFace = 0;
+ mUpdatingProbe = nullptr;
+ mRadiancePass = false;
+ mRealtimeRadiancePass = false;
+
+ for (auto& probe : mProbes)
+ {
+ probe->mLastUpdateTime = 0.f;
+ probe->mComplete = false;
+ probe->mProbeIndex = -1;
+ probe->mCubeArray = nullptr;
+ probe->mCubeIndex = -1;
+ probe->mNeighbors.clear();
+ }
+
+ mCubeFree.clear();
+ initCubeFree();
+
+ if (mDefaultProbe.isNull())
+ {
+ llassert(mProbes.empty()); // default probe MUST be the first probe created
+ mDefaultProbe = new LLReflectionMap();
+ mProbes.push_back(mDefaultProbe);
+ }
+
+ llassert(mProbes[0] == mDefaultProbe);
+
+ mDefaultProbe->mCubeIndex = 0;
+ mDefaultProbe->mCubeArray = mTexture;
+ mDefaultProbe->mDistance = 64.f;
+ mDefaultProbe->mRadius = 4096.f;
+ mDefaultProbe->mProbeIndex = 0;
+ touch_default_probe(mDefaultProbe);
+
+ mHeroProbeResolution = 128;
+
+ // Revise when we have both water and mirrors in hero probes.
+ mHeroProbeCount = 1;
+
+ mHeroArray = new LLCubeMapArray();
+
+ // We use an extra probe for scratch space on these.
+ mHeroArray->allocate(mHeroProbeResolution, 3, mHeroProbeCount + 1, true);
+
+ if (mHeroProbe.isNull()) {
+ mHeroProbe = new LLReflectionMap();
+ }
+
+ mHeroProbe->mCubeIndex = 0;
+ mHeroProbe->mCubeArray = mHeroArray;
+ mHeroProbe->mDistance = 64.f;
+ mHeroProbe->mRadius = 4096.f;
+ mHeroProbe->mProbeIndex = 0;
+ touch_default_probe(mHeroProbe);
+
+ }
+
+ if (mVertexBuffer.isNull())
+ {
+ U32 mask = LLVertexBuffer::MAP_VERTEX;
+ LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask);
+ buff->allocateBuffer(4, 0);
+
+ LLStrider<LLVector3> v;
+
+ buff->getVertexStrider(v);
+
+ v[0] = LLVector3(-1, -1, -1);
+ v[1] = LLVector3(1, -1, -1);
+ v[2] = LLVector3(-1, 1, -1);
+ v[3] = LLVector3(1, 1, -1);
+
+ buff->unmapBuffer();
+
+ mVertexBuffer = buff;
+ }
+}
+
+void LLHeroProbeManager::cleanup()
+{
+ mVertexBuffer = nullptr;
+ mRenderTarget.release();
+ mHeroRenderTarget.release();
+
+ mMipChain.clear();
+
+ mTexture = nullptr;
+ mIrradianceMaps = nullptr;
+ mHeroArray = nullptr;
+
+ mProbes.clear();
+ mKillList.clear();
+ mCreateList.clear();
+
+ mReflectionMaps.clear();
+ mUpdatingFace = 0;
+
+ mDefaultProbe = nullptr;
+ mUpdatingProbe = nullptr;
+
+ mHeroProbe = nullptr;
+
+ glDeleteBuffers(1, &mUBO);
+ mUBO = 0;
+
+ glDeleteBuffers(1, &mHeroUBO);
+ mHeroUBO = 0;
+
+ // note: also called on teleport (not just shutdown), so make sure we're in a good "starting" state
+ initCubeFree();
+}
+
+void LLHeroProbeManager::doOcclusion()
+{
+ LLVector4a eye;
+ eye.load3(LLViewerCamera::instance().getOrigin().mV);
+
+ for (auto& probe : mProbes)
+ {
+ if (probe != nullptr && probe != mDefaultProbe)
+ {
+ probe->doOcclusion(eye);
+ }
+ }
+}
diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h
new file mode 100644
index 0000000000..be761a680e
--- /dev/null
+++ b/indra/newview/llheroprobemanager.h
@@ -0,0 +1,208 @@
+/**
+ * @file llheroprobemanager.h
+ * @brief LLHeroProbeManager class declaration
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include "llreflectionmap.h"
+#include "llrendertarget.h"
+#include "llcubemaparray.h"
+#include "llcubemap.h"
+
+class LLSpatialGroup;
+class LLViewerObject;
+
+// number of reflection probes to keep in vram
+#define LL_MAX_REFLECTION_PROBE_COUNT 256
+
+// reflection probe resolution
+#define LL_IRRADIANCE_MAP_RESOLUTION 64
+
+// reflection probe mininum scale
+#define LL_REFLECTION_PROBE_MINIMUM_SCALE 1.f;
+
+class alignas(16) LLHeroProbeManager
+{
+ LL_ALIGN_NEW
+public:
+ enum class DetailLevel
+ {
+ STATIC_ONLY = 0,
+ STATIC_AND_DYNAMIC,
+ REALTIME = 2
+ };
+
+ // allocate an environment map of the given resolution
+ LLHeroProbeManager();
+
+ // release any GL state
+ void cleanup();
+
+ // maintain reflection probes
+ void update();
+
+ // add a probe for the given spatial group
+ LLReflectionMap* addProbe(LLSpatialGroup* group = nullptr);
+
+ // Populate "maps" with the N most relevant Reflection Maps where N is no more than maps.size()
+ // If less than maps.size() ReflectionMaps are available, will assign trailing elements to nullptr.
+ // maps -- presized array of Reflection Map pointers
+ void getReflectionMaps(std::vector<LLReflectionMap*>& maps);
+
+ // called by LLSpatialGroup constructor
+ // If spatial group should receive a Reflection Probe, will create one for the specified spatial group
+ LLReflectionMap* registerSpatialGroup(LLSpatialGroup* group);
+
+ // presently hacked into LLViewerObject::setTE
+ // Used by LLViewerObjects that are Reflection Probes
+ // vobj must not be null
+ // Guaranteed to not return null
+ LLReflectionMap* registerViewerObject(LLViewerObject* vobj);
+
+ // reset all state on the next update
+ void reset();
+
+ // called on region crossing to "shift" probes into new coordinate frame
+ void shift(const LLVector4a& offset);
+
+ // debug display, called from llspatialpartition if reflection
+ // probe debug display is active
+ void renderDebug();
+
+ // call once at startup to allocate cubemap arrays
+ void initReflectionMaps();
+
+ // True if currently updating a radiance map, false if currently updating an irradiance map
+ bool isRadiancePass() { return mRadiancePass; }
+
+ // perform occlusion culling on all active reflection probes
+ void doOcclusion();
+
+private:
+ friend class LLPipeline;
+
+ // initialize mCubeFree array to default values
+ void initCubeFree();
+
+ // delete the probe with the given index in mProbes
+ void deleteProbe(U32 i);
+
+ // get a free cube index
+ // returns -1 if allocation failed
+ S32 allocateCubeIndex();
+
+ // update the neighbors of the given probe
+ void updateNeighbors(LLReflectionMap* probe);
+
+ // update UBO used for rendering (call only once per render pipe flush)
+ void updateUniforms();
+
+ // bind UBO used for rendering
+ void setUniforms();
+
+ // render target for cube snapshots
+ // used to generate mipmaps without doing a copy-to-texture
+ LLRenderTarget mRenderTarget;
+
+ LLRenderTarget mHeroRenderTarget;
+
+ std::vector<LLRenderTarget> mMipChain;
+
+ // storage for reflection probe radiance maps (plus two scratch space cubemaps)
+ LLPointer<LLCubeMapArray> mTexture;
+
+ LLPointer<LLCubeMapArray> mHeroArray;
+
+ // vertex buffer for pushing verts to filter shaders
+ LLPointer<LLVertexBuffer> mVertexBuffer;
+
+ // storage for reflection probe irradiance maps
+ LLPointer<LLCubeMapArray> mIrradianceMaps;
+
+ // list of free cubemap indices
+ std::list<S32> mCubeFree;
+
+ // perform an update on the currently updating Probe
+ void doProbeUpdate();
+
+ // update the specified face of the specified probe
+ void updateProbeFace(LLReflectionMap* probe, U32 face, U32 probeResolution, LLPointer<LLCubeMapArray> cubeArray, std::vector<LLRenderTarget> &mipChain, U32 probeCount);
+
+ // list of active reflection maps
+ std::vector<LLPointer<LLReflectionMap> > mProbes;
+
+ // list of reflection maps to kill
+ std::vector<LLPointer<LLReflectionMap> > mKillList;
+
+ // list of reflection maps to create
+ std::vector<LLPointer<LLReflectionMap> > mCreateList;
+
+ // handle to UBO
+ U32 mUBO = 0;
+
+ // Hero UBO
+ U32 mHeroUBO = 0;
+
+ // list of maps being used for rendering
+ std::vector<LLReflectionMap*> mReflectionMaps;
+
+ LLReflectionMap* mUpdatingProbe = nullptr;
+ U32 mUpdatingFace = 0;
+
+ // if true, we're generating the radiance map for the current probe, otherwise we're generating the irradiance map.
+ // Update sequence should be to generate the irradiance map from render of the world that has no irradiance,
+ // then generate the radiance map from a render of the world that includes irradiance.
+ // This should avoid feedback loops and ensure that the colors in the radiance maps match the colors in the environment.
+ bool mRadiancePass = false;
+
+ // same as above, but for the realtime probe.
+ // Realtime probes should update all six sides of the irradiance map on "odd" frames and all six sides of the
+ // radiance map on "even" frames.
+ bool mRealtimeRadiancePass = false;
+
+ LLPointer<LLReflectionMap> mDefaultProbe; // default reflection probe to fall back to for pixels with no probe influences (should always be at cube index 0)
+
+ LLPointer<LLReflectionMap> mHeroProbe;
+
+ // number of reflection probes to use for rendering
+ U32 mReflectionProbeCount;
+
+ U32 mHeroProbeCount;
+
+ // resolution of reflection probes
+ U32 mProbeResolution = 128;
+
+ U32 mHeroProbeResolution = 512;
+
+ // maximum LoD of reflection probes (mip levels - 1)
+ F32 mMaxProbeLOD = 6.f;
+
+ // amount to scale local lights during an irradiance map update (set during updateProbeFace and used by LLPipeline)
+ F32 mLightScale = 1.f;
+
+ // if true, reset all probe render state on the next update (for teleports and sky changes)
+ bool mReset = false;
+};
+
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index ad4d3d94e2..e855be8fbd 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -130,17 +130,10 @@ void LLReflectionMapManager::update()
U32 targetRes = mProbeResolution * 4; // super sample
mRenderTarget.allocate(targetRes, targetRes, color_fmt, true);
}
-
- if (!mHeroRenderTarget.isComplete())
- {
- U32 color_fmt = GL_RGB16F;
- U32 targetRes = mHeroProbeResolution * 2;
- mHeroRenderTarget.allocate(targetRes, targetRes, color_fmt, true);
- }
if (mMipChain.empty())
{
- U32 res = mHeroProbeResolution;
+ U32 res = mProbeResolution;
U32 count = log2((F32)res) + 0.5f;
mMipChain.resize(count);
@@ -304,8 +297,8 @@ void LLReflectionMapManager::update()
}
}
- if (realtime &&
- closestDynamic == nullptr &&
+ if (realtime &&
+ closestDynamic == nullptr &&
probe->mCubeIndex != -1 &&
probe->getIsDynamic())
{
@@ -320,35 +313,19 @@ void LLReflectionMapManager::update()
// should do a full irradiance pass on "odd" frames and a radiance pass on "even" frames
closestDynamic->autoAdjustOrigin();
- // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set
+ // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set
// lighting values etc
bool radiance_pass = isRadiancePass();
mRadiancePass = mRealtimeRadiancePass;
for (U32 i = 0; i < 6; ++i)
{
- updateProbeFace(closestDynamic, i, mProbeResolution, mTexture, mMipChain, mReflectionProbeCount);
+ updateProbeFace(closestDynamic, i);
}
mRealtimeRadiancePass = !mRealtimeRadiancePass;
// restore "isRadiancePass"
mRadiancePass = radiance_pass;
}
-
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmu - realtime");
-
- mHeroProbe->mOrigin.load3(LLViewerCamera::instance().mOrigin.mV);
- bool radiance_pass = isRadiancePass();
- mRadiancePass = true;
- for (U32 i = 0; i < 6; ++i)
- {
- updateProbeFace(mHeroProbe, i, mHeroProbeResolution, mHeroArray, mMipChain, mHeroProbeCount);
- }
-
- // restore "isRadiancePass"
- mRadiancePass = radiance_pass;
- }
static LLCachedControl<F32> sUpdatePeriod(gSavedSettings, "RenderDefaultProbeUpdatePeriod", 2.f);
if ((gFrameTimeSeconds - mDefaultProbe->mLastUpdateTime) < sUpdatePeriod)
@@ -555,7 +532,7 @@ void LLReflectionMapManager::doProbeUpdate()
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
llassert(mUpdatingProbe != nullptr);
- updateProbeFace(mUpdatingProbe, mUpdatingFace, mProbeResolution, mTexture, mMipChain, mReflectionProbeCount);
+ updateProbeFace(mUpdatingProbe, mUpdatingFace);
if (++mUpdatingFace == 6)
{
@@ -576,26 +553,16 @@ void LLReflectionMapManager::doProbeUpdate()
// Do the reflection map update render passes.
// For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated
-// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate
+// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate
// a simple mip chain (not convolution filter).
// At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe
// The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain.
// At the end of these passes, a radiance map is generated for this probe and placed into the radiance cube map array at the index for this probe.
// In effect this simulates single-bounce lighting.
-void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U32 probeResolution, LLPointer<LLCubeMapArray> cubeArray, std::vector<LLRenderTarget> &mipChain, U32 probeCount)
+void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face)
{
// hacky hot-swap of camera specific render targets
gPipeline.mRT = &gPipeline.mAuxillaryRT;
-
- LLRenderTarget* target = &mRenderTarget;
-
- S32 sourceIdx = probeCount;
-
- if (probeResolution == mHeroProbeResolution)
- {
- sourceIdx = 0;
- target = &mHeroRenderTarget;
- }
mLightScale = 1.f;
static LLCachedControl<F32> max_local_light_ambiance(gSavedSettings, "RenderReflectionProbeMaxLocalLightAmbiance", 8.f);
@@ -614,18 +581,20 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, LLPipeline::RENDER_TYPE_WL_SKY,
LLPipeline::RENDER_TYPE_WATER, LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_CLOUDS, LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::END_RENDER_TYPES);
- probe->update(target->getWidth(), face);
+ probe->update(mRenderTarget.getWidth(), face);
gPipeline.popRenderTypeMask();
}
else
{
- probe->update(target->getWidth(), face);
+ probe->update(mRenderTarget.getWidth(), face);
}
gPipeline.mRT = &gPipeline.mMainRT;
- if (probe != mUpdatingProbe && probe->mType != LLReflectionMap::ProbeType::REFLECTION)
+ S32 sourceIdx = mReflectionProbeCount;
+
+ if (probe != mUpdatingProbe)
{ // this is the "realtime" probe that's updating every frame, use the secondary scratch space channel
sourceIdx += 1;
}
@@ -646,7 +615,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
gGL.loadIdentity();
gGL.flush();
- U32 res = probeResolution * 2;
+ U32 res = mProbeResolution * 2;
static LLStaticHashedString resScale("resScale");
static LLStaticHashedString direction("direction");
@@ -658,20 +627,20 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
// perform a gaussian blur on the super sampled render before downsampling
{
gGaussianProgram.bind();
- gGaussianProgram.uniform1f(resScale, 1.f / (probeResolution * 2));
+ gGaussianProgram.uniform1f(resScale, 1.f / (mProbeResolution * 2));
S32 diffuseChannel = gGaussianProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
// horizontal
gGaussianProgram.uniform2f(direction, 1.f, 0.f);
gGL.getTexUnit(diffuseChannel)->bind(screen_rt);
- target->bindTarget();
+ mRenderTarget.bindTarget();
gPipeline.mScreenTriangleVB->setBuffer();
gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- target->flush();
+ mRenderTarget.flush();
// vertical
gGaussianProgram.uniform2f(direction, 0.f, 1.f);
- gGL.getTexUnit(diffuseChannel)->bind(target);
+ gGL.getTexUnit(diffuseChannel)->bind(&mRenderTarget);
screen_rt->bindTarget();
gPipeline.mScreenTriangleVB->setBuffer();
gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
@@ -679,7 +648,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
}
- S32 mips = log2((F32)probeResolution) + 0.5f;
+ S32 mips = log2((F32)mProbeResolution) + 0.5f;
gReflectionMipProgram.bind();
S32 diffuseChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
@@ -698,7 +667,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
}
- gReflectionMipProgram.uniform1f(resScale, 1.f/(probeResolution*2));
+ gReflectionMipProgram.uniform1f(resScale, 1.f/(mProbeResolution*2));
gPipeline.mScreenTriangleVB->setBuffer();
gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
@@ -710,11 +679,14 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
if (mip >= 0)
{
LL_PROFILE_GPU_ZONE("probe mip copy");
- cubeArray->bind(0);
-
+ mTexture->bind(0);
+ //glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, probe->mCubeIndex * 6 + face, 0, 0, res, res);
glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, sourceIdx * 6 + face, 0, 0, res, res);
-
- cubeArray->unbind();
+ //if (i == 0)
+ //{
+ //glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, probe->mCubeIndex * 6 + face, 0, 0, res, res);
+ //}
+ mTexture->unbind();
}
mMipChain[i].flush();
}
@@ -739,7 +711,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
mVertexBuffer->setBuffer();
S32 channel = gRadianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
- cubeArray->bind(channel);
+ mTexture->bind(channel);
gRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
@@ -754,7 +726,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
gRadianceGenProgram.uniform1f(sRoughness, (F32)i / (F32)(mMipChain.size() - 1));
gRadianceGenProgram.uniform1f(sMipLevel, i);
- gRadianceGenProgram.uniform1i(sWidth, probeResolution);
+ gRadianceGenProgram.uniform1i(sWidth, mProbeResolution);
for (int cf = 0; cf < 6; ++cf)
{ // for each cube face
@@ -784,7 +756,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
//generate irradiance map
gIrradianceGenProgram.bind();
S32 channel = gIrradianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
- cubeArray->bind(channel);
+ mTexture->bind(channel);
gIrradianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
gIrradianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
@@ -819,7 +791,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face, U
S32 res = mMipChain[i].getWidth();
mIrradianceMaps->bind(channel);
glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i - start_mip, 0, 0, probe->mCubeIndex * 6 + cf, 0, 0, res, res);
- cubeArray->bind(channel);
+ mTexture->bind(channel);
}
}
}
@@ -896,14 +868,14 @@ void LLReflectionMapManager::updateUniforms()
// see class3/deferred/reflectionProbeF.glsl
struct ReflectionProbeData
{
- // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of
+ // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of
// the box probe
- LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT];
+ LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT];
// for sphere probes, origin (xyz) and radius (w) of refmaps in clip space
- LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
+ LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
- // extra parameters
+ // extra parameters
// x - irradiance scale
// y - radiance scale
// z - fade in
@@ -915,10 +887,10 @@ void LLReflectionMapManager::updateUniforms()
// [i][1] - index into "refNeighbor" for probes that intersect this probe
// [i][2] - number of probes that intersect this probe, or -1 for no neighbors
// [i][3] - priority (probe type stored in sign bit - positive for spheres, negative for boxes)
- GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4];
+ GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4];
// list of neighbor indices
- GLint refNeighbor[4096];
+ GLint refNeighbor[4096];
GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth
// numbrer of active refmaps
@@ -1101,7 +1073,7 @@ void LLReflectionMapManager::updateUniforms()
#endif
rpd.refmapCount = count;
-
+
//copy rpd into uniform buffer object
if (mUBO == 0)
{
@@ -1114,35 +1086,7 @@ void LLReflectionMapManager::updateUniforms()
glBufferData(GL_UNIFORM_BUFFER, sizeof(ReflectionProbeData), &rpd, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
-
- struct HeroProbeData
- {
- LLVector4 heroPosition[1];
- GLint heroProbeCount = 1;
- };
-
- HeroProbeData hpd;
-
- modelview.loadu(gGLModelView);
-
- oa.set(0, 0, 0, 0);
- hpd.heroProbeCount = 1;
- modelview.affineTransform(mHeroProbe->mOrigin, oa);
- hpd.heroPosition[0].set(oa.getF32ptr());
- //copy rpd into uniform buffer object
- if (mUBO == 0)
- {
- glGenBuffers(1, &mHeroUBO);
- }
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer");
- glBindBuffer(GL_UNIFORM_BUFFER, mHeroUBO);
- glBufferData(GL_UNIFORM_BUFFER, sizeof(HeroProbeData), &hpd, GL_STREAM_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
- }
-
#if 0
if (!gCubeSnapshot)
{
@@ -1167,7 +1111,7 @@ void LLReflectionMapManager::setUniforms()
}
if (mUBO == 0)
- {
+ {
updateUniforms();
}
glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
@@ -1275,7 +1219,7 @@ void LLReflectionMapManager::initReflectionMaps()
{
U32 count = LL_MAX_REFLECTION_PROBE_COUNT;
- if (mTexture.isNull() || mReflectionProbeCount != count || mReset || mHeroArray.isNull())
+ if (mTexture.isNull() || mReflectionProbeCount != count || mReset)
{
mReset = false;
mReflectionProbeCount = count;
@@ -1324,27 +1268,6 @@ void LLReflectionMapManager::initReflectionMaps()
mDefaultProbe->mRadius = 4096.f;
mDefaultProbe->mProbeIndex = 0;
touch_default_probe(mDefaultProbe);
-
- mHeroProbeResolution = 128;
-
- // Revise when we have both water and mirrors in hero probes.
- mHeroProbeCount = 1;
-
- mHeroArray = new LLCubeMapArray();
-
- // We use an extra probe for scratch space on these.
- mHeroArray->allocate(mHeroProbeResolution, 3, mHeroProbeCount + 1, true);
-
- if (mHeroProbe.isNull()) {
- mHeroProbe = new LLReflectionMap();
- }
-
- mHeroProbe->mCubeIndex = 0;
- mHeroProbe->mCubeArray = mHeroArray;
- mHeroProbe->mDistance = 64.f;
- mHeroProbe->mRadius = 4096.f;
- mHeroProbe->mProbeIndex = 0;
- touch_default_probe(mHeroProbe);
}
@@ -1369,17 +1292,15 @@ void LLReflectionMapManager::initReflectionMaps()
}
}
-void LLReflectionMapManager::cleanup()
-{
+void LLReflectionMapManager::cleanup()
+{
mVertexBuffer = nullptr;
mRenderTarget.release();
- mHeroRenderTarget.release();
mMipChain.clear();
mTexture = nullptr;
mIrradianceMaps = nullptr;
- mHeroArray = nullptr;
mProbes.clear();
mKillList.clear();
@@ -1390,14 +1311,9 @@ void LLReflectionMapManager::cleanup()
mDefaultProbe = nullptr;
mUpdatingProbe = nullptr;
-
- mHeroProbe = nullptr;
glDeleteBuffers(1, &mUBO);
mUBO = 0;
-
- glDeleteBuffers(1, &mHeroUBO);
- mHeroUBO = 0;
// note: also called on teleport (not just shutdown), so make sure we're in a good "starting" state
initCubeFree();
diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h
index 9f48ce3cfd..7b17112814 100644
--- a/indra/newview/llreflectionmapmanager.h
+++ b/indra/newview/llreflectionmapmanager.h
@@ -43,21 +43,23 @@ class LLViewerObject;
// reflection probe mininum scale
#define LL_REFLECTION_PROBE_MINIMUM_SCALE 1.f;
+void renderReflectionProbe(LLReflectionMap* probe);
+
class alignas(16) LLReflectionMapManager
{
LL_ALIGN_NEW
public:
- enum class DetailLevel
+ enum class DetailLevel
{
STATIC_ONLY = 0,
STATIC_AND_DYNAMIC,
REALTIME = 2
};
- // allocate an environment map of the given resolution
+ // allocate an environment map of the given resolution
LLReflectionMapManager();
- // release any GL state
+ // release any GL state
void cleanup();
// maintain reflection probes
@@ -113,7 +115,7 @@ private:
// returns -1 if allocation failed
S32 allocateCubeIndex();
- // update the neighbors of the given probe
+ // update the neighbors of the given probe
void updateNeighbors(LLReflectionMap* probe);
// update UBO used for rendering (call only once per render pipe flush)
@@ -125,15 +127,11 @@ private:
// render target for cube snapshots
// used to generate mipmaps without doing a copy-to-texture
LLRenderTarget mRenderTarget;
-
- LLRenderTarget mHeroRenderTarget;
std::vector<LLRenderTarget> mMipChain;
// storage for reflection probe radiance maps (plus two scratch space cubemaps)
LLPointer<LLCubeMapArray> mTexture;
-
- LLPointer<LLCubeMapArray> mHeroArray;
// vertex buffer for pushing verts to filter shaders
LLPointer<LLVertexBuffer> mVertexBuffer;
@@ -148,7 +146,7 @@ private:
void doProbeUpdate();
// update the specified face of the specified probe
- void updateProbeFace(LLReflectionMap* probe, U32 face, U32 probeResolution, LLPointer<LLCubeMapArray> cubeArray, std::vector<LLRenderTarget> &mipChain, U32 probeCount);
+ void updateProbeFace(LLReflectionMap* probe, U32 face);
// list of active reflection maps
std::vector<LLPointer<LLReflectionMap> > mProbes;
@@ -161,9 +159,6 @@ private:
// handle to UBO
U32 mUBO = 0;
-
- // Hero UBO
- U32 mHeroUBO = 0;
// list of maps being used for rendering
std::vector<LLReflectionMap*> mReflectionMaps;
@@ -183,18 +178,12 @@ private:
bool mRealtimeRadiancePass = false;
LLPointer<LLReflectionMap> mDefaultProbe; // default reflection probe to fall back to for pixels with no probe influences (should always be at cube index 0)
-
- LLPointer<LLReflectionMap> mHeroProbe;
// number of reflection probes to use for rendering
U32 mReflectionProbeCount;
-
- U32 mHeroProbeCount;
// resolution of reflection probes
U32 mProbeResolution = 128;
-
- U32 mHeroProbeResolution = 512;
// maximum LoD of reflection probes (mip levels - 1)
F32 mMaxProbeLOD = 6.f;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index e742992c1d..909c0f9aaa 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -8461,13 +8461,14 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader)
mReflectionMapManager.mIrradianceMaps->bind(channel);
bound = true;
}
-
+ /*
channel = shader.enableTexture(LLShaderMgr::HERO_PROBE, LLTexUnit::TT_CUBE_MAP_ARRAY);
if (channel > -1 && mReflectionMapManager.mHeroArray.notNull())
{
mReflectionMapManager.mHeroArray->bind(channel);
bound = true;
}
+ */
if (bound)
{