secondlife/viewer-issues#43: Proof-of-concept PBR terrain normal gen feature flag. Final implementation would likely be very different.

meow-7.2.2
Cosmic Linden 2023-09-29 17:19:48 -07:00
parent 00c65b6270
commit 118996225b
12 changed files with 292 additions and 24 deletions

View File

@ -10932,6 +10932,17 @@
<key>Value</key>
<real>8.0</real>
</map>
<key>RenderTerrainPBRNormalsEnabled</key>
<map>
<key>Comment</key>
<string>EXPERIMENTAL: Change normal gen for PBR Terrain.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderTrackerBeacon</key>
<map>
<key>Comment</key>

View File

@ -643,17 +643,18 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
}
}
BOOL LLSurface::idleUpdate(F32 max_update_time)
template<bool PBR>
bool LLSurface::idleUpdate(F32 max_update_time)
{
if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
{
return FALSE;
return false;
}
// Perform idle time update of non-critical stuff.
// In this case, texture and normal updates.
LLTimer update_timer;
BOOL did_update = FALSE;
bool did_update = false;
// If the Z height data has changed, we need to rebuild our
// property line vertex arrays.
@ -669,13 +670,13 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
{
std::set<LLSurfacePatch *>::iterator curiter = iter++;
LLSurfacePatch *patchp = *curiter;
patchp->updateNormals();
patchp->updateNormals<PBR>();
patchp->updateVerticalStats();
if (max_update_time == 0.f || update_timer.getElapsedTimeF32() < max_update_time)
{
if (patchp->updateTexture())
{
did_update = TRUE;
did_update = true;
patchp->clearDirty();
mDirtyPatchList.erase(curiter);
}
@ -691,6 +692,9 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
return did_update;
}
template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
void LLSurface::decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch)
{

View File

@ -112,7 +112,8 @@ public:
LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
// Update methods (called during idle, normally)
BOOL idleUpdate(F32 max_update_time);
template<bool PBR>
bool idleUpdate(F32 max_update_time);
BOOL containsPosition(const LLVector3 &position);
@ -224,6 +225,9 @@ private:
static S32 sTextureSize; // Size of the surface texture
};
extern template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
extern template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
// . __.

View File

@ -243,7 +243,8 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3
}
void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
template<>
void LLSurfacePatch::calcNormal</*PBR=*/false>(const U32 x, const U32 y, const U32 stride)
{
U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
U32 surface_stride = mSurfacep->getGridsPerEdge();
@ -356,6 +357,166 @@ void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
*(mDataNorm + surface_stride * y + x) = normal;
}
template<>
void LLSurfacePatch::calcNormal</*PBR=*/true>(const U32 x, const U32 y, const U32 stride)
{
llassert(mDataNorm);
constexpr U32 index = 0;
const U32 surface_stride = mSurfacep->getGridsPerEdge();
LLVector3& normal_out = *(mDataNorm + surface_stride * y + x);
calcNormalFlat(normal_out, x, y, index);
}
// Calculate the flat normal of a triangle whose least coordinate is specified by the given x,y values.
// If index = 0, calculate the normal of the first triangle, otherwise calculate the normal of the second.
void LLSurfacePatch::calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index)
{
llassert(index == 0 || index == 1);
U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
U32 surface_stride = mSurfacep->getGridsPerEdge();
// Vertex stride is always 1 because we want the flat surface of the current triangle face
constexpr U32 stride = 1;
const F32 mpg = mSurfacep->getMetersPerGrid() * stride;
S32 poffsets[2][2][2];
poffsets[0][0][0] = x;
poffsets[0][0][1] = y;
poffsets[0][1][0] = x;
poffsets[0][1][1] = y + stride;
poffsets[1][0][0] = x + stride;
poffsets[1][0][1] = y;
poffsets[1][1][0] = x + stride;
poffsets[1][1][1] = y + stride;
const LLSurfacePatch *ppatches[2][2];
// LLVector3 p1, p2, p3, p4;
ppatches[0][0] = this;
ppatches[0][1] = this;
ppatches[1][0] = this;
ppatches[1][1] = this;
U32 i, j;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 2; j++)
{
if (poffsets[i][j][0] < 0)
{
if (!ppatches[i][j]->getNeighborPatch(WEST))
{
poffsets[i][j][0] = 0;
}
else
{
poffsets[i][j][0] += patch_width;
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(WEST);
}
}
if (poffsets[i][j][1] < 0)
{
if (!ppatches[i][j]->getNeighborPatch(SOUTH))
{
poffsets[i][j][1] = 0;
}
else
{
poffsets[i][j][1] += patch_width;
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(SOUTH);
}
}
if (poffsets[i][j][0] >= (S32)patch_width)
{
if (!ppatches[i][j]->getNeighborPatch(EAST))
{
poffsets[i][j][0] = patch_width - 1;
}
else
{
poffsets[i][j][0] -= patch_width;
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(EAST);
}
}
if (poffsets[i][j][1] >= (S32)patch_width)
{
if (!ppatches[i][j]->getNeighborPatch(NORTH))
{
poffsets[i][j][1] = patch_width - 1;
}
else
{
poffsets[i][j][1] -= patch_width;
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(NORTH);
}
}
}
}
LLVector3 p00(-mpg,-mpg,
*(ppatches[0][0]->mDataZ
+ poffsets[0][0][0]
+ poffsets[0][0][1]*surface_stride));
LLVector3 p01(-mpg,+mpg,
*(ppatches[0][1]->mDataZ
+ poffsets[0][1][0]
+ poffsets[0][1][1]*surface_stride));
LLVector3 p10(+mpg,-mpg,
*(ppatches[1][0]->mDataZ
+ poffsets[1][0][0]
+ poffsets[1][0][1]*surface_stride));
LLVector3 p11(+mpg,+mpg,
*(ppatches[1][1]->mDataZ
+ poffsets[1][1][0]
+ poffsets[1][1][1]*surface_stride));
// Triangle index / coordinate convention
// for a single surface patch
//
// p01 p11
//
// ^ ._____.
// | |\ |
// | | \ 1 |
// | | \ |
// | 0 \ |
// y |____\|
//
// p00 x ---> p10
//
// (z up / out of the screen due to right-handed coordinate system)
LLVector3 normal;
if (index == 0)
{
LLVector3 c1 = p10 - p00;
LLVector3 c2 = p01 - p00;
normal = c1;
normal %= c2;
normal.normVec();
}
else // index == 1
{
LLVector3 c1 = p11 - p01;
LLVector3 c2 = p11 - p10;
normal = c1;
normal %= c2;
normal.normVec();
}
llassert(&normal_out);
normal_out = normal;
}
const LLVector3 &LLSurfacePatch::getNormal(const U32 x, const U32 y) const
{
U32 surface_stride = mSurfacep->getGridsPerEdge();
@ -453,6 +614,7 @@ void LLSurfacePatch::updateVerticalStats()
}
template<bool PBR>
void LLSurfacePatch::updateNormals()
{
if (mSurfacep->mType == 'w')
@ -470,9 +632,9 @@ void LLSurfacePatch::updateNormals()
{
for (j = 0; j <= grids_per_patch_edge; j++)
{
calcNormal(grids_per_patch_edge, j, 2);
calcNormal(grids_per_patch_edge - 1, j, 2);
calcNormal(grids_per_patch_edge - 2, j, 2);
calcNormal<PBR>(grids_per_patch_edge, j, 2);
calcNormal<PBR>(grids_per_patch_edge - 1, j, 2);
calcNormal<PBR>(grids_per_patch_edge - 2, j, 2);
}
dirty_patch = TRUE;
@ -483,9 +645,9 @@ void LLSurfacePatch::updateNormals()
{
for (i = 0; i <= grids_per_patch_edge; i++)
{
calcNormal(i, grids_per_patch_edge, 2);
calcNormal(i, grids_per_patch_edge - 1, 2);
calcNormal(i, grids_per_patch_edge - 2, 2);
calcNormal<PBR>(i, grids_per_patch_edge, 2);
calcNormal<PBR>(i, grids_per_patch_edge - 1, 2);
calcNormal<PBR>(i, grids_per_patch_edge - 2, 2);
}
dirty_patch = TRUE;
@ -496,8 +658,8 @@ void LLSurfacePatch::updateNormals()
{
for (j = 0; j < grids_per_patch_edge; j++)
{
calcNormal(0, j, 2);
calcNormal(1, j, 2);
calcNormal<PBR>(0, j, 2);
calcNormal<PBR>(1, j, 2);
}
dirty_patch = TRUE;
}
@ -507,8 +669,8 @@ void LLSurfacePatch::updateNormals()
{
for (i = 0; i < grids_per_patch_edge; i++)
{
calcNormal(i, 0, 2);
calcNormal(i, 1, 2);
calcNormal<PBR>(i, 0, 2);
calcNormal<PBR>(i, 1, 2);
}
dirty_patch = TRUE;
}
@ -584,10 +746,10 @@ void LLSurfacePatch::updateNormals()
// We've got a northeast patch in the same surface.
// The z and normals will be handled by that patch.
}
calcNormal(grids_per_patch_edge, grids_per_patch_edge, 2);
calcNormal(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge, 2);
calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
dirty_patch = TRUE;
}
@ -598,7 +760,7 @@ void LLSurfacePatch::updateNormals()
{
for (i=2; i < grids_per_patch_edge - 2; i++)
{
calcNormal(i, j, 2);
calcNormal<PBR>(i, j, 2);
}
}
dirty_patch = TRUE;
@ -615,6 +777,9 @@ void LLSurfacePatch::updateNormals()
}
}
template void LLSurfacePatch::updateNormals</*PBR=*/false>();
template void LLSurfacePatch::updateNormals</*PBR=*/true>();
void LLSurfacePatch::updateEastEdge()
{
U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();

View File

@ -77,6 +77,7 @@ public:
void updateVerticalStats();
void updateCompositionStats();
template<bool PBR>
void updateNormals();
void updateEastEdge();
@ -102,9 +103,18 @@ public:
LLVector3 getPointAgent(const U32 x, const U32 y) const; // get the point at the offset.
LLVector2 getTexCoords(const U32 x, const U32 y) const;
// Per-vertex normals
// *TODO: PBR=true is a test implementation solely for proof-of-concept.
// Final implementation would likely be very different and may not even use
// this function. If we decide to keep calcNormalFlat, remove index as it
// is a debug parameter for testing.
template<bool PBR>
void calcNormal(const U32 x, const U32 y, const U32 stride);
const LLVector3 &getNormal(const U32 x, const U32 y) const;
// Per-triangle normals for flat edges
void calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index /* 0 or 1 */);
void eval(const U32 x, const U32 y, const U32 stride,
LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex0, LLVector2 *tex1);
@ -181,5 +191,8 @@ protected:
LLSurface *mSurfacep; // Pointer to "parent" surface
};
extern template void LLSurfacePatch::updateNormals</*PBR=*/false>();
extern template void LLSurfacePatch::updateNormals</*PBR=*/true>();
#endif // LL_LLSURFACEPATCH_H

View File

@ -2122,6 +2122,20 @@ class LLAdvancedPurgeShaderCache : public view_listener_t
}
};
/////////////////////
// REBUILD TERRAIN //
/////////////////////
class LLAdvancedRebuildTerrain : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
gPipeline.rebuildTerrain();
return true;
}
};
////////////////////
// EVENT Recorder //
///////////////////
@ -9492,6 +9506,10 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile");
view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
if (gSavedSettings.get<bool>("RenderTerrainPBREnabled"))
{
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
}
#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode");

View File

@ -1105,6 +1105,11 @@ void LLViewerRegion::dirtyHeights()
}
}
void LLViewerRegion::dirtyAllPatches()
{
getLand().dirtyAllPatches();
}
//physically delete the cache entry
void LLViewerRegion::killCacheEntry(LLVOCacheEntry* entry, bool for_rendering)
{
@ -1605,7 +1610,19 @@ void LLViewerRegion::idleUpdate(F32 max_update_time)
mLastUpdate = LLViewerOctreeEntryData::getCurrentFrame();
mImpl->mLandp->idleUpdate(max_update_time);
static bool pbr_terrain_enabled = gSavedSettings.get<bool>("RenderTerrainPBREnabled");
static LLCachedControl<bool> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", FALSE);
bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR);
bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals;
if (!pbr_land)
{
mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time);
}
else
{
mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time);
}
if (mParcelOverlay)
{
@ -1906,7 +1923,21 @@ LLViewerObject* LLViewerRegion::updateCacheEntry(U32 local_id, LLViewerObject* o
// As above, but forcibly do the update.
void LLViewerRegion::forceUpdate()
{
mImpl->mLandp->idleUpdate(0.f);
constexpr F32 max_update_time = 0.f;
static bool pbr_terrain_enabled = gSavedSettings.get<BOOL>("RenderTerrainPBREnabled");
static LLCachedControl<BOOL> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", FALSE);
bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR);
bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals;
if (!pbr_land)
{
mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time);
}
else
{
mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time);
}
if (mParcelOverlay)
{

View File

@ -163,6 +163,9 @@ public:
// Call this whenever you change the height data in the region.
// (Automatically called by LLSurfacePatch's update routine)
void dirtyHeights();
// Call this whenever you want to force all terrain to rebuild.
// (For example, if a global terrain config option has changed)
void dirtyAllPatches();
LLViewerParcelOverlay *getParcelOverlay() const
{ return mParcelOverlay; }

View File

@ -214,6 +214,7 @@ BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
void LLVOSurfacePatch::updateFaceSize(S32 idx)
{
LL_PROFILE_ZONE_SCOPED;
if (idx != 0)
{
LL_WARNS() << "Terrain partition requested invalid face!!!" << LL_ENDL;

View File

@ -10870,3 +10870,12 @@ void LLPipeline::rebuildDrawInfo()
}
}
void LLPipeline::rebuildTerrain()
{
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
{
LLViewerRegion* region = *iter;
region->dirtyAllPatches();
}
}

View File

@ -134,6 +134,8 @@ public:
// rebuild all LLVOVolume render batches
void rebuildDrawInfo();
// Rebuild all terrain
void rebuildTerrain();
// Clear LLFace mVertexBuffer pointers
void resetVertexBuffers(LLDrawable* drawable);

View File

@ -3297,6 +3297,13 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClearShaderCache" />
</menu_item_call>
<menu_item_call
enabled="true"
label="Rebuild Terrain"
name="Rebuild Terrain">
<menu_item_call.on_click
function="Advanced.RebuildTerrain" />
</menu_item_call>
<menu_item_separator />
<menu_item_call
enabled="true"