From a937b237de3651e79cdb517f14381a5bdd4c844b Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Thu, 9 Jul 2026 08:09:24 -0400 Subject: Geenz/texture loading speed (#5985) * Add more controls for texture loading budgets. Should yield much faster loading within a given FPS target - should generally self regulate depending on your framerate. * Harden texture pipeline against stalls and OOM. Generally makes texture loading faster, at the expense of some budgeting (which we weren't doing a great job at anyways). --- indra/newview/llviewerdisplay.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'indra/newview/llviewerdisplay.cpp') diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 0d50ba6fe2..be83fd0279 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -140,6 +140,27 @@ void render_disconnected_background(); void getProfileStatsContext(boost::json::object& stats); std::string getProfileStatsFilename(); +// Adaptive texture-pipeline budget: spend the frame-time headroom between the +// frame we're rendering and TextureLoadTargetFPS, clamped [2ms, max]. Headroom +// is measured (smoothed frame interval minus the pipeline's own last spend), +// so fast machines get big budgets and machines already at target hold the +// floor. Only consumed while queues have work - drain loops exit when empty. +static F32 sTexturePipelineSpent = 0.f; +static F32 texture_pipeline_budget() +{ + static LLCachedControl target_fps(gSavedSettings, "TextureLoadTargetFPS", 60.f); + static LLCachedControl max_ms(gSavedSettings, "TextureLoadBudgetMaxMS", 10.f); + static F32 smoothed_other = 0.008f; + F32 other = llmax(gFrameIntervalSeconds.value() - sTexturePipelineSpent, 0.f); + // A single multi-second hitch must not crater the budget for the following + // frames, so cap the sample before it enters the EMA. + other = llmin(other, 0.1f); + smoothed_other = smoothed_other * 0.9f + other * 0.1f; + F32 target_interval = 1.f / llclamp((F32)target_fps, 15.f, 240.f); + F32 headroom = target_interval - smoothed_other; + return llclamp(headroom, 0.002f, llclamp((F32)max_ms, 2.f, 50.f) * 0.001f); +} + void display_startup() { if ( !gViewerWindow @@ -500,9 +521,10 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) { LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("List"); - F32 max_image_decode_time = 0.050f * gFrameIntervalSeconds.value(); // 50 ms/second decode time - max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f); // min 2ms/frame, max 5ms/frame) + F32 max_image_decode_time = texture_pipeline_budget(); + LLTimer tex_timer; gTextureList.updateImages(max_image_decode_time); + sTexturePipelineSpent = tex_timer.getElapsedTimeF32(); } { @@ -862,9 +884,10 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) { LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("List"); - F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds.value(); // 50 ms/second decode time - max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f ); // min 2ms/frame, max 5ms/frame) + F32 max_image_decode_time = texture_pipeline_budget(); + LLTimer tex_timer; gTextureList.updateImages(max_image_decode_time); + sTexturePipelineSpent = tex_timer.getElapsedTimeF32(); } { -- cgit v1.3