diff options
Diffstat (limited to 'indra/newview')
24 files changed, 1872 insertions, 81 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 776797da7c..1a009967f5 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1383,6 +1383,9 @@ set(viewer_HEADER_FILES VorbisFramework.h ) + list(APPEND viewer_SOURCE_FILES llperfstats.cpp) + list(APPEND viewer_HEADER_FILES llperfstats.h) + source_group("CMake Rules" FILES ViewerInstall.cmake) #build_data.json creation moved to viewer_manifest.py MAINT-6413 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 1877c30dc4..d92632c0a2 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -16776,6 +16776,138 @@ <key>Value</key> <real>50</real> </map> + <key>TargetFPS</key> + <map> + <key>Comment</key> + <string>Desired minimum FPS</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>25</integer> + </map> + <key>AutoTuneFPS</key> + <map> + <key>Comment</key> + <string>Allow the viewer to adjust your settings to achieve target FPS</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>AutoTuneLock</key> + <map> + <key>Comment</key> + <string>When enabled the viewer will dynamically change settings until auto tune is explicitly turned off.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>AllowSelfImpostor</key> + <map> + <key>Comment</key> + <string>Allow own render time to impostor your avatar.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>RenderAvatarMaxART</key> + <map> + <key>Comment</key> + <string>Render Time Limit in microseconds (0.0 = no limit)</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>4.699</real> + </map> + <key>AutoTuneRenderFarClipMin</key> + <map> + <key>Comment</key> + <string>The lowest draw distance that auto tune is allowed to use</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>32.0</real> + </map> + <key>AutoTuneRenderFarClipTarget</key> + <map> + <key>Comment</key> + <string>The draw distance that auto tune will try to achieve</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>256.0</real> + </map> + <key>UserTargetReflections</key> + <map> + <key>Comment</key> + <string>Set by auto tune floater on build</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>4</integer> + </map> + <key>PerfStatsCaptureEnabled</key> + <map> + <key>Comment</key> + <string>Enable/disable render time data to support autotune.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>AutoTuneImpostorByDistEnabled</key> + <map> + <key>Comment</key> + <string>Enable/disable using MaxNonImpostor to limit avatar rendering by distance.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>AutoTuneImpostorFarAwayDistance</key> + <map> + <key>Comment</key> + <string>Avatars beyond this range will automatically be optimized</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>64.0</real> + </map> + <key>TuningFPSStrategy</key> + <map> + <key>Comment</key> + <string>Strategy to use when tuning FPS. 0=Tune avatar rendering only, 1=Tune both avatar and global scene settings.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>0</integer> + </map> <key>CameraOpacity</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/toolbars.xml b/indra/newview/app_settings/toolbars.xml index f3a23edc58..a1c9d6d9ee 100644 --- a/indra/newview/app_settings/toolbars.xml +++ b/indra/newview/app_settings/toolbars.xml @@ -22,6 +22,7 @@ <command name="voice"/> <command name="minimap"/> <command name="snapshot"/> + <command name="performance"/> </left_toolbar> <right_toolbar button_display_mode="icons_only"> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index d61a66c696..a04d21c4b0 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -108,6 +108,7 @@ #include "llscenemonitor.h" #include "llavatarrenderinfoaccountant.h" #include "lllocalbitmaps.h" +#include "llperfstats.h" // Linden library includes #include "llavatarnamecache.h" @@ -1377,82 +1378,91 @@ bool LLAppViewer::frame() bool LLAppViewer::doFrame() { - LL_RECORD_BLOCK_TIME(FTM_FRAME); + LL_RECORD_BLOCK_TIME(FTM_FRAME); + { + // and now adjust the visuals from previous frame. + if(LLPerfStats::tunables.userAutoTuneEnabled && LLPerfStats::tunables.tuningFlag != LLPerfStats::Tunables::Nothing) + { + LLPerfStats::tunables.applyUpdates(); + } + LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_FRAME); if (!LLWorld::instanceExists()) { LLWorld::createInstance(); } - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - LLSD newFrame; - - { - LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df LLTrace"); - if (LLFloaterReg::instanceVisible("block_timers")) + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); + LLSD newFrame; + { + LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); // perf stats { - LLTrace::BlockTimer::processTimes(); - } - - LLTrace::get_frame_recording().nextPeriod(); - LLTrace::BlockTimer::logStats(); - } + LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df LLTrace"); + if (LLFloaterReg::instanceVisible("block_timers")) + { + LLTrace::BlockTimer::processTimes(); + } - LLTrace::get_thread_recorder()->pullFromChildren(); + LLTrace::get_frame_recording().nextPeriod(); + LLTrace::BlockTimer::logStats(); + } - //clear call stack records - LL_CLEAR_CALLSTACKS(); + LLTrace::get_thread_recorder()->pullFromChildren(); - { - LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df processMiscNativeEvents" ) - pingMainloopTimeout("Main:MiscNativeWindowEvents"); + //clear call stack records + LL_CLEAR_CALLSTACKS(); + } + { + { + LLPerfStats::RecordSceneTime T(LLPerfStats::StatType_t::RENDER_IDLE); // ensure we have the entire top scope of frame covered (input event and coro) + LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df processMiscNativeEvents") + pingMainloopTimeout("Main:MiscNativeWindowEvents"); - if (gViewerWindow) - { - LL_RECORD_BLOCK_TIME(FTM_MESSAGES); - gViewerWindow->getWindow()->processMiscNativeEvents(); - } + if (gViewerWindow) + { + LL_RECORD_BLOCK_TIME(FTM_MESSAGES); + gViewerWindow->getWindow()->processMiscNativeEvents(); + } - { - LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df gatherInput" ) - pingMainloopTimeout("Main:GatherInput"); - } + { + LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df gatherInput") + pingMainloopTimeout("Main:GatherInput"); + } - if (gViewerWindow) - { - LL_RECORD_BLOCK_TIME(FTM_MESSAGES); - if (!restoreErrorTrap()) - { - LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; - } + if (gViewerWindow) + { + LL_RECORD_BLOCK_TIME(FTM_MESSAGES); + if (!restoreErrorTrap()) + { + LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; + } - gViewerWindow->getWindow()->gatherInput(); - } + gViewerWindow->getWindow()->gatherInput(); + } - //memory leaking simulation - if (gSimulateMemLeak) - { - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); - if (mem_leak_instance) - { - mem_leak_instance->idle(); - } - } + //memory leaking simulation + if (gSimulateMemLeak) + { + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); + if (mem_leak_instance) + { + mem_leak_instance->idle(); + } + } - { - LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df mainloop" ) - // canonical per-frame event - mainloop.post(newFrame); - } + { + LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df mainloop") + // canonical per-frame event + mainloop.post(newFrame); + } - { - LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df suspend" ) - // give listeners a chance to run - llcoro::suspend(); - // if one of our coroutines threw an uncaught exception, rethrow it now - LLCoros::instance().rethrow(); - } + { + LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df suspend") + // give listeners a chance to run + llcoro::suspend(); + } + } if (!LLApp::isExiting()) { @@ -1470,6 +1480,7 @@ bool LLAppViewer::doFrame() && (gHeadlessClient || !gViewerWindow->getShowProgress()) && !gFocusMgr.focusLocked()) { + LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); joystick->scanJoystick(); gKeyboard->scanKeyboard(); gViewerInput.scanMouse(); @@ -1483,6 +1494,7 @@ bool LLAppViewer::doFrame() } { + LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df idle"); //LL_RECORD_BLOCK_TIME(FTM_IDLE); idle(); } @@ -1517,13 +1529,14 @@ bool LLAppViewer::doFrame() display(); - { - LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Snapshot" ) - pingMainloopTimeout("Main:Snapshot"); - LLFloaterSnapshot::update(); // take snapshots - LLFloaterOutfitSnapshot::update(); - gGLActive = FALSE; - } + { + LLPerfStats::RecordSceneTime T(LLPerfStats::StatType_t::RENDER_IDLE); + LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Snapshot" ) + pingMainloopTimeout("Main:Snapshot"); + LLFloaterSnapshot::update(); // take snapshots + LLFloaterOutfitSnapshot::update(); + gGLActive = FALSE; + } } } @@ -1569,7 +1582,8 @@ bool LLAppViewer::doFrame() // of equal priority on Windows if (milliseconds_to_sleep > 0) { - ms_sleep(milliseconds_to_sleep); + LLPerfStats::RecordSceneTime T ( LLPerfStats::StatType_t::RENDER_SLEEP ); + ms_sleep(milliseconds_to_sleep); // also pause worker threads during this wait period LLAppViewer::getTextureCache()->pause(); LLAppViewer::getImageDecodeThread()->pause(); @@ -1670,7 +1684,7 @@ bool LLAppViewer::doFrame() LL_INFOS() << "Exiting main_loop" << LL_ENDL; } - + }LLPerfStats::StatsRecorder::endFrame(); LL_PROFILER_FRAME_END return ! LLApp::isRunning(); diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index a3837fe10c..594cfe513d 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -52,6 +52,7 @@ #include "llglcommonfunc.h" #include "llvoavatar.h" #include "llviewershadermgr.h" +#include "llperfstats.h" S32 LLDrawPool::sNumDrawPools = 0; @@ -391,13 +392,22 @@ void LLRenderPass::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL t { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[type]; - + + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; // Perf stats for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) { LLDrawInfo *pparams = *k; if (pparams) { - pushBatch(*pparams, mask, texture); + if(pparams->mFace) + { + LLViewerObject* vobj = pparams->mFace->getViewerObject(); + if(vobj->isAttachment()) + { + trackAttachments(vobj, false, &ratPtr); + } + } + pushBatch(*pparams, mask, texture); } } } @@ -410,11 +420,21 @@ void LLRenderPass::renderRiggedGroup(LLSpatialGroup* group, U32 type, U32 mask, U64 lastMeshId = 0; mask |= LLVertexBuffer::MAP_WEIGHT4; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; // Perf stats for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) { LLDrawInfo* pparams = *k; if (pparams) { + if(pparams->mFace) + { + LLViewerObject* vobj = pparams->mFace->getViewerObject(); + if(vobj->isAttachment()) + { + trackAttachments( vobj, true ,&ratPtr); + } + } + if (lastAvatar != pparams->mAvatar || lastMeshId != pparams->mSkinInfo->mHash) { uploadMatrixPalette(*pparams); @@ -430,12 +450,21 @@ void LLRenderPass::renderRiggedGroup(LLSpatialGroup* group, U32 type, U32 mask, void LLRenderPass::pushBatches(U32 type, U32 mask, BOOL texture, BOOL batch_textures) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i) { LLDrawInfo* pparams = *i; if (pparams) { - pushBatch(*pparams, mask, texture, batch_textures); + if(pparams->mFace) + { + LLViewerObject* vobj = pparams->mFace->getViewerObject(); + if(vobj->isAttachment()) + { + trackAttachments( vobj, false, &ratPtr); + } + } + pushBatch(*pparams, mask, texture, batch_textures); } } } @@ -446,11 +475,21 @@ void LLRenderPass::pushRiggedBatches(U32 type, U32 mask, BOOL texture, BOOL batc LLVOAvatar* lastAvatar = nullptr; U64 lastMeshId = 0; mask |= LLVertexBuffer::MAP_WEIGHT4; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; // Perf stats for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i) { LLDrawInfo* pparams = *i; if (pparams) { + if(pparams->mFace) + { + LLViewerObject* vobj = pparams->mFace->getViewerObject(); + if(vobj->isAttachment()) + { + trackAttachments( vobj, true, &ratPtr); + } + } + if (pparams->mAvatar.notNull() && (lastAvatar != pparams->mAvatar || lastMeshId != pparams->mSkinInfo->mHash)) { uploadMatrixPalette(*pparams); @@ -466,11 +505,20 @@ void LLRenderPass::pushRiggedBatches(U32 type, U32 mask, BOOL texture, BOOL batc void LLRenderPass::pushMaskBatches(U32 type, U32 mask, BOOL texture, BOOL batch_textures) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i) { LLDrawInfo* pparams = *i; if (pparams) { + if((*pparams).mFace) + { + LLViewerObject* vobj = (*pparams).mFace->getViewerObject(); + if(vobj->isAttachment()) + { + trackAttachments( vobj, false, &ratPtr); + } + } LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(pparams->mAlphaMaskCutoff); pushBatch(*pparams, mask, texture, batch_textures); } @@ -482,11 +530,20 @@ void LLRenderPass::pushRiggedMaskBatches(U32 type, U32 mask, BOOL texture, BOOL LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; LLVOAvatar* lastAvatar = nullptr; U64 lastMeshId = 0; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i) { LLDrawInfo* pparams = *i; if (pparams) { + if((*pparams).mFace) + { + LLViewerObject* vobj = (*pparams).mFace->getViewerObject(); + if(vobj->isAttachment()) + { + trackAttachments( vobj, true, &ratPtr); + } + } if (LLGLSLShader::sCurBoundShaderPtr) { LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(pparams->mAlphaMaskCutoff); diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index e674707c01..de5b9d5062 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -49,6 +49,7 @@ #include "llspatialpartition.h" #include "llglcommonfunc.h" #include "llvoavatar.h" +#include "llperfstats.h" BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE; @@ -327,10 +328,20 @@ void LLDrawPoolAlpha::renderAlphaHighlight(U32 mask) { LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA+pass]; // <-- hacky + pass to use PASS_ALPHA_RIGGED on second pass + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; // Render time Stats collection for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) { LLDrawInfo& params = **k; + if(params.mFace) + { + LLViewerObject* vobj = (LLViewerObject *)params.mFace->getViewerObject(); + if(vobj->isAttachment()) + { + trackAttachments( vobj, params.mFace->isState(LLFace::RIGGED), &ratPtr ); + } + } + if (params.mParticle) { continue; @@ -500,8 +511,16 @@ void LLDrawPoolAlpha::renderRiggedEmissives(U32 mask, std::vector<LLDrawInfo*>& mask |= LLVertexBuffer::MAP_WEIGHT4; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; // Render time Stats collection for (LLDrawInfo* draw : emissives) { + LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("Emissives"); + auto vobj = draw->mFace?draw->mFace->getViewerObject():nullptr; + if(vobj && vobj->isAttachment()) + { + trackAttachments( vobj, draw->mFace->isState(LLFace::RIGGED), &ratPtr ); + } + bool tex_setup = TexSetup(draw, false); if (lastAvatar != draw->mAvatar || lastMeshId != draw->mSkinInfo->mHash) { @@ -566,7 +585,8 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged) LLSpatialGroup::drawmap_elem_t& draw_info = rigged ? group->mDrawMap[LLRenderPass::PASS_ALPHA_RIGGED] : group->mDrawMap[LLRenderPass::PASS_ALPHA]; - for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; // Render time Stats collection + for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) { LLDrawInfo& params = **k; if ((bool)params.mAvatar != rigged) @@ -585,6 +605,16 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged) continue; } + if(params.mFace) + { + LLViewerObject* vobj = (LLViewerObject *)params.mFace->getViewerObject(); + + if(vobj->isAttachment()) + { + trackAttachments( vobj, params.mFace->isState(LLFace::RIGGED), &ratPtr ); + } + } + if(depth_only) { // when updating depth buffer, discard faces that are more than 90% transparent @@ -769,6 +799,8 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged) } } + ratPtr.reset(); // force the final batch to terminate to avoid double counting on the subsidiary batches for FB and Emmissives + // render emissive faces into alpha channel for bloom effects if (!depth_only) { diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 4a9a3caaec..02dea6b828 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -52,6 +52,7 @@ #include "llviewerpartsim.h" #include "llviewercontrol.h" // for gSavedSettings #include "llviewertexturelist.h" +#include "llperfstats.h" static U32 sDataMask = LLDrawPoolAvatar::VERTEX_DATA_MASK; static U32 sBufferUsage = GL_STREAM_DRAW_ARB; @@ -380,9 +381,12 @@ void LLDrawPoolAvatar::renderShadow(S32 pass) { return; } + LLPerfStats::RecordAvatarTime T(avatarp->getID(), LLPerfStats::StatType_t::RENDER_SHADOWS); + LLVOAvatar::AvatarOverallAppearance oa = avatarp->getOverallAppearance(); - BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor(); - if (impostor || (oa == LLVOAvatar::AOA_INVISIBLE)) + BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor(); + // no shadows if the shadows are causing this avatar to breach the limit. + if (avatarp->isTooSlowWithShadows() || impostor || (oa == LLVOAvatar::AOA_INVISIBLE)) { // No shadows for impostored (including jellydolled) or invisible avs. return; @@ -789,6 +793,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) { return; } + LLPerfStats::RecordAvatarTime T(avatarp->getID(), LLPerfStats::StatType_t::RENDER_GEOMETRY); if (!single_avatar && !avatarp->isFullyLoaded() ) { diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 8db6a10e26..09b2400aff 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -48,6 +48,7 @@ #include "llspatialpartition.h" #include "llviewershadermgr.h" #include "llmodel.h" +#include "llperfstats.h" //#include "llimagebmp.h" //#include "../tools/imdebug/imdebug.h" @@ -538,10 +539,18 @@ void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL { LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[type]; - for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; + for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) { LLDrawInfo& params = **k; + LLViewerObject* vobj = (LLViewerObject *)params.mFace->getViewerObject(); + + if( vobj && vobj->isAttachment() ) + { + trackAttachments( vobj, params.mFace->isState(LLFace::RIGGED), &ratPtr ); + } + applyModelMatrix(params); if (params.mGroup) @@ -706,10 +715,21 @@ void LLDrawPoolBump::renderDeferred(S32 pass) LLVOAvatar* avatar = nullptr; U64 skin = 0; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i) { LLDrawInfo& params = **i; + if(params.mFace) + { + LLViewerObject* vobj = (LLViewerObject *)params.mFace->getViewerObject(); + + if(vobj && vobj->isAttachment()) + { + trackAttachments( vobj, params.mFace->isState(LLFace::RIGGED), &ratPtr ); + } + } + LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(params.mAlphaMaskCutoff); LLDrawPoolBump::bindBumpMap(params, bump_channel); @@ -1337,10 +1357,21 @@ void LLDrawPoolBump::renderBump(U32 type, U32 mask) LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type); LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type); + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i) { LLDrawInfo& params = **i; + if(params.mFace) + { + LLViewerObject* vobj = (LLViewerObject *)params.mFace->getViewerObject(); + + if( vobj && vobj->isAttachment() ) + { + trackAttachments( vobj, params.mFace->isState(LLFace::RIGGED), &ratPtr ); + } + } + if (LLDrawPoolBump::bindBumpMap(params)) { if (mRigged) diff --git a/indra/newview/lldrawpoolmaterials.cpp b/indra/newview/lldrawpoolmaterials.cpp index 2b05f4c453..f2408a3294 100644 --- a/indra/newview/lldrawpoolmaterials.cpp +++ b/indra/newview/lldrawpoolmaterials.cpp @@ -32,6 +32,7 @@ #include "pipeline.h" #include "llglcommonfunc.h" #include "llvoavatar.h" +#include "llperfstats.h" S32 diffuse_channel = -1; @@ -164,9 +165,20 @@ void LLDrawPoolMaterials::renderDeferred(S32 pass) LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type); LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type); - for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i) + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; + for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i) { LLDrawInfo& params = **i; + + if(params.mFace) + { + LLViewerObject* vobj = (LLViewerObject *)params.mFace->getViewerObject(); + + if( vobj && vobj->isAttachment() ) + { + trackAttachments( vobj, params.mFace->isState(LLFace::RIGGED), &ratPtr ); + } + } mShader->uniform4f(LLShaderMgr::SPECULAR_COLOR, params.mSpecColor.mV[0], params.mSpecColor.mV[1], params.mSpecColor.mV[2], params.mSpecColor.mV[3]); mShader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, params.mEnvIntensity); diff --git a/indra/newview/llfloaterperformance.cpp b/indra/newview/llfloaterperformance.cpp index 0ef9ab3215..257c0b2b37 100644 --- a/indra/newview/llfloaterperformance.cpp +++ b/indra/newview/llfloaterperformance.cpp @@ -99,16 +99,19 @@ BOOL LLFloaterPerformance::postBuild() mComplexityPanel = getChild<LLPanel>("panel_performance_complexity"); mSettingsPanel = getChild<LLPanel>("panel_performance_preferences"); mHUDsPanel = getChild<LLPanel>("panel_performance_huds"); + mAutoadjustmentsPanel = getChild<LLPanel>("panel_performance_autoadjustments"); getChild<LLPanel>("nearby_subpanel")->setMouseDownCallback(boost::bind(&LLFloaterPerformance::showSelectedPanel, this, mNearbyPanel)); getChild<LLPanel>("complexity_subpanel")->setMouseDownCallback(boost::bind(&LLFloaterPerformance::showSelectedPanel, this, mComplexityPanel)); getChild<LLPanel>("settings_subpanel")->setMouseDownCallback(boost::bind(&LLFloaterPerformance::showSelectedPanel, this, mSettingsPanel)); getChild<LLPanel>("huds_subpanel")->setMouseDownCallback(boost::bind(&LLFloaterPerformance::showSelectedPanel, this, mHUDsPanel)); + getChild<LLPanel>("autoadjustments_subpanel")->setMouseDownCallback(boost::bind(&LLFloaterPerformance::showSelectedPanel, this, mAutoadjustmentsPanel)); initBackBtn(mNearbyPanel); initBackBtn(mComplexityPanel); initBackBtn(mSettingsPanel); initBackBtn(mHUDsPanel); + initBackBtn(mAutoadjustmentsPanel); mHUDList = mHUDsPanel->getChild<LLNameListCtrl>("hud_list"); mHUDList->setNameListType(LLNameListCtrl::SPECIAL); @@ -197,6 +200,7 @@ void LLFloaterPerformance::hidePanels() mComplexityPanel->setVisible(FALSE); mHUDsPanel->setVisible(FALSE); mSettingsPanel->setVisible(FALSE); + mAutoadjustmentsPanel->setVisible(FALSE); } void LLFloaterPerformance::initBackBtn(LLPanel* panel) diff --git a/indra/newview/llfloaterperformance.h b/indra/newview/llfloaterperformance.h index e40eee162d..01b65365da 100644 --- a/indra/newview/llfloaterperformance.h +++ b/indra/newview/llfloaterperformance.h @@ -76,6 +76,7 @@ private: LLPanel* mComplexityPanel; LLPanel* mHUDsPanel; LLPanel* mSettingsPanel; + LLPanel* mAutoadjustmentsPanel; LLNameListCtrl* mHUDList; LLNameListCtrl* mObjectList; LLNameListCtrl* mNearbyList; diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index ac1dbe9867..77bf03852f 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -115,6 +115,7 @@ #include "llpresetsmanager.h" #include "llsearchableui.h" +#include "llperfstats.h" const F32 BANDWIDTH_UPDATER_TIMEOUT = 0.5f; char const* const VISIBILITY_DEFAULT = "default"; @@ -1481,6 +1482,23 @@ void LLAvatarComplexityControls::setText(U32 value, LLTextBox* text_box, bool sh } } +void LLAvatarComplexityControls::updateMaxRenderTime(LLSliderCtrl* slider, LLTextBox* value_label, bool short_val) +{ + setRenderTimeText((F32)(LLPerfStats::renderAvatarMaxART_ns/1000), value_label, short_val); +} + +void LLAvatarComplexityControls::setRenderTimeText(F32 value, LLTextBox* text_box, bool short_val) +{ + if (0 == value) + { + text_box->setText(LLTrans::getString("no_limit")); + } + else + { + text_box->setText(llformat("%.0f", value)); + } +} + void LLFloaterPreference::updateMaxComplexity() { // Called when the IndirectMaxComplexity control changes diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index a089fde9ff..de4e3cd886 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -358,6 +358,8 @@ class LLAvatarComplexityControls public: static void updateMax(LLSliderCtrl* slider, LLTextBox* value_label, bool short_val = false); static void setText(U32 value, LLTextBox* text_box, bool short_val = false); + static void updateMaxRenderTime(LLSliderCtrl* slider, LLTextBox* value_label, bool short_val = false); + static void setRenderTimeText(F32 value, LLTextBox* text_box, bool short_val = false); static void setIndirectControls(); static void setIndirectMaxNonImpostors(); static void setIndirectMaxArc(); diff --git a/indra/newview/llperfstats.cpp b/indra/newview/llperfstats.cpp new file mode 100644 index 0000000000..16d0df0245 --- /dev/null +++ b/indra/newview/llperfstats.cpp @@ -0,0 +1,469 @@ +/** +* @file llperfstats.cpp +* @brief Statistics collection to support autotune and perf flaoter. +* +* $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 "llperfstats.h" +#include "llcontrol.h" +#include "pipeline.h" +#include "llagentcamera.h" +#include "llvoavatar.h" +#include "llworld.h" +#include <llthread.h> + +extern LLControlGroup gSavedSettings; + +namespace LLPerfStats +{ + std::atomic<int64_t> tunedAvatars{0}; + std::atomic<U64> renderAvatarMaxART_ns{(U64)(ART_UNLIMITED_NANOS)}; // highest render time we'll allow without culling features + bool belowTargetFPS{false}; + U32 lastGlobalPrefChange{0}; + std::mutex bufferToggleLock{}; + + Tunables tunables; + + std::atomic<int> StatsRecorder::writeBuffer{0}; + bool StatsRecorder::collectionEnabled{true}; + LLUUID StatsRecorder::focusAv{LLUUID::null}; + std::array<StatsRecorder::StatsTypeMatrix,2> StatsRecorder::statsDoubleBuffer{ {} }; + std::array<StatsRecorder::StatsSummaryArray,2> StatsRecorder::max{ {} }; + std::array<StatsRecorder::StatsSummaryArray,2> StatsRecorder::sum{ {} }; + + void Tunables::applyUpdates() + { + assert_main_thread(); + // these following variables are proxies for pipeline statics we do not need a two way update (no llviewercontrol handler) + if( tuningFlag & NonImpostors ){ gSavedSettings.setU32("IndirectMaxNonImpostors", nonImpostors); }; + if( tuningFlag & ReflectionDetail ){ gSavedSettings.setS32("RenderReflectionDetail", reflectionDetail); }; + if( tuningFlag & FarClip ){ gSavedSettings.setF32("RenderFarClip", farClip); }; + if( tuningFlag & UserMinDrawDistance ){ gSavedSettings.setF32("AutoTuneRenderFarClipMin", userMinDrawDistance); }; + if( tuningFlag & UserTargetDrawDistance ){ gSavedSettings.setF32("AutoTuneRenderFarClipTarget", userTargetDrawDistance); }; + if( tuningFlag & UserImpostorDistance ){ gSavedSettings.setF32("AutoTuneImpostorFarAwayDistance", userImpostorDistance); }; + if( tuningFlag & UserImpostorDistanceTuningEnabled ){ gSavedSettings.setBOOL("AutoTuneImpostorByDistEnabled", userImpostorDistanceTuningEnabled); }; + if( tuningFlag & UserFPSTuningStrategy ){ gSavedSettings.setU32("TuningFPSStrategy", userFPSTuningStrategy); }; + if( tuningFlag & UserAutoTuneEnabled ){ gSavedSettings.setBOOL("AutoTuneFPS", userAutoTuneEnabled); }; + if( tuningFlag & UserAutoTuneLock ){ gSavedSettings.setBOOL("AutoTuneLock", userAutoTuneLock); }; + if( tuningFlag & UserTargetFPS ){ gSavedSettings.setU32("TargetFPS", userTargetFPS); }; + if( tuningFlag & UserTargetReflections ){ gSavedSettings.setS32("UserTargetReflections", userTargetReflections); }; + // Note: The Max ART slider is logarithmic and thus we have an intermediate proxy value + if( tuningFlag & UserARTCutoff ){ gSavedSettings.setF32("RenderAvatarMaxART", userARTCutoffSliderValue); }; + resetChanges(); + } + + void Tunables::updateRenderCostLimitFromSettings() + { + assert_main_thread(); + const auto newval = gSavedSettings.getF32("RenderAvatarMaxART"); + if(newval < log10(LLPerfStats::ART_UNLIMITED_NANOS/1000)) + { + LLPerfStats::renderAvatarMaxART_ns = pow(10,newval)*1000; + } + else + { + LLPerfStats::renderAvatarMaxART_ns = 0; + }; + } + + // static + void Tunables::updateSettingsFromRenderCostLimit() + { + if( userARTCutoffSliderValue != log10( ( (F32)LLPerfStats::renderAvatarMaxART_ns )/1000 ) ) + { + if( LLPerfStats::renderAvatarMaxART_ns != 0 ) + { + updateUserARTCutoffSlider(log10( ( (F32)LLPerfStats::renderAvatarMaxART_ns )/1000 ) ); + } + else + { + updateUserARTCutoffSlider(log10( (F32)LLPerfStats::ART_UNLIMITED_NANOS/1000 ) ); + } + } + } + + void Tunables::initialiseFromSettings() + { + assert_main_thread(); + // the following variables are two way and have "push" in llviewercontrol + LLPerfStats::tunables.userMinDrawDistance = gSavedSettings.getF32("AutoTuneRenderFarClipMin"); + LLPerfStats::tunables.userTargetDrawDistance = gSavedSettings.getF32("AutoTuneRenderFarClipTarget"); + LLPerfStats::tunables.userImpostorDistance = gSavedSettings.getF32("AutoTuneImpostorFarAwayDistance"); + LLPerfStats::tunables.userImpostorDistanceTuningEnabled = gSavedSettings.getBOOL("AutoTuneImpostorByDistEnabled"); + LLPerfStats::tunables.userFPSTuningStrategy = gSavedSettings.getU32("TuningFPSStrategy"); + LLPerfStats::tunables.userTargetFPS = gSavedSettings.getU32("TargetFPS"); + LLPerfStats::tunables.userTargetReflections = gSavedSettings.getS32("UserTargetReflections"); + LLPerfStats::tunables.userAutoTuneEnabled = gSavedSettings.getBOOL("AutoTuneFPS"); + LLPerfStats::tunables.userAutoTuneLock = gSavedSettings.getBOOL("AutoTuneLock"); + // Note: The Max ART slider is logarithmic and thus we have an intermediate proxy value + updateRenderCostLimitFromSettings(); + resetChanges(); + } + + StatsRecorder::StatsRecorder():q(1024*16),t(&StatsRecorder::run) + { + // create a queue + // create a thread to consume from the queue + tunables.initialiseFromSettings(); + t.detach(); + } + + // static + void StatsRecorder::toggleBuffer() + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + using ST = StatType_t; + + bool unreliable{false}; + LLPerfStats::StatsRecorder::getSceneStat(LLPerfStats::StatType_t::RENDER_FRAME); + auto& sceneStats = statsDoubleBuffer[writeBuffer][static_cast<size_t>(ObjType_t::OT_GENERAL)][LLUUID::null]; + auto& lastStats = statsDoubleBuffer[writeBuffer ^ 1][static_cast<size_t>(ObjType_t::OT_GENERAL)][LLUUID::null]; + + static constexpr std::initializer_list<StatType_t> sceneStatsToAvg = { + StatType_t::RENDER_FRAME, + StatType_t::RENDER_DISPLAY, + StatType_t::RENDER_HUDS, + StatType_t::RENDER_UI, + StatType_t::RENDER_SWAP, + // RENDER_LFS, + // RENDER_MESHREPO, + StatType_t::RENDER_IDLE }; + + static constexpr std::initializer_list<StatType_t> avatarStatsToAvg = { + StatType_t::RENDER_GEOMETRY, + StatType_t::RENDER_SHADOWS, + StatType_t::RENDER_COMBINED, + StatType_t::RENDER_IDLE }; + + + if( /*sceneStats[static_cast<size_t>(StatType_t::RENDER_FPSLIMIT)] != 0 ||*/ sceneStats[static_cast<size_t>(StatType_t::RENDER_SLEEP)] != 0 ) + { + unreliable = true; + //lastStats[static_cast<size_t>(StatType_t::RENDER_FPSLIMIT)] = sceneStats[static_cast<size_t>(StatType_t::RENDER_FPSLIMIT)]; + lastStats[static_cast<size_t>(StatType_t::RENDER_SLEEP)] = sceneStats[static_cast<size_t>(StatType_t::RENDER_SLEEP)]; + lastStats[static_cast<size_t>(StatType_t::RENDER_FRAME)] = sceneStats[static_cast<size_t>(StatType_t::RENDER_FRAME)]; // bring over the total frame render time to deal with region crossing overlap issues + } + + if(!unreliable) + { + // only use these stats when things are reliable. + + for(auto & statEntry : sceneStatsToAvg) + { + auto avg = lastStats[static_cast<size_t>(statEntry)]; + auto val = sceneStats[static_cast<size_t>(statEntry)]; + sceneStats[static_cast<size_t>(statEntry)] = avg + (val / SMOOTHING_PERIODS) - (avg / SMOOTHING_PERIODS); + // LL_INFOS("scenestats") << "Scenestat: " << static_cast<size_t>(statEntry) << " before=" << avg << " new=" << val << " newavg=" << statsDoubleBuffer[writeBuffer][static_cast<size_t>(ObjType_t::OT_GENERAL)][LLUUID::null][static_cast<size_t>(statEntry)] << LL_ENDL; + } + } +// Allow attachment times etc to update even when FPS limited or sleeping. + auto& statsMap = statsDoubleBuffer[writeBuffer][static_cast<size_t>(ObjType_t::OT_ATTACHMENT)]; + for(auto& stat_entry : statsMap) + { + auto val = stat_entry.second[static_cast<size_t>(ST::RENDER_COMBINED)]; + if(val > SMOOTHING_PERIODS){ + auto avg = statsDoubleBuffer[writeBuffer ^ 1][static_cast<size_t>(ObjType_t::OT_ATTACHMENT)][stat_entry.first][static_cast<size_t>(ST::RENDER_COMBINED)]; + stat_entry.second[static_cast<size_t>(ST::RENDER_COMBINED)] = avg + (val / SMOOTHING_PERIODS) - (avg / SMOOTHING_PERIODS); + } + } + + + auto& statsMapAv = statsDoubleBuffer[writeBuffer][static_cast<size_t>(ObjType_t::OT_AVATAR)]; + for(auto& stat_entry : statsMapAv) + { + for(auto& stat : avatarStatsToAvg) + { + auto val = stat_entry.second[static_cast<size_t>(stat)]; + if(val > SMOOTHING_PERIODS) + { + auto avg = statsDoubleBuffer[writeBuffer ^ 1][static_cast<size_t>(ObjType_t::OT_AVATAR)][stat_entry.first][static_cast<size_t>(stat)]; + stat_entry.second[static_cast<size_t>(stat)] = avg + (val / SMOOTHING_PERIODS) - (avg / SMOOTHING_PERIODS); + } + } + } + + // swap the buffers + if(enabled()) + { + std::lock_guard<std::mutex> lock{bufferToggleLock}; + writeBuffer ^= 1; + }; // note we are relying on atomic updates here. The risk is low and would cause minor errors in the stats display. + + // clean the write maps in all cases. + auto& statsTypeMatrix = statsDoubleBuffer[writeBuffer]; + for(auto& statsMapByType : statsTypeMatrix) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_STATS("Clear stats maps"); + for(auto& stat_entry : statsMapByType) + { + std::fill_n(stat_entry.second.begin() ,static_cast<size_t>(ST::STATS_COUNT),0); + } + statsMapByType.clear(); + } + for(int i=0; i< static_cast<size_t>(ObjType_t::OT_COUNT); i++) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_STATS("clear max/sum"); + max[writeBuffer][i].fill(0); + sum[writeBuffer][i].fill(0); + } + + // and now adjust the proxy vars so that the main thread can adjust the visuals. + if(tunables.userAutoTuneEnabled) + { + updateAvatarParams(); + } + } + + // clear buffers when we change region or need a hard reset. + // static + void StatsRecorder::clearStatsBuffers() + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + using ST = StatType_t; + + auto& statsTypeMatrix = statsDoubleBuffer[writeBuffer]; + for(auto& statsMap : statsTypeMatrix) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_STATS("Clear stats maps"); + for(auto& stat_entry : statsMap) + { + std::fill_n(stat_entry.second.begin() ,static_cast<size_t>(ST::STATS_COUNT),0); + } + statsMap.clear(); + } + for(int i=0; i< static_cast<size_t>(ObjType_t::OT_COUNT); i++) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_STATS("clear max/sum"); + max[writeBuffer][i].fill(0); + sum[writeBuffer][i].fill(0); + } + // swap the clean buffers in + if(enabled()) + { + std::lock_guard<std::mutex> lock{bufferToggleLock}; + writeBuffer ^= 1; + }; + // repeat before we start processing new stuff + for(auto& statsMap : statsTypeMatrix) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_STATS("Clear stats maps"); + for(auto& stat_entry : statsMap) + { + std::fill_n(stat_entry.second.begin() ,static_cast<size_t>(ST::STATS_COUNT),0); + } + statsMap.clear(); + } + for(int i=0; i< static_cast<size_t>(ObjType_t::OT_COUNT); i++) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_STATS("clear max/sum"); + max[writeBuffer][i].fill(0); + sum[writeBuffer][i].fill(0); + } + } + + //static + int StatsRecorder::countNearbyAvatars(S32 distance) + { + const auto our_pos = gAgentCamera.getCameraPositionGlobal(); + + std::vector<LLVector3d> positions; + uuid_vec_t avatar_ids; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, our_pos, distance); + return positions.size(); + } + + // static + void StatsRecorder::updateAvatarParams() + { + + if(tunables.userImpostorDistanceTuningEnabled) + { + // if we have less than the user's "max Non-Impostors" avatars within the desired range then adjust the limit. + // also adjusts back up again for nearby crowds. + auto count = countNearbyAvatars(std::min(LLPipeline::RenderFarClip, tunables.userImpostorDistance)); + if( count != tunables.nonImpostors ) + { + tunables.updateNonImposters( (count < LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER)?count : LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER ); + LL_DEBUGS("AutoTune") << "There are " << count << "avatars within " << std::min(LLPipeline::RenderFarClip, tunables.userImpostorDistance) << "m of the camera" << LL_ENDL; + } + } + + auto av_render_max_raw = LLPerfStats::StatsRecorder::getMax(ObjType_t::OT_AVATAR, LLPerfStats::StatType_t::RENDER_COMBINED); + // Is our target frame time lower than current? If so we need to take action to reduce draw overheads. + // cumulative avatar time (includes idle processing, attachments and base av) + auto tot_avatar_time_raw = LLPerfStats::StatsRecorder::getSum(ObjType_t::OT_AVATAR, LLPerfStats::StatType_t::RENDER_COMBINED); + // sleep time is basically forced sleep when window out of focus + auto tot_sleep_time_raw = LLPerfStats::StatsRecorder::getSceneStat(LLPerfStats::StatType_t::RENDER_SLEEP); + // similar to sleep time, induced by FPS limit + //auto tot_limit_time_raw = LLPerfStats::StatsRecorder::getSceneStat(LLPerfStats::StatType_t::RENDER_FPSLIMIT); + + + // the time spent this frame on the "doFrame" call. Treated as "tot time for frame" + auto tot_frame_time_raw = LLPerfStats::StatsRecorder::getSceneStat(LLPerfStats::StatType_t::RENDER_FRAME); + + if( tot_sleep_time_raw != 0 ) + { + // Note: we do not average sleep + // if at some point we need to, the averaging will need to take this into account or + // we forever think we're in the background due to residuals. + LL_DEBUGS() << "No tuning when not in focus" << LL_ENDL; + return; + } + + // The frametime budget we have based on the target FPS selected + auto target_frame_time_raw = (U64)llround((F64)LLTrace::BlockTimer::countsPerSecond()/(tunables.userTargetFPS==0?1:tunables.userTargetFPS)); + // LL_INFOS() << "Effective FPS(raw):" << tot_frame_time_raw << " Target:" << target_frame_time_raw << LL_ENDL; + auto inferredFPS{1000/(U32)std::max(raw_to_ms(tot_frame_time_raw),1.0)}; + U32 settingsChangeFrequency{inferredFPS > 25?inferredFPS:25}; + /*if( tot_limit_time_raw != 0) + { + // This could be problematic. + tot_frame_time_raw -= tot_limit_time_raw; + }*/ + // 1) Is the target frame time lower than current? + if( target_frame_time_raw <= tot_frame_time_raw ) + { + if(belowTargetFPS == false) + { + // this is the first frame under. hold fire to add a little hysteresis + belowTargetFPS = true; + LLPerfStats::lastGlobalPrefChange = gFrameCount; + } + // if so we've got work to do + + // how much of the frame was spent on non avatar related work? + U64 non_avatar_time_raw = tot_frame_time_raw - tot_avatar_time_raw; + + // If the target frame time < scene time (estimated as non_avatar time) + U64 target_avatar_time_raw; + if(target_frame_time_raw < non_avatar_time_raw) + { + // we cannnot do this by avatar adjustment alone. + if((gFrameCount - LLPerfStats::lastGlobalPrefChange) > settingsChangeFrequency) // give changes a short time to take effect. + { + if(tunables.userFPSTuningStrategy == TUNE_SCENE_AND_AVATARS) + { + // 1 - hack the water to opaque. all non opaque have a significant hit, this is a big boost for (arguably) a minor visual hit. + // the other reflection options make comparatively little change and if this overshoots we'll be stepping back up later + if(LLPipeline::RenderReflectionDetail != -2) + { + LLPerfStats::tunables.updateReflectionDetail(-2); + LLPerfStats::lastGlobalPrefChange = gFrameCount; + return; + } + else // deliberately "else" here so we only do one of these in any given frame + { + // step down the DD by 10m per update + auto new_dd = (LLPipeline::RenderFarClip - DD_STEP > tunables.userMinDrawDistance)?(LLPipeline::RenderFarClip - DD_STEP) : tunables.userMinDrawDistance; + if(new_dd != LLPipeline::RenderFarClip) + { + LLPerfStats::tunables.updateFarClip( new_dd ); + LLPerfStats::lastGlobalPrefChange = gFrameCount; + return; + } + } + } + // if we reach here, we've no more changes to make to tune scenery so we'll resort to agressive Avatar tuning + // Note: moved from outside "if changefrequency elapsed" to stop fallthrough and allow scenery changes time to take effect. + target_avatar_time_raw = 0; + } + else + { + // we made a settings change recently so let's give it time. + return; + } + } + else + { + // set desired avatar budget. + target_avatar_time_raw = target_frame_time_raw - non_avatar_time_raw; + } + + if( target_avatar_time_raw < tot_avatar_time_raw ) + { + // we need to spend less time drawing avatars to meet our budget + auto new_render_limit_ns {LLPerfStats::raw_to_ns(av_render_max_raw)}; + // max render this frame may be higher than the last (cos new entrants and jitter) so make sure we are heading in the right direction + if( new_render_limit_ns > renderAvatarMaxART_ns ) + { + new_render_limit_ns = renderAvatarMaxART_ns; + } + new_render_limit_ns -= LLPerfStats::ART_MIN_ADJUST_DOWN_NANOS; + + // bounce at the bottom to prevent "no limit" + new_render_limit_ns = std::max((U64)new_render_limit_ns, (U64)LLPerfStats::ART_MINIMUM_NANOS); + + // assign the new value + if(renderAvatarMaxART_ns != new_render_limit_ns) + { + renderAvatarMaxART_ns = new_render_limit_ns; + tunables.updateSettingsFromRenderCostLimit(); + } + // LL_DEBUGS() << "AUTO_TUNE: avatar_budget adjusted to:" << new_render_limit_ns << LL_ENDL; + } + // LL_DEBUGS() << "AUTO_TUNE: Target frame time:"<< LLPerfStats::raw_to_us(target_frame_time_raw) << "usecs (non_avatar is " << LLPerfStats::raw_to_us(non_avatar_time_raw) << "usecs) Max cost limited=" << renderAvatarMaxART_ns << LL_ENDL; + } + else if( LLPerfStats::raw_to_ns(target_frame_time_raw) > (LLPerfStats::raw_to_ns(tot_frame_time_raw) + renderAvatarMaxART_ns) ) + { + if(belowTargetFPS == true) + { + // we reached target, force a pause + lastGlobalPrefChange = gFrameCount; + belowTargetFPS = false; + } + + // once we're over the FPS target we slow down further + if((gFrameCount - lastGlobalPrefChange) > settingsChangeFrequency*3) + { + if(!tunables.userAutoTuneLock) + { + // we've reached the target and stayed long enough to consider stable. + // turn off if we are not locked. + tunables.updateUserAutoTuneEnabled(false); + } + if( LLPerfStats::tunedAvatars > 0 ) + { + // if we have more time to spare let's shift up little in the hope we'll restore an avatar. + renderAvatarMaxART_ns += LLPerfStats::ART_MIN_ADJUST_UP_NANOS; + tunables.updateSettingsFromRenderCostLimit(); + return; + } + if(tunables.userFPSTuningStrategy == TUNE_SCENE_AND_AVATARS) + { + if( LLPipeline::RenderFarClip < tunables.userTargetDrawDistance ) + { + LLPerfStats::tunables.updateFarClip( std::min(LLPipeline::RenderFarClip + DD_STEP, tunables.userTargetDrawDistance) ); + LLPerfStats::lastGlobalPrefChange = gFrameCount; + return; + } + if( (tot_frame_time_raw * 1.5) < target_frame_time_raw ) + { + // if everything else is "max" and we have >50% headroom let's knock the water quality up a notch at a time. + LLPerfStats::tunables.updateReflectionDetail( std::min(LLPipeline::RenderReflectionDetail + 1, tunables.userTargetReflections) ); + } + } + } + } + } +}
\ No newline at end of file diff --git a/indra/newview/llperfstats.h b/indra/newview/llperfstats.h new file mode 100644 index 0000000000..1e867f5ef1 --- /dev/null +++ b/indra/newview/llperfstats.h @@ -0,0 +1,454 @@ +/** +* @file llperfstats.h +* @brief Statistics collection to support autotune and perf flaoter. +* +* $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 +#ifndef LL_PERFSTATS_H_INCLUDED +#define LL_PERFSTATS_H_INCLUDED + +#include <atomic> +#include <chrono> +#include <array> +#include <unordered_map> +#include <mutex> +#include "lluuid.h" +#include "llfasttimer.h" +#include "llapp.h" +#include "llprofiler.h" +#include "pipeline.h" + +extern U32 gFrameCount; +extern LLUUID gAgentID; +namespace LLPerfStats +{ +// Note if changing these, they should correspond with the log range of the correpsonding sliders + static constexpr U64 ART_UNLIMITED_NANOS{50000000}; + static constexpr U64 ART_MINIMUM_NANOS{100000}; + static constexpr U64 ART_MIN_ADJUST_UP_NANOS{10000}; + static constexpr U64 ART_MIN_ADJUST_DOWN_NANOS{10000}; + + static constexpr F32 PREFERRED_DD{180}; + static constexpr U32 SMOOTHING_PERIODS{50}; + static constexpr U32 DD_STEP{10}; + + static constexpr U32 TUNE_AVATARS_ONLY{0}; + static constexpr U32 TUNE_SCENE_AND_AVATARS{1}; + + extern std::atomic<int64_t> tunedAvatars; + extern std::atomic<U64> renderAvatarMaxART_ns; + extern bool belowTargetFPS; + extern U32 lastGlobalPrefChange; + extern std::mutex bufferToggleLock; + + enum class ObjType_t{ + OT_GENERAL=0, // Also Unknown. Used for n/a type stats such as scenery + OT_AVATAR, + OT_ATTACHMENT, + OT_HUD, + OT_COUNT + }; + enum class StatType_t{ + RENDER_GEOMETRY=0, + RENDER_SHADOWS, + RENDER_HUDS, + RENDER_UI, + RENDER_COMBINED, + RENDER_SWAP, + RENDER_FRAME, + RENDER_DISPLAY, + RENDER_SLEEP, + RENDER_LFS, + RENDER_MESHREPO, + //RENDER_FPSLIMIT, + RENDER_FPS, + RENDER_IDLE, + RENDER_DONE, // toggle buffer & clearbuffer (see processUpdate for hackery) + STATS_COUNT + }; + + struct StatsRecord + { + StatType_t statType; + ObjType_t objType; + LLUUID avID; + LLUUID objID; + uint64_t time; + bool isRigged; + bool isHUD; + }; + + struct Tunables + { + static constexpr U32 Nothing{0}; + static constexpr U32 NonImpostors{1}; + static constexpr U32 ReflectionDetail{2}; + static constexpr U32 FarClip{4}; + static constexpr U32 UserMinDrawDistance{8}; + static constexpr U32 UserTargetDrawDistance{16}; + static constexpr U32 UserImpostorDistance{32}; + static constexpr U32 UserImpostorDistanceTuningEnabled{64}; + static constexpr U32 UserFPSTuningStrategy{128}; + static constexpr U32 UserAutoTuneEnabled{256}; + static constexpr U32 UserTargetFPS{512}; + static constexpr U32 UserARTCutoff{1024}; + static constexpr U32 UserTargetReflections{2048}; + static constexpr U32 UserAutoTuneLock{4096}; + + U32 tuningFlag{0}; // bit mask for changed settings + + // proxy variables, used to pas the new value to be set via the mainthread + U32 nonImpostors{0}; + S32 reflectionDetail{0}; + F32 farClip{0.0}; + F32 userMinDrawDistance{0.0}; + F32 userTargetDrawDistance{0.0}; + F32 userImpostorDistance{0.0}; + bool userImpostorDistanceTuningEnabled{false}; + U32 userFPSTuningStrategy{0}; + bool userAutoTuneEnabled{false}; + bool userAutoTuneLock{true}; + U32 userTargetFPS{0}; + F32 userARTCutoffSliderValue{0}; + S32 userTargetReflections{0}; + + void updateNonImposters(U32 nv){nonImpostors=nv; tuningFlag |= NonImpostors;}; + void updateReflectionDetail(S32 nv){reflectionDetail=nv; tuningFlag |= ReflectionDetail;}; + void updateFarClip(F32 nv){farClip=nv; tuningFlag |= FarClip;}; + void updateUserMinDrawDistance(F32 nv){userMinDrawDistance=nv; tuningFlag |= UserMinDrawDistance;}; + void updateUserTargetDrawDistance(F32 nv){userTargetDrawDistance=nv; tuningFlag |= UserTargetDrawDistance;}; + void updateImposterDistance(F32 nv){userImpostorDistance=nv; tuningFlag |= UserImpostorDistance;}; + void updateImposterDistanceTuningEnabled(bool nv){userImpostorDistanceTuningEnabled=nv; tuningFlag |= UserImpostorDistanceTuningEnabled;}; + void updateUserFPSTuningStrategy(U32 nv){userFPSTuningStrategy=nv; tuningFlag |= UserFPSTuningStrategy;}; + void updateTargetFps(U32 nv){userTargetFPS=nv; tuningFlag |= UserTargetFPS;}; + void updateUserARTCutoffSlider(F32 nv){userARTCutoffSliderValue=nv; tuningFlag |= UserARTCutoff;}; + void updateUserAutoTuneEnabled(bool nv){userAutoTuneEnabled=nv; tuningFlag |= UserAutoTuneEnabled;}; + void updateUserAutoTuneLock(bool nv){userAutoTuneLock=nv; tuningFlag |= UserAutoTuneLock;}; + void updateUserTargetReflections(S32 nv){userTargetReflections=nv; tuningFlag |= UserTargetReflections;}; + + void resetChanges(){tuningFlag=Nothing;}; + void initialiseFromSettings(); + void updateRenderCostLimitFromSettings(); + void updateSettingsFromRenderCostLimit(); + void applyUpdates(); + }; + + extern Tunables tunables; + + class StatsRecorder{ + using Queue = LLThreadSafeQueue<StatsRecord>; + public: + + static inline StatsRecorder& getInstance() + { + static StatsRecorder instance; + return instance; + } + static inline void setFocusAv(const LLUUID& avID){focusAv = avID;}; + static inline const LLUUID& getFocusAv(){return focusAv;}; + static inline void send(StatsRecord && upd){StatsRecorder::getInstance().q.pushFront(std::move(upd));}; + static void endFrame(){StatsRecorder::getInstance().q.pushFront(StatsRecord{StatType_t::RENDER_DONE, ObjType_t::OT_GENERAL, LLUUID::null, LLUUID::null, 0});}; + static void clearStats(){StatsRecorder::getInstance().q.pushFront(StatsRecord{StatType_t::RENDER_DONE, ObjType_t::OT_GENERAL, LLUUID::null, LLUUID::null, 1});}; + + static inline void setEnabled(bool on_or_off){collectionEnabled=on_or_off;}; + static inline void enable() { collectionEnabled=true; }; + static inline void disable() { collectionEnabled=false; }; + static inline bool enabled() { return collectionEnabled; }; + + static inline int getReadBufferIndex() { return (writeBuffer ^ 1); }; + // static inline const StatsTypeMatrix& getCurrentStatsMatrix(){ return statsDoubleBuffer[getReadBufferIndex()];} + static inline uint64_t get(ObjType_t otype, LLUUID id, StatType_t type) + { + return statsDoubleBuffer[getReadBufferIndex()][static_cast<size_t>(otype)][id][static_cast<size_t>(type)]; + } + static inline uint64_t getSceneStat(StatType_t type) + { + return statsDoubleBuffer[getReadBufferIndex()][static_cast<size_t>(ObjType_t::OT_GENERAL)][LLUUID::null][static_cast<size_t>(type)]; + } + + static inline uint64_t getSum(ObjType_t otype, StatType_t type) + { + return sum[getReadBufferIndex()][static_cast<size_t>(otype)][static_cast<size_t>(type)]; + } + static inline uint64_t getMax(ObjType_t otype, StatType_t type) + { + return max[getReadBufferIndex()][static_cast<size_t>(otype)][static_cast<size_t>(type)]; + } + static void updateAvatarParams(); + private: + StatsRecorder(); + + static int countNearbyAvatars(S32 distance); +// StatsArray is a uint64_t for each possible statistic type. + using StatsArray = std::array<uint64_t, static_cast<size_t>(LLPerfStats::StatType_t::STATS_COUNT)>; + using StatsMap = std::unordered_map<LLUUID, StatsArray, boost::hash<LLUUID>>; + using StatsTypeMatrix = std::array<StatsMap, static_cast<size_t>(LLPerfStats::ObjType_t::OT_COUNT)>; + using StatsSummaryArray = std::array<StatsArray, static_cast<size_t>(LLPerfStats::ObjType_t::OT_COUNT)>; + + static std::atomic<int> writeBuffer; + static LLUUID focusAv; + static std::array<StatsTypeMatrix,2> statsDoubleBuffer; + static std::array<StatsSummaryArray,2> max; + static std::array<StatsSummaryArray,2> sum; + static bool collectionEnabled; + + + void processUpdate(const StatsRecord& upd) const + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + // LL_INFOS("perfstats") << "processing update:" << LL_ENDL; + // Note: nullptr is used as the key for global stats + #ifdef TRACY_ENABLE + static char avstr[36]; + static char obstr[36]; + #endif + + if (upd.statType == StatType_t::RENDER_DONE && upd.objType == ObjType_t::OT_GENERAL && upd.time == 0) + { + // LL_INFOS("perfstats") << "End of Frame Toggle Buffer:" << gFrameCount << LL_ENDL; + toggleBuffer(); + return; + } + if (upd.statType == StatType_t::RENDER_DONE && upd.objType == ObjType_t::OT_GENERAL && upd.time == 1) + { + // LL_INFOS("perfstats") << "New region - clear buffers:" << gFrameCount << LL_ENDL; + clearStatsBuffers(); + return; + } + + auto ot{upd.objType}; + auto& key{upd.objID}; + auto& avKey{upd.avID}; + auto type {upd.statType}; + auto val {upd.time}; + + if (ot == ObjType_t::OT_GENERAL) + { + // LL_INFOS("perfstats") << "General update:" << LL_ENDL; + doUpd(key, ot, type,val); + return; + } + + if (ot == ObjType_t::OT_AVATAR) + { + // LL_INFOS("perfstats") << "Avatar update:" << LL_ENDL; + doUpd(avKey, ot, type, val); + return; + } + + if (ot == ObjType_t::OT_ATTACHMENT) + { + if( !upd.isHUD ) // don't include HUD cost in self. + { + LL_PROFILE_ZONE_NAMED("Att as Av") + // For all attachments that are not rigged we add them to the avatar (for all avatars) cost. + doUpd(avKey, ObjType_t::OT_AVATAR, type, val); + } + if( avKey == focusAv ) + { + LL_PROFILE_ZONE_NAMED("Att as Att") + // For attachments that are for the focusAv (self for now) we record them for the attachment/complexity view + if(upd.isHUD) + { + ot = ObjType_t::OT_HUD; + } + // LL_INFOS("perfstats") << "frame: " << gFrameCount << " Attachment update("<< (type==StatType_t::RENDER_GEOMETRY?"GEOMETRY":"SHADOW") << ": " << key.asString() << " = " << val << LL_ENDL; + doUpd(key, ot, type, val); + } + // else + // { + // // LL_INFOS("perfstats") << "frame: " << gFrameCount << " non-self Att update("<< (type==StatType_t::RENDER_GEOMETRY?"GEOMETRY":"SHADOW") << ": " << key.asString() << " = " << val << " for av " << avKey.asString() << LL_ENDL; + // } + } + } + + static inline void doUpd(const LLUUID& key, ObjType_t ot, StatType_t type, uint64_t val) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + using ST = StatType_t; + StatsMap& stm {statsDoubleBuffer[writeBuffer][static_cast<size_t>(ot)]}; + auto& thisAsset = stm[key]; + + thisAsset[static_cast<size_t>(type)] += val; + thisAsset[static_cast<size_t>(ST::RENDER_COMBINED)] += val; + + sum[writeBuffer][static_cast<size_t>(ot)][static_cast<size_t>(type)] += val; + sum[writeBuffer][static_cast<size_t>(ot)][static_cast<size_t>(ST::RENDER_COMBINED)] += val; + + if(max[writeBuffer][static_cast<size_t>(ot)][static_cast<size_t>(type)] < thisAsset[static_cast<size_t>(type)]) + { + max[writeBuffer][static_cast<size_t>(ot)][static_cast<size_t>(type)] = thisAsset[static_cast<size_t>(type)]; + } + if(max[writeBuffer][static_cast<size_t>(ot)][static_cast<size_t>(ST::RENDER_COMBINED)] < thisAsset[static_cast<size_t>(ST::RENDER_COMBINED)]) + { + max[writeBuffer][static_cast<size_t>(ot)][static_cast<size_t>(ST::RENDER_COMBINED)] = thisAsset[static_cast<size_t>(ST::RENDER_COMBINED)]; + } + } + + static void toggleBuffer(); + static void clearStatsBuffers(); + + // thread entry + static void run() + { + StatsRecord upd[10]; + auto & instance {StatsRecorder::getInstance()}; + LL_PROFILER_SET_THREAD_NAME("PerfStats"); + + while( enabled() && !LLApp::isExiting() ) + { + auto count = 0; + while (count < 10) + { + if (instance.q.tryPopFor(std::chrono::milliseconds(10), upd[count])) + { + count++; + } + else + { + break; + } + } + //LL_PROFILER_THREAD_BEGIN("PerfStats"); + if(count) + { + // LL_INFOS("perfstats") << "processing " << count << " updates." << LL_ENDL; + for(auto i =0; i < count; i++) + { + instance.processUpdate(upd[i]); + } + } + //LL_PROFILER_THREAD_END("PerfStats"); + } + } + + Queue q; + std::thread t; + + ~StatsRecorder() = default; + StatsRecorder(const StatsRecorder&) = delete; + StatsRecorder& operator=(const StatsRecorder&) = delete; + + }; + + template <enum ObjType_t ObjTypeDiscriminator> + class RecordTime + { + + private: + RecordTime(const RecordTime&) = delete; + RecordTime() = delete; + U64 start; + public: + StatsRecord stat; + + RecordTime( const LLUUID& av, const LLUUID& id, StatType_t type, bool isRiggedAtt=false, bool isHUDAtt=false): + start{LLTrace::BlockTimer::getCPUClockCount64()}, + stat{type, ObjTypeDiscriminator, std::move(av), std::move(id), 0, isRiggedAtt, isHUDAtt} + { + //LL_PROFILE_ZONE_COLOR(tracy::Color::Orange); + }; + + template < ObjType_t OD = ObjTypeDiscriminator, + std::enable_if_t<OD == ObjType_t::OT_GENERAL> * = nullptr> + explicit RecordTime( StatType_t type ):RecordTime<ObjTypeDiscriminator>(LLUUID::null, LLUUID::null, type ) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + }; + + template < ObjType_t OD = ObjTypeDiscriminator, + std::enable_if_t<OD == ObjType_t::OT_AVATAR> * = nullptr> + RecordTime( const LLUUID & av, StatType_t type ):RecordTime<ObjTypeDiscriminator>(std::move(av), LLUUID::null, type) + { + //LL_PROFILE_ZONE_COLOR(tracy::Color::Purple); + }; + + ~RecordTime() + { + if(!LLPerfStats::StatsRecorder::enabled()) + { + return; + } + + //LL_PROFILE_ZONE_COLOR(tracy::Color::Red); + + stat.time = LLTrace::BlockTimer::getCPUClockCount64() - start; + StatsRecorder::send(std::move(stat)); + }; + }; + + + inline double raw_to_ns(U64 raw) { return (static_cast<double>(raw) * 1000000000.0) / (F64)LLTrace::BlockTimer::countsPerSecond(); }; + inline double raw_to_us(U64 raw) { return (static_cast<double>(raw) * 1000000.0) / (F64)LLTrace::BlockTimer::countsPerSecond(); }; + inline double raw_to_ms(U64 raw) { return (static_cast<double>(raw) * 1000.0) / (F64)LLTrace::BlockTimer::countsPerSecond(); }; + + using RecordSceneTime = RecordTime<ObjType_t::OT_GENERAL>; + using RecordAvatarTime = RecordTime<ObjType_t::OT_AVATAR>; + using RecordAttachmentTime = RecordTime<ObjType_t::OT_ATTACHMENT>; + using RecordHudAttachmentTime = RecordTime<ObjType_t::OT_HUD>; + +};// namespace LLPerfStats + +// helper functions +using RATptr = std::unique_ptr<LLPerfStats::RecordAttachmentTime>; +using RSTptr = std::unique_ptr<LLPerfStats::RecordSceneTime>; + +template <typename T> +static inline void trackAttachments(const T * vobj, bool isRigged, RATptr* ratPtrp) +{ + if( !vobj ){ ratPtrp->reset(); return;}; + + const T* rootAtt{vobj}; + if (rootAtt->isAttachment()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + + while( !rootAtt->isRootEdit() ) + { + rootAtt = (T*)(rootAtt->getParent()); + } + + auto avPtr = (T*)(rootAtt->getParent()); + if(!avPtr){ratPtrp->reset(); return;} + + auto& av = avPtr->getID(); + auto& obj = rootAtt->getAttachmentItemID(); + if (!*ratPtrp || (*ratPtrp)->stat.objID != obj || (*ratPtrp)->stat.avID != av) + { + if (*ratPtrp) + { + // deliberately reset to ensure destruction before construction of replacement. + ratPtrp->reset(); + }; + *ratPtrp = std::make_unique<LLPerfStats::RecordAttachmentTime>( + av, + obj, + ( LLPipeline::sShadowRender?LLPerfStats::StatType_t::RENDER_SHADOWS : LLPerfStats::StatType_t::RENDER_GEOMETRY ), + isRigged, + rootAtt->isHUDAttachment()); + } + } + return; +}; + +#endif
\ No newline at end of file diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index dc48eaa823..9cbd56ee7f 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -207,6 +207,7 @@ #include "llstacktrace.h" #include "threadpool.h" +#include "llperfstats.h" #if LL_WINDOWS @@ -1492,6 +1493,8 @@ bool idle_startup() LLViewerParcelAskPlay::getInstance()->loadSettings(); } + gAgent.addRegionChangedCallback(boost::bind(&LLPerfStats::StatsRecorder::clearStats)); + // *Note: this is where gWorldMap used to be initialized. // register null callbacks for audio until the audio system is initialized @@ -3405,6 +3408,9 @@ bool process_login_success_response() if(!text.empty()) gAgentID.set(text); gDebugInfo["AgentID"] = text; + LLPerfStats::StatsRecorder::setEnabled(gSavedSettings.getBOOL("PerfStatsCaptureEnabled")); + LLPerfStats::StatsRecorder::setFocusAv(gAgentID); + // Agent id needed for parcel info request in LLUrlEntryParcel // to resolve parcel name. LLUrlEntryParcel::setAgentID(gAgentID); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index fd07fc32bc..e5723ebfe5 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -75,6 +75,7 @@ #include "llspellcheck.h" #include "llslurl.h" #include "llstartup.h" +#include "llperfstats.h" // Third party library includes #include <boost/algorithm/string.hpp> @@ -643,6 +644,66 @@ bool toggle_show_object_render_cost(const LLSD& newvalue) } void handleRenderAutoMuteByteLimitChanged(const LLSD& new_value); + +void handleTargetFPSChanged(const LLSD& newValue) +{ + const auto targetFPS = gSavedSettings.getU32("TargetFPS"); + LLPerfStats::tunables.userTargetFPS = targetFPS; +} + +void handleAutoTuneLockChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getBOOL("AutoTuneLock"); + LLPerfStats::tunables.userAutoTuneLock = newval; +} + +void handleAutoTuneFPSChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getBOOL("AutoTuneFPS"); + LLPerfStats::tunables.userAutoTuneEnabled = newval; + if(newval && LLPerfStats::renderAvatarMaxART_ns == 0) // If we've enabled autotune we override "unlimited" to max + { + gSavedSettings.setF32("RenderAvatarMaxART",log10(LLPerfStats::ART_UNLIMITED_NANOS-1000));//triggers callback to update static var + } +} + +void handleRenderAvatarMaxARTChanged(const LLSD& newValue) +{ + LLPerfStats::tunables.updateRenderCostLimitFromSettings(); +} + +void handleUserTargetDrawDistanceChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getF32("AutoTuneRenderFarClipTarget"); + LLPerfStats::tunables.userTargetDrawDistance = newval; +} + +void handleUserTargetReflectionsChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getS32("UserTargetReflections"); + LLPerfStats::tunables.userTargetReflections = newval; +} + +void handlePerformanceStatsEnabledChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getBOOL("PerfStatsCaptureEnabled"); + LLPerfStats::StatsRecorder::setEnabled(newval); +} +void handleUserImpostorByDistEnabledChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getBOOL("AutoTuneImpostorByDistEnabled"); + LLPerfStats::tunables.userImpostorDistanceTuningEnabled = newval; +} +void handleUserImpostorDistanceChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getF32("AutoTuneImpostorFarAwayDistance"); + LLPerfStats::tunables.userImpostorDistance = newval; +} +void handleFPSTuningStrategyChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getU32("TuningFPSStrategy"); + LLPerfStats::tunables.userFPSTuningStrategy = newval; +} //////////////////////////////////////////////////////////////////////////// void settings_setup_listeners() @@ -796,6 +857,17 @@ void settings_setup_listeners() gSavedSettings.getControl("DebugAvatarJoints")->getCommitSignal()->connect(boost::bind(&handleDebugAvatarJointsChanged, _2)); gSavedSettings.getControl("RenderAutoMuteByteLimit")->getSignal()->connect(boost::bind(&handleRenderAutoMuteByteLimitChanged, _2)); gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ")->getCommitSignal()->connect(boost::bind(&handleAvatarHoverOffsetChanged, _2)); + + gSavedSettings.getControl("TargetFPS")->getSignal()->connect(boost::bind(&handleTargetFPSChanged, _2)); + gSavedSettings.getControl("AutoTuneFPS")->getSignal()->connect(boost::bind(&handleAutoTuneFPSChanged, _2)); + gSavedSettings.getControl("AutoTuneLock")->getSignal()->connect(boost::bind(&handleAutoTuneLockChanged, _2)); + gSavedSettings.getControl("RenderAvatarMaxART")->getSignal()->connect(boost::bind(&handleRenderAvatarMaxARTChanged, _2)); + gSavedSettings.getControl("PerfStatsCaptureEnabled")->getSignal()->connect(boost::bind(&handlePerformanceStatsEnabledChanged, _2)); + gSavedSettings.getControl("UserTargetReflections")->getSignal()->connect(boost::bind(&handleUserTargetReflectionsChanged, _2)); + gSavedSettings.getControl("AutoTuneRenderFarClipTarget")->getSignal()->connect(boost::bind(&handleUserTargetDrawDistanceChanged, _2)); + gSavedSettings.getControl("AutoTuneImpostorFarAwayDistance")->getSignal()->connect(boost::bind(&handleUserImpostorDistanceChanged, _2)); + gSavedSettings.getControl("AutoTuneImpostorByDistEnabled")->getSignal()->connect(boost::bind(&handleUserImpostorByDistEnabledChanged, _2)); + gSavedSettings.getControl("TuningFPSStrategy")->getSignal()->connect(boost::bind(&handleFPSTuningStrategyChanged, _2)); } #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index be54cb2f96..fa026c2888 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -78,6 +78,7 @@ #include "llscenemonitor.h" #include "llenvironment.h" +#include "llperfstats.h" extern LLPointer<LLViewerTexture> gStartTexture; extern bool gShiftFrame; @@ -256,7 +257,8 @@ static LLTrace::BlockTimerStatHandle FTM_EEP_UPDATE("Env Update"); // Paint the display! void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) { - LL_RECORD_BLOCK_TIME(FTM_RENDER); + LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_DISPLAY); // render time capture - This is the main stat for overall rendering. + LL_RECORD_BLOCK_TIME(FTM_RENDER); if (gWindowResized) { //skip render on frames where window has been resized @@ -1051,7 +1053,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) void render_hud_attachments() { - gGL.matrixMode(LLRender::MM_PROJECTION); + LLPerfStats::RecordSceneTime T ( LLPerfStats::StatType_t::RENDER_HUDS); // render time capture - Primary contributor to HUDs (though these end up in render batches) + gGL.matrixMode(LLRender::MM_PROJECTION); gGL.pushMatrix(); gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); @@ -1242,7 +1245,8 @@ bool setup_hud_matrices(const LLRect& screen_region) void render_ui(F32 zoom_factor, int subfield) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; //LL_RECORD_BLOCK_TIME(FTM_RENDER_UI); + LLPerfStats::RecordSceneTime T ( LLPerfStats::StatType_t::RENDER_UI ); // render time capture - Primary UI stat can have HUD time overlap (TODO) + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; //LL_RECORD_BLOCK_TIME(FTM_RENDER_UI); LLGLState::checkStates(); @@ -1325,7 +1329,8 @@ static LLTrace::BlockTimerStatHandle FTM_SWAP("Swap"); void swap() { - LL_RECORD_BLOCK_TIME(FTM_SWAP); + LLPerfStats::RecordSceneTime T ( LLPerfStats::StatType_t::RENDER_SWAP ); // render time capture - Swap buffer time - can signify excessive data transfer to/from GPU + LL_RECORD_BLOCK_TIME(FTM_SWAP); if (gDisplaySwapBuffers) { diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index f3b0e82b3a..489f90aabb 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -55,6 +55,7 @@ #include "m3math.h" #include "m4math.h" #include "llmatrix4a.h" +#include "llperfstats.h" #if !LL_DARWIN && !LL_LINUX extern PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB; @@ -230,6 +231,16 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy) return 0; } + // render time capture + // This path does not appear to have attachments. Prove this then remove. + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; + auto vobj = mFace->getViewerObject(); + if( vobj && vobj->isAttachment() ) + { + trackAttachments( vobj, mFace->isState(LLFace::RIGGED), &ratPtr ); + LL_WARNS("trackAttachments") << "Attachment render time is captuted." << LL_ENDL; + } + U32 triangle_count = 0; S32 diffuse_channel = LLDrawPoolAvatar::sDiffuseChannel; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index b66a6958fe..1a71780a88 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -113,6 +113,8 @@ #include "llrendersphere.h" #include "llskinningutil.h" +#include "llperfstats.h" + #include <boost/lexical_cast.hpp> extern F32 SPEED_ADJUST_MAX; @@ -2544,6 +2546,9 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) LL_INFOS() << "Warning! Idle on dead avatar" << LL_ENDL; return; } + // record time and refresh "tooSlow" status + LLPerfStats::RecordAvatarTime T(getID(), LLPerfStats::StatType_t::RENDER_IDLE); // per avatar "idle" time. + updateTooSlow(); static LLCachedControl<bool> disable_all_render_types(gSavedSettings, "DisableAllRenderTypes"); if (!(gPipeline.hasRenderType(mIsControlAvatar ? LLPipeline::RENDER_TYPE_CONTROL_AV : LLPipeline::RENDER_TYPE_AVATAR)) @@ -8268,6 +8273,94 @@ bool LLVOAvatar::isTooComplex() const return too_complex; } +// use Avatar Render Time as complexity metric +// markARTStale - Mark stale and set the frameupdate to now so that we can wait at least one frame to get a revised number. +void LLVOAvatar::markARTStale() +{ + mARTStale=true; + mLastARTUpdateFrame = LLFrameTimer::getFrameCount(); +} + +// Udpate Avatar state based on render time +void LLVOAvatar::updateTooSlow() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + static LLCachedControl<bool> alwaysRenderFriends(gSavedSettings, "AlwaysRenderFriends"); + static LLCachedControl<bool> allowSelfImpostor(gSavedSettings, "AllowSelfImpostor"); + const auto id = getID(); + + // mTooSlow - Is the avatar flagged as being slow (includes shadow time) + // mTooSlowWithoutShadows - Is the avatar flagged as being slow even with shadows removed. + // mARTStale - the rendertime we have is stale because of an update. We need to force a re-render to re-assess slowness + + if( mARTStale ) + { + if ( LLFrameTimer::getFrameCount() - mLastARTUpdateFrame < 5 ) + { + // LL_INFOS() << this->getFullname() << " marked stale " << LL_ENDL; + // we've not had a chance to update yet (allow a few to be certain a full frame has passed) + return; + } + + mARTStale = false; + mTooSlow = false; + mTooSlowWithoutShadows = false; + // LL_INFOS() << this->getFullname() << " refreshed ART combined = " << mRenderTime << " @ " << mLastARTUpdateFrame << LL_ENDL; + } + + // Either we're not stale or we've updated. + + U64 render_time_raw; + U64 render_geom_time_raw; + + if( !mTooSlow ) + { + // we are fully rendered, so we use the live values + std::lock_guard<std::mutex> lock{LLPerfStats::bufferToggleLock}; + render_time_raw = LLPerfStats::StatsRecorder::get(LLPerfStats::ObjType_t::OT_AVATAR, id, LLPerfStats::StatType_t::RENDER_COMBINED); + render_geom_time_raw = LLPerfStats::StatsRecorder::get(LLPerfStats::ObjType_t::OT_AVATAR, id, LLPerfStats::StatType_t::RENDER_GEOMETRY); + } + else + { + // use the cached values. + render_time_raw = mRenderTime; + render_geom_time_raw = mGeomTime; + } + if( (LLPerfStats::renderAvatarMaxART_ns > 0) && + (LLPerfStats::raw_to_ns(render_time_raw) >= LLPerfStats::renderAvatarMaxART_ns) ) + { + if( !mTooSlow ) // if we were previously not slow (with or without shadows.) + { + // if we weren't capped, we are now + mLastARTUpdateFrame = LLFrameTimer::getFrameCount(); + mRenderTime = render_time_raw; + mGeomTime = render_geom_time_raw; + mARTStale = false; + mTooSlow = true; + } + if(!mTooSlowWithoutShadows) // if we were not previously above the full impostor cap + { + bool render_friend_or_exception = ( alwaysRenderFriends && LLAvatarTracker::instance().isBuddy( id ) ) || + ( getVisualMuteSettings() == LLVOAvatar::AV_ALWAYS_RENDER ); + if( (!isSelf() || allowSelfImpostor) && !render_friend_or_exception ) + { + // Note: slow rendering Friends still get their shadows zapped. + mTooSlowWithoutShadows = (LLPerfStats::raw_to_ns(render_geom_time_raw) >= LLPerfStats::renderAvatarMaxART_ns); + } + } + } + else + { + // LL_INFOS() << this->getFullname() << " ("<< (combined?"combined":"geometry") << ") good render time = " << LLPerfStats::raw_to_ns(render_time_raw) << " vs ("<< LLVOAvatar::sRenderTimeCap_ns << " set @ " << mLastARTUpdateFrame << LL_ENDL; + mTooSlow = false; + mTooSlowWithoutShadows = false; + } + if(mTooSlow) + { + LLPerfStats::tunedAvatars++; // increment the number of avatars that have been tweaked. + } +} + //----------------------------------------------------------------------------- // findMotion() //----------------------------------------------------------------------------- diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index e9c3d48a78..a27327d8a3 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -350,6 +350,18 @@ public: //-------------------------------------------------------------------- public: BOOL isFullyLoaded() const; + + // check and return current state relative to limits + // default will test only the geometry (combined=false). + // this allows us to disable shadows separately on complex avatars. + inline bool isTooSlowWithShadows() const {return mTooSlow;}; + inline bool isTooSlowWithoutShadows() const {return mTooSlowWithoutShadows;}; + inline bool isTooSlow(bool combined = false) const + { + return(combined?mTooSlow:mTooSlowWithoutShadows); + } + void updateTooSlow(); + bool isTooComplex() const; bool visualParamWeightsAreDefault(); virtual bool getIsCloud() const; @@ -369,6 +381,7 @@ public: void logMetricsTimerRecord(const std::string& phase_name, F32 elapsed, bool completed); void calcMutedAVColor(); + void markARTStale(); protected: LLViewerStats::PhaseMap& getPhases() { return mPhases; } @@ -390,6 +403,15 @@ private: LLFrameTimer mFullyLoadedTimer; LLFrameTimer mRuthTimer; + U32 mLastARTUpdateFrame{0}; + U64 mRenderTime{0}; + U64 mGeomTime{0}; + bool mARTStale{true}; + bool mARTCapped{false}; + // variables to hold "slowness" status + bool mTooSlow{false}; + bool mTooSlowWithoutShadows{false}; + private: LLViewerStats::PhaseMap mPhases; @@ -1145,6 +1167,8 @@ public: // COF version of last appearance message received for this av. S32 mLastUpdateReceivedCOFVersion; + U64 getLastART() const { return mRenderTime; } + /** Diagnostics ** ** *******************************************************************************/ diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index f4a938e57d..77d1511dcd 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -88,6 +88,7 @@ #include "llcallstack.h" #include "llsculptidsize.h" #include "llavatarappearancedefines.h" +#include "llperfstats.h" const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; const F32 FORCE_CULL_AREA = 8.f; @@ -5589,6 +5590,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LL_PROFILE_ZONE_NAMED("rebuildGeom - face list"); //get all the faces into a list + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin(); drawable_iter != group->getDataEnd(); ++drawable_iter) { @@ -5620,6 +5622,11 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) continue; } + if(vobj->isAttachment()) + { + trackAttachments( vobj, drawablep->isState(LLDrawable::RIGGED),&ratPtr); + } + LLVolume* volume = vobj->getVolume(); if (volume) { @@ -6010,6 +6017,7 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) U32 buffer_count = 0; + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr{}; for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin(); drawable_iter != group->getDataEnd(); ++drawable_iter) { LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable(); @@ -6019,6 +6027,11 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) LLVOVolume* vobj = drawablep->getVOVolume(); if (!vobj) continue; + + if (vobj->isAttachment()) + { + trackAttachments( vobj, drawablep->isState(LLDrawable::RIGGED), &ratPtr ); + } if (debugLoggingEnabled("AnimatedObjectsLinkset")) { @@ -6458,10 +6471,16 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace U32 indices_index = 0; U16 index_offset = 0; - while (face_iter < i) + std::unique_ptr<LLPerfStats::RecordAttachmentTime> ratPtr; + while (face_iter < i) { //update face indices for new buffer facep = *face_iter; + LLViewerObject* vobj = facep->getViewerObject(); + if(vobj && vobj->isAttachment()) + { + trackAttachments(vobj, LLPipeline::sShadowRender, &ratPtr); + } if (buffer.isNull()) { // Bulk allocation failed diff --git a/indra/newview/skins/default/xui/en/floater_performance.xml b/indra/newview/skins/default/xui/en/floater_performance.xml index bf2623f356..ee88701037 100644 --- a/indra/newview/skins/default/xui/en/floater_performance.xml +++ b/indra/newview/skins/default/xui/en/floater_performance.xml @@ -277,6 +277,52 @@ top="19" right="-20"/> </panel> + <panel + bg_alpha_color="PanelGray" + background_visible="true" + background_opaque="false" + border="true" + bevel_style="none" + follows="left|top" + height="50" + width="560" + name="autoadjustments_subpanel" + layout="topleft" + top_pad="10"> + <text + follows="left|top" + font="SansSerifLarge" + text_color="White" + height="20" + layout="topleft" + left="10" + name="auto_adj_lbl" + top="7" + width="175"> + Preferred frame rate + </text> + <text + follows="left|top" + font="SansSerif" + text_color="White" + height="20" + layout="topleft" + left="10" + name="auto_adj_desc" + top_pad="0" + width="485"> + Allow automatic adjustments to reach your preferred frame rate (advanced). + </text> + <icon + height="16" + width="16" + image_name="Arrow_Right_Off" + mouse_opaque="true" + name="icon_arrow4" + follows="right|top" + top="19" + right="-20"/> + </panel> </panel> <panel filename="panel_performance_nearby.xml" @@ -310,4 +356,12 @@ name="panel_performance_huds" visible="false" top="55" /> + <panel + filename="panel_performance_autoadjustments.xml" + follows="all" + layout="topleft" + left="0" + name="panel_performance_autoadjustments" + visible="false" + top="55" /> </floater> diff --git a/indra/newview/skins/default/xui/en/panel_performance_autoadjustments.xml b/indra/newview/skins/default/xui/en/panel_performance_autoadjustments.xml new file mode 100644 index 0000000000..10ac4b98b7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_performance_autoadjustments.xml @@ -0,0 +1,272 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + bevel_style="none" + follows="left|top" + height="580" + width="580" + name="panel_performance_autoadjustments" + layout="topleft" + left="0" + top="0"> + <button + height="16" + width="16" + layout="topleft" + mouse_opaque="true" + follows="left|top" + name="back_btn" + top="7" + image_selected="Arrow_Left_Off" + image_pressed="Arrow_Left_Off" + image_unselected="Arrow_Left_Off" + left="15" + is_toggle="true"> + </button> + <text + follows="left|top" + height="20" + layout="topleft" + left_pad="3" + top="10" + name="back_lbl" + width="40"> + Back + </text> + <text + follows="left|top" + font="SansSerifLarge" + text_color="white" + height="20" + layout="topleft" + left="20" + top_pad="10" + name="settings_title" + width="300"> + Preferred frame rate + </text> + <view_border + bevel_style="in" + height="0" + layout="topleft" + name="border0" + top_pad="15" + left="20" + width="540"/> + <text + follows="left|top" + font="SansSerif" + text_color="White" + height="20" + layout="topleft" + left="20" + name="targetfps_desc" + wrap="true" + width="140" + top_pad="20"> + Desired frame rate + </text> + <spinner + name="target_fps" + control_name="TargetFPS" + font="SansSerifLarge" + tool_tip="The viewer will attempt to achieve this by adjusting your graphics settings." + layout="topleft" + follows="right|top" + left_pad="5" + top_delta="0" + height="25" + visible="true" + decimal_digits="0" + increment="1" + initial_value="25" + max_val="300" + min_val="1" + width="48" + label_width="0" /> + <button + control_name="AutoTuneFPS" + follows="top|right" + height="24" + initial_value="false" + image_pressed="PushButton_Press" + image_pressed_selected="PushButton_Selected_Press" + image_selected="PushButton_Selected_Press" + is_toggle="true" + label="Start" + label_selected="Stop" + layout="topleft" + left_pad="10" + name="AutoTuneFPS" + top_delta="-1" + tool_tip="The viewer will attempt to adjust settings to meet the target FPS." + width="72"> + </button> + <check_box + control_name="AutoTuneLock" + follows="top|right" + height="20" + initial_value="true" + image_pressed="PushButton_Press" + image_pressed_selected="PushButton_Selected_Press" + image_selected="PushButton_Selected_Press" + is_toggle="true" + label="Continuous" + layout="topleft" + left_pad="10" + name="AutoTuneContinuous" + top_delta="0" + tool_tip="The viewer will continually adapt the settings to meet the target FPS until stopped even with the floater closed. When disabled clicking the Auto Tune button will adjust for the current settings then stop." + width="64"> + </check_box> + <text + follows="left|top" + font="SansSerif" + text_color="White" + height="20" + layout="topleft" + left="20" + name="settings_desc" + top_pad="20" + wrap="true" + width="150"> + Settings affect + </text> + <combo_box + follows="top|left" + font="SansSerif" + height="20" + layout="topleft" + left_pad="15" + control_name="TuningFPSStrategy" + name="TuningFPSStrategy" + width="130"> + <combo_box.item + label="Avatars Only" + name="av_only" + value="0" /> + <combo_box.item + label="Avatars and Scene" + name="av_and_scene" + value="1" /> + </combo_box> + <view_border + bevel_style="in" + height="0" + layout="topleft" + name="border1" + top_pad="20" + left="20" + width="540"/> + <text + follows="left|top" + font="SansSerifSmall" + text_color="White" + height="18" + layout="topleft" + left="20" + top_pad="15" + name="simplify_dist_desc" + width="580"> + Reducing detail shown on avatars that are far away will improve graphics speed. + </text> + <check_box + control_name="AutoTuneImpostorByDistEnabled" + height="19" + label="Simplify avatars beyond" + label_text.text_color="White" + layout="topleft" + follows="top|left" + name="AutoTuneImpostorByDistEnabled" + tool_tip="When enabled the viewer will adjust the MaxNonImpostors setting to limit fully rendered avatars to those within the defined radius." + top_pad="15" + width="190" /> + <spinner + control_name="AutoTuneImpostorFarAwayDistance" + height="20" + layout="topleft" + follows="top|left" + name="ffa_autotune" + left_pad="20" + decimal_digits="2" + min_val="16" + max_val="256" + width="60" > + </spinner> + <text + follows="left|top" + font="SansSerifSmall" + text_color="White" + height="18" + layout="topleft" + left_pad="10" + name="dist_meters" + width="70"> + meters + </text> + <view_border + bevel_style="in" + height="0" + layout="topleft" + name="border2" + top_pad="20" + left="20" + width="540"/> + <text + follows="left|top" + font="SansSerifSmall" + text_color="White" + height="18" + layout="topleft" + left="20" + top_pad="20" + name="dist_limits_desc" + width="580"> + Choose the distance range that automatic settings will affect. + </text> + <text + follows="left|top" + font="SansSerifSmall" + text_color="White" + height="18" + layout="topleft" + top_pad="15" + name="min_dist_lbl" + width="120"> + Minimum distance + </text> + <spinner + control_name="AutoTuneRenderFarClipMin" + height="20" + layout="topleft" + left_pad="15" + follows="top|left" + name="min_dd_autotune" + decimal_digits="2" + min_val="32" + max_val="256" + width="60"> + </spinner> + <text + follows="left|top" + font="SansSerifSmall" + text_color="White" + height="18" + layout="topleft" + top_pad="15" + left="20" + name="pref_dist_lbl" + width="120"> + Maximum distance + </text> + <spinner + control_name="AutoTuneRenderFarClipTarget" + height="20" + layout="topleft" + follows="top|left" + name="pref_dd_autotune" + left_pad="15" + min_val="32" + max_val="256" + width="60"> + </spinner> +</panel> |