diff options
| author | Jonathan "Geenz" Goodman <geenz@lindenlab.com> | 2026-07-23 08:19:10 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-23 08:19:10 -0400 |
| commit | bcfda3b68a95bbbfb8b53f6313929fc5aeaa591c (patch) | |
| tree | 5982185abc453619dddf6f234e1bc6802e1d69b8 /indra/newview/llviewertexturelist.cpp | |
| parent | 8f2d07b717db2997c5701977ef3d65a56c811771 (diff) | |
| parent | f364b5c882da959284e8fc1b3bc80810a7374969 (diff) | |
Merge pull request #6029 from secondlife/geenz/26.3-texture-streaming-revert
Remove texture streaming changes from 26.3
Diffstat (limited to 'indra/newview/llviewertexturelist.cpp')
| -rw-r--r-- | indra/newview/llviewertexturelist.cpp | 513 |
1 files changed, 108 insertions, 405 deletions
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index ad05a0273b..e4fd947892 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -47,7 +47,6 @@ #include "message.h" #include "lldrawpoolbump.h" // to init bumpmap images -#include "llagentcamera.h" #include "lltexturecache.h" #include "lltexturefetch.h" #include "llviewercontrol.h" @@ -62,11 +61,6 @@ #include "lltracerecording.h" #include "llviewerdisplay.h" #include "llviewerwindow.h" -#include "llsurface.h" -#include "llvoavatarself.h" -#include "lldrawable.h" -#include "llvovolume.h" -#include "llviewertextureanim.h" #include "llprogressview.h" //////////////////////////////////////////////////////////////////////////// @@ -99,20 +93,6 @@ LLTextureKey::LLTextureKey(LLUUID id, ETexListType tex_type) /////////////////////////////////////////////////////////////////////////////// -// eTexIndex -> TextureChannel* index (0=Normal, 1=BaseColor, 2=Specular, -// 3=Emissive). Single source of truth - route all channel-priority lookups -// through this table. -const S32 LLViewerTextureList::sChannelToPriority[LLRender::NUM_TEXTURE_CHANNELS] = -{ - 1, // DIFFUSE_MAP (0) -> Y (diffuse) - 0, // NORMAL_MAP / ALT_DIFFUSE (1) -> X (normals) - 2, // SPECULAR_MAP (2) -> Z (specular/metallic) - 1, // BASECOLOR_MAP (3) -> Y (diffuse) - 2, // METALLIC_ROUGHNESS_MAP (4) -> Z (specular/metallic) - 0, // GLTF_NORMAL_MAP (5) -> X (normals) - 3, // EMISSIVE_MAP (6) -> W (emissive) -}; - LLViewerTextureList::LLViewerTextureList() : mForceResetTextureStats(false), mInitialized(false) @@ -378,12 +358,6 @@ void LLViewerTextureList::shutdown() } mFastCacheList.clear(); - for (auto& img : mFastFetchList) - { - img->mInFastFetchList = false; - } - mFastFetchList.clear(); - mUUIDMap.clear(); mImageList.clear(); @@ -877,36 +851,6 @@ void LLViewerTextureList::updateImages(F32 max_time) remaining_time -= updateImagesFetchTextures(remaining_time); remaining_time = llmax(remaining_time, min_time); - // Fast pump: advance every in-flight fetch each frame so results are - // collected and creates scheduled the frame they're ready, instead of - // one state transition per round-robin visit. Cheap - no face scans - - // and bounded by the fetch worker's own concurrency. - LLTimer fast_fetch_timer; - S32 min_count = 32; - for (size_t i = 0; i < mFastFetchList.size(); ) - { - LLViewerFetchedTexture* imagep = mFastFetchList[i]; - if (imagep->getNumRefs() > 1) - { - imagep->updateFetch(); - } - if (imagep->getNumRefs() <= 1 || (!imagep->isFetching() && !imagep->hasFetcher())) - { - imagep->mInFastFetchList = false; - mFastFetchList[i] = mFastFetchList.back(); - mFastFetchList.pop_back(); - } - else - { - ++i; - } - - if (fast_fetch_timer.getElapsedTimeF32() > remaining_time && --min_count <= 0) - { - break; - } - } - //handle results from decode threads updateImagesCreateTextures(remaining_time); @@ -951,383 +895,144 @@ void LLViewerTextureList::clearFetchingRequests() extern bool gCubeSnapshot; -// Refresh a face's cached per-channel streaming coverage (face->mStreamVSize). -// This is the most-demanding-point measurement plus each channel's own UV -// repeat source, computed ONCE per face per update cadence and shared by every -// texture registered on the face. Doing the material/transform pointer chases -// per texture visit instead made updateImageDecodePriority several times more -// expensive per face than develop's, and since the round-robin runs in a fixed -// per-frame time slice, that directly cut how many textures advance their -// load state each frame - the whole pipeline paced slower. -static void update_face_stream_vsize(LLFace* face) -{ - // Bounds on the per-face UV repeat-area divisor (mined from the old - // getTextureVirtualSize texel_area clamp [1/64, 128]): atlas/crop boost - // capped at 64x (3 mips finer), tiling penalty at 128x (3.5 mips - // coarser) so pathological UV scales can't explode either direction. - constexpr F32 MIN_REPEAT_AREA = 1.f / 64.f; - constexpr F32 MAX_REPEAT_AREA = 128.f; - - LLViewerObject* objp = face->getViewerObject(); - - // Most-demanding-point measurement: the spec is that the LOWEST pixel:texel - // ratio governs, so pixel density is evaluated at the face's NEAREST point - // and applied to the face's true world area. A whole-face average - // (bounding-disc pixel area) under-resolves perspective surfaces: on a - // floor, the tile at your feet covers far more screen than the average - // tile, and the GPU samples fine mips right there. - const LLVector4a* ext = face->isState(LLFace::RIGGED) ? face->mRiggedExtents : face->mExtents; - LLVector4a diag; - diag.setSub(ext[1], ext[0]); - // World area of the face ~ product of the two largest AABB dims (max - // pairwise product; robust for flat faces). - F32 dx = diag[0], dy = diag[1], dz = diag[2]; - F32 area_world = llmax(dx * dy, llmax(dx * dz, dy * dz)); - // Pixels per meter at the nearest point. Distance floored: nearer than - // this the screen clamp below governs anyway. - F32 dist = llmax(face->mDistanceToCamera, 0.5f); - F32 ppm = LLDrawable::sCurPixelAngle / dist; - F32 face_px = area_world * ppm * ppm; - if (face_px <= 0.f) - { - // Degenerate extents: the face hasn't been through a geometry build - // yet (or a rigged face has no rigged extents) - it isn't renderable, - // so it must not be measured. Zero marks "skip": an invented - // placeholder value would become the texture's least-demanding "use" - // and, under TextureDownrezCoverageBias, drag the whole texture to - // its deepest mip (and it poisoned BP and PBR asymmetrically, since - // the two register faces at different points in the geometry - // lifecycle). - for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch) - { - face->mStreamVSize[ch] = 0.f; - } - return; - } - - S32 te_offset = face->getTEOffset(); // offset is -1 if not inited - const LLTextureEntry* te = (te_offset < 0 || te_offset >= objp->getNumTEs()) ? nullptr : objp->getTE(te_offset); - - // Shared, channel-independent chases - hoisted out of the channel loop. - const LLGLTFMaterial* gltf_mat = te ? te->getGLTFRenderMaterial() : nullptr; - const LLMaterial* mat = te ? te->getMaterialParams().get() : nullptr; - - // Continuously-animated scale (llSetTextureAnim SCALE) bypasses both - // static sources via mTextureMatrix - the live animated values win. - bool anim_scale = false; - F32 anim_ss = 0.f, anim_st = 0.f; - if (te) - { - if (LLVOVolume* vvo = face->getDrawable() ? face->getDrawable()->getVOVolume() : nullptr) - { - LLViewerTextureAnim* anim = vvo->mTextureAnimp; - if (anim && (anim->mMode & LLTextureAnim::ON) && (anim->mMode & LLTextureAnim::SCALE) - && (anim->mFace < 0 || anim->mFace == te_offset)) - { - anim_scale = true; - anim_ss = anim->mScaleS; - anim_st = anim->mScaleT; - } - } - } - - // Mesh atlas sub-rect: a face whose intrinsic UVs span only part of - // [0,1]^2 shows that fraction of the image. Applies identically to all - // channels - the per-channel transforms stack on the raw face UVs. - F32 span = 1.f; - if (te) - { - if (LLVolume* vol = objp->getVolume()) - { - if (te_offset >= 0 && te_offset < vol->getNumVolumeFaces()) - { - const LLVolumeFace& vf = vol->getVolumeFace(te_offset); - F32 s = fabsf((vf.mTexCoordExtents[1].mV[0] - vf.mTexCoordExtents[0].mV[0]) - * (vf.mTexCoordExtents[1].mV[1] - vf.mTexCoordExtents[0].mV[1])); - if (s > 0.f) - { - span = s; - } - } - } - } - - // Avatar bonus: worn attachments get a coverage multiplier - avatars are - // what people look at, and rigged extents make attachment coverage - // measurement unreliable anyway. Multiplicative, not a slam. - static LLCachedControl<F32> avatar_boost(gSavedSettings, "TextureAvatarBoost", 4.f); - const F32 boost = objp->isAttachment() ? llmax((F32)avatar_boost, 1.f) : 1.f; - - for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch) - { - // Effective UV repeat AREA: the tiling term of texels-drawn-per- - // screen-pixel. More tiling => each tile smaller on screen => coarser - // mips suffice (penalty). Repeats < 1 (atlas/crop) => whole-image - // residency for a sub-rect legitimately demands more than its screen - // coverage (boost). - F32 repeats = 1.f; - if (te) - { - // UV scale source: every channel reads the repeat values ITS - // renderer actually applies. diffuse -> TE scale; Blinn - // normal/spec -> LLMaterial per-map repeats; PBR channels -> KHR - // texture_transform scale. Fallback is the TE scale - never a - // silent hardcoded 1. - F32 scale_s = te->getScaleS(); - F32 scale_t = te->getScaleT(); - if (ch >= LLRender::BASECOLOR_MAP) - { - // LLRender channel -> LLGLTFMaterial::TextureInfo - static const S32 gltf_info[4] = { - LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR, // BASECOLOR_MAP (3) - LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS, // METALLIC_ROUGHNESS_MAP (4) - LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL, // GLTF_NORMAL_MAP (5) - LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE, // EMISSIVE_MAP (6) - }; - if (gltf_mat) - { - const LLVector2& s = gltf_mat->mTextureTransform[gltf_info[ch - LLRender::BASECOLOR_MAP]].mScale; - scale_s = s.mV[0]; - scale_t = s.mV[1]; - } - } - else if (ch == LLRender::NORMAL_MAP || ch == LLRender::SPECULAR_MAP) - { - // Blinn-Phong normal/specular maps carry their own repeats in - // LLMaterial - the renderer builds their texture matrices - // from these, NOT from the TE's diffuse scale. - if (mat) - { - if (ch == LLRender::NORMAL_MAP) - { - mat->getNormalRepeat(scale_s, scale_t); - } - else - { - mat->getSpecularRepeat(scale_s, scale_t); - } - } - } - - if (anim_scale) - { - scale_s = anim_ss; - scale_t = anim_st; - } - - repeats = fabsf(scale_s * scale_t) * span; - } - - repeats = llclamp(repeats, MIN_REPEAT_AREA, MAX_REPEAT_AREA); - - // Apply the two sides of the repeat term in the right order relative - // to the screen clamp: tiling (repeats > 1) divides the nearest-point - // footprint BEFORE the clamp (one tile can't draw more pixels than - // the screen); atlas/crop (repeats < 1) boosts AFTER it (whole-image - // residency for a crop legitimately demands more than its screen - // coverage). - F32 tiling = llmax(repeats, 1.f); - F32 crop = llmin(repeats, 1.f); - face->mStreamVSize[ch] = llmin(face_px / tiling, LLViewerTexture::sWindowPixelArea) / crop * boost; - } -} - void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imagep, bool flush_images) { llassert(!gCubeSnapshot); - // Refresh spotlight priorities first: light projector textures register as - // LIGHT_TEX volumes (no faces), and both their fetch priority - // (addTextureStats inside updateSpotLightPriority) and their coverage - // (folded into the block below) derive from mSpotLightPriority. - for (S32 vi = 0; vi < imagep->getNumVolumes(LLRender::LIGHT_TEX); ++vi) - { - LLVOVolume* volume = (*imagep->getVolumeList(LLRender::LIGHT_TEX))[vi]; - volume->updateSpotLightPriority(); - } + constexpr F32 BIAS_TRS_OUT_OF_SCREEN = 1.5f; + constexpr F32 BIAS_TRS_ON_SCREEN = 1.f; if (imagep->getBoostLevel() < LLViewerFetchedTexture::BOOST_HIGH) // don't bother checking face list for boosted textures { - // Per priority bucket (0=Normal, 1=BaseColor, 2=Specular, 3=Emissive): - // the HIGHEST per-face effective coverage (= the lowest texels-per-pixel - // use, the most demanding variant - drives desired discard) and the - // LOWEST positive coverage (= the highest texels-per-pixel use, the most - // oversampled variant - available for downrez-bias policy). The overall - // max is the fetch priority (raw - no bias). A bucket with faces but - // zero coverage (all off-screen) publishes 0, which - // computeDesiredDiscard reads as "coarsest mip". - F32 channel_coverage[4] = { 0.f, 0.f, 0.f, 0.f }; - F32 channel_coverage_min[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; - bool bucket_used[4] = { false, false, false, false }; - F32 max_coverage = 0.f; - bool on_screen = false; // any face's projected disc overlaps the screen - bool any_face = false; - F32 min_overflow = FLT_MAX; // least out-of-frustum use across faces + static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.0095f); + static LLCachedControl<F32> texture_scale_max(gSavedSettings, "TextureScaleMaxAreaFactor", 25.f); + F32 max_vsize = 0.f; + bool on_screen = false; U32 face_count = 0; - const U32 max_faces_to_check = 1024; + U32 max_faces_to_check = 1024; + + // get adjusted bias based on image resolution + LLImageGL* img = imagep->getGLTexture(); + F32 max_discard = F32(img ? img->getMaxDiscardLevel() : MAX_DISCARD_LEVEL); + F32 bias = llclamp(max_discard - 2.f, 1.f, LLViewerTexture::sDesiredDiscardBias); + + // convert bias into a vsize scaler + bias = (F32) llroundf(powf(4, bias - 1.f)); - // Cheap first pass: which buckets is this texture used in, and how many - // faces total. No per-face geometry work. + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i) { - U32 n = imagep->getNumFaces(i); - face_count += n; - if (n > 0) - { - S32 b = sChannelToPriority[i]; - if (b >= 0 && b < 4) bucket_used[b] = true; - } - } + face_count += imagep->getNumFaces(i); + S32 faces_to_check = (face_count > max_faces_to_check) ? 0 : imagep->getNumFaces(i); - if (face_count > max_faces_to_check) - { - // Used in so many places that scanning the face list isn't worth it - // (and isn't time-sliced) - treat as full-screen so it loads sharp. - for (S32 b = 0; b < 4; ++b) - { - if (bucket_used[b]) - { - channel_coverage[b] = (F32)MAX_IMAGE_AREA; - channel_coverage_min[b] = (F32)MAX_IMAGE_AREA; - } - } - max_coverage = (F32)MAX_IMAGE_AREA; - } - else - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i) + for (S32 fi = 0; fi < faces_to_check; ++fi) { - const S32 bucket = sChannelToPriority[i]; - const U32 num_faces = imagep->getNumFaces(i); - for (U32 fi = 0; fi < num_faces; ++fi) - { - LLFace* face = (*(imagep->getFaceList(i)))[fi]; - if (!face || !face->getViewerObject()) - { - continue; - } + LLFace* face = (*(imagep->getFaceList(i)))[fi]; + if (face && face->getViewerObject()) + { F32 radius; F32 cos_angle_to_view_dir; + if ((gFrameCount - face->mLastTextureUpdate) > 10) - { // refresh the face's geometry + cached coverage at most once every - // 10 frames; every texture/channel sharing this face (GLTF and - // Blinn-Phong materials) reuses the cache instead of redoing the - // measurement. (calcPixelArea maintains face->mInFrustum itself.) - face->calcPixelArea(cos_angle_to_view_dir, radius); - update_face_stream_vsize(face); + { // only call calcPixelArea at most once every 10 frames for a given face + // this helps eliminate redundant calls to calcPixelArea for faces that have multiple textures + // assigned to them, such as is the case with GLTF materials or Blinn-Phong materials + face->mInFrustum = face->calcPixelArea(cos_angle_to_view_dir, radius); face->mLastTextureUpdate = gFrameCount; } - // Cached measurement - see update_face_stream_vsize above. - // Zero = degenerate extents / not yet through a geometry - // build: not renderable, must not be measured (a - // placeholder value would poison the per-bucket MIN bound - // and drag the texture to its deepest mip). - F32 vsize = face->mStreamVSize[i]; - if (vsize <= 0.f) + F32 vsize = face->getPixelArea(); + + on_screen |= face->mInFrustum; + + // Scale desired texture resolution higher or lower depending on texture scale + // + // Minimum usage examples: a 1024x1024 texture with aplhabet (texture atlas), + // runing string shows one letter at a time. If texture has ten 100px symbols + // per side, minimal scale is (100/1024)^2 = 0.0095 + // + // Maximum usage examples: huge chunk of terrain repeats texture + // TODO: make this work with the GLTF texture transforms + S32 te_offset = face->getTEOffset(); // offset is -1 if not inited + LLViewerObject* objp = face->getViewerObject(); + const LLTextureEntry* te = (te_offset < 0 || te_offset >= objp->getNumTEs()) ? nullptr : objp->getTE(te_offset); + F32 min_scale = te ? llmin(fabsf(te->getScaleS()), fabsf(te->getScaleT())) : 1.f; + min_scale = llclamp(min_scale * min_scale, texture_scale_min(), texture_scale_max()); + vsize /= min_scale; + + // apply bias to offscreen faces all the time, but only to onscreen faces when bias is large + // use mImportanceToCamera to make bias switch a bit more gradual + if (!face->mInFrustum || LLViewerTexture::sDesiredDiscardBias > 1.9f + face->mImportanceToCamera / 2.f) + { + vsize /= bias; + } + + // boost resolution of textures that are important to the camera + if (face->mInFrustum) { - continue; + static LLCachedControl<F32> texture_camera_boost(gSavedSettings, "TextureCameraBoost", 8.f); + vsize *= llmax(face->mImportanceToCamera*texture_camera_boost, 1.f); } - any_face = true; - on_screen = on_screen || face->mInFrustum; - min_overflow = llmin(min_overflow, face->mFrustumOverflow); + max_vsize = llmax(max_vsize, vsize); - if (bucket >= 0 && bucket < 4) + // addTextureStats limits size to sMaxVirtualSize + if (max_vsize >= LLViewerFetchedTexture::sMaxVirtualSize + && (on_screen || LLViewerTexture::sDesiredDiscardBias <= BIAS_TRS_ON_SCREEN)) { - channel_coverage[bucket] = llmax(channel_coverage[bucket], vsize); - if (vsize > 0.f) - { - channel_coverage_min[bucket] = llmin(channel_coverage_min[bucket], vsize); - } + break; } - max_coverage = llmax(max_coverage, vsize); } } - } - // Terrain detail textures register no faces (LLVOSurfacePatch - // addFace(NULL)); synthesize coverage from the nearest visible patch so - // they degrade with distance like everything else. - if (face_count == 0 && imagep->getBoostLevel() == LLGLTexture::BOOST_TERRAIN) - { - F32 nearest = LLSurface::sNearestVisiblePatchDistance; - F32 draw_distance = llmax(gAgentCamera.mDrawDistance, 1.f); - F32 near_frac = (nearest < FLT_MAX) ? llclampf(1.f - nearest / draw_distance) : 0.f; - // Floor so terrain never collapses to nothing at draw distance. - F32 cov = LLViewerTexture::sWindowPixelArea * llmax(near_frac, 0.05f); - channel_coverage[1] = cov; // terrain detail maps are diffuse / base color - channel_coverage_min[1] = cov; - max_coverage = cov; - } - // Baked avatar textures render on system-avatar body meshes whose - // joint meshes never register faces with the texture list - // (LLAvatarJointMesh::setTexture just stores the pointer). - // LLVOAvatar::updateTextures feeds their on-screen pixel area through - // addTextureStats every frame - use that as BaseColor coverage, with - // the same avatar bonus attachments get above. - else if (face_count == 0 - && (imagep->getFTType() == FTT_SERVER_BAKE || imagep->getFTType() == FTT_HOST_BAKE)) - { - static LLCachedControl<F32> avatar_boost(gSavedSettings, "TextureAvatarBoost", 4.f); - F32 cov = llmin(imagep->getMaxVirtualSize(), LLViewerTexture::sWindowPixelArea) - * llmax((F32)avatar_boost, 1.f); - if (cov > 0.f) + if (max_vsize >= LLViewerFetchedTexture::sMaxVirtualSize + && (on_screen || LLViewerTexture::sDesiredDiscardBias <= BIAS_TRS_ON_SCREEN)) { - channel_coverage[1] = cov; - channel_coverage_min[1] = cov; - max_coverage = cov; + break; } } - // Light projector textures register as LIGHT_TEX volumes, not faces. - // mSpotLightPriority (refreshed above) is already a screen-pixel-area - // estimate of the lit radius - fold it in as BaseColor coverage so - // projectors stream view-dependently like everything else. - for (S32 vi = 0; vi < imagep->getNumVolumes(LLRender::LIGHT_TEX); ++vi) - { - LLVOVolume* volume = (*imagep->getVolumeList(LLRender::LIGHT_TEX))[vi]; - F32 cov = llmin(volume->getSpotLightPriority(), LLViewerTexture::sWindowPixelArea); - if (cov > 0.f) - { - channel_coverage[1] = llmax(channel_coverage[1], cov); - channel_coverage_min[1] = llmin(channel_coverage_min[1], cov); - max_coverage = llmax(max_coverage, cov); - } + if (face_count > max_faces_to_check) + { // this texture is used in so many places we should just boost it and not bother checking its vsize + // this is especially important because the above is not time sliced and can hit multiple ms for a single texture + max_vsize = MAX_IMAGE_AREA; } - imagep->addTextureStats(max_coverage); + if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE) + { // conditionally reset max virtual size for unboosted LOD_TEXTURES + // this is an alternative to decaying mMaxVirtualSize over time + // that keeps textures from continously downrezzing and uprezzing in the background - // Publish per-bucket coverage bounds for - // LLViewerLODTexture::computeDesiredDiscard. - for (S32 b = 0; b < 4; ++b) - { - imagep->mChannelCoverage[b] = channel_coverage[b]; - imagep->mChannelCoverageMin[b] = (channel_coverage_min[b] == FLT_MAX) ? 0.f : channel_coverage_min[b]; + if (LLViewerTexture::sDesiredDiscardBias > BIAS_TRS_OUT_OF_SCREEN || + (!on_screen && LLViewerTexture::sDesiredDiscardBias > BIAS_TRS_ON_SCREEN)) + { + imagep->mMaxVirtualSize = 0.f; + } } - // Fetch admission signal: false only when faces were actually scanned and - // every one projects off screen. Textures with no scannable faces (bakes, - // spotlights, the >1024-face boost path, not-yet-built geometry) stay - // eligible - blocking them is what stalls load-in. - imagep->mOnScreen = on_screen || !any_face; - // Least out-of-frustum use governs the allowance falloff; unknown = 0 - // (no penalty), same reasoning as mOnScreen. - imagep->mFrustumOverflow = any_face ? min_overflow : 0.f; + imagep->addTextureStats(max_vsize); } #if 0 - imagep->setDebugText(llformat("%d/%d -- %d/%d", + imagep->setDebugText(llformat("%d/%d - %d/%d -- %d/%d", + (S32)sqrtf(max_vsize), + (S32)sqrtf(imagep->mMaxVirtualSize), imagep->getDiscardLevel(), imagep->getDesiredDiscardLevel(), imagep->getWidth(), imagep->getFullWidth())); #endif + // make sure to addTextureStats for any spotlights that are using this texture + for (S32 vi = 0; vi < imagep->getNumVolumes(LLRender::LIGHT_TEX); ++vi) + { + LLVOVolume* volume = (*imagep->getVolumeList(LLRender::LIGHT_TEX))[vi]; + volume->updateSpotLightPriority(); + } + F32 max_inactive_time = 20.f; // inactive time before deleting saved raw image S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, and 1 for "entries" in updateImagesFetchTextures @@ -1417,7 +1122,8 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) imagep->postCreateTexture(); imagep->mCreatePending = false; - if (imagep->hasGLTexture() && imagep->getDiscardLevel() < imagep->getDesiredDiscardLevel()) + if (imagep->hasGLTexture() && imagep->getDiscardLevel() < imagep->getDesiredDiscardLevel() && + (imagep->getDesiredDiscardLevel() <= MAX_DISCARD_LEVEL)) { // NOTE: this may happen if the desired discard reduces while a decode is in progress and does not // necessarily indicate a problem, but if log occurrences excede that of dsiplay_stats: FPS, @@ -1442,16 +1148,13 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) gCopyProgram.bind(); gPipeline.mScreenTriangleVB->setBuffer(); - // give time to downscaling first - if mDownScaleQueue is not empty, we're running out of memory and need + // give time to downscaling first -- if mDownScaleQueue is not empty, we're running out of memory and need // to free up memory by discarding off screen textures quickly - // Drain rate scales with both pending creates and the downscale - // queue itself. Without the queue term, a backlog of evictions - // could only drain 5/frame regardless of size, and the system - // can't actually free VRAM fast enough under pressure. - S32 min_count = (S32)mCreateTextureList.size() / 20 - + (S32)mDownScaleQueue.size() / 5 - + 5; + // do at least 5 and make sure we don't get too far behind even if it violates + // the time limit. If we don't downscale quickly the viewer will hit swap and may + // freeze. + S32 min_count = (S32)mCreateTextureList.size() / 20 + 5; create_timer.reset(); while (!mDownScaleQueue.empty()) @@ -1553,6 +1256,18 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) //update MIN_UPDATE_COUNT or 5% of other textures, whichever is greater update_count = llmax((U32) MIN_UPDATE_COUNT, (U32) mUUIDMap.size()/20); + if (LLViewerTexture::sDesiredDiscardBias > 1.f + && LLViewerTexture::sBiasTexturesUpdated < (U32)mUUIDMap.size()) + { + // We are over memory target. Bias affects discard rates, so update + // existing textures agresively to free memory faster. + update_count = (S32)(update_count * LLViewerTexture::sDesiredDiscardBias); + + // This isn't particularly precise and can overshoot, but it doesn't need + // to be, just making sure it did a full circle and doesn't get stuck updating + // at bias = 4 with 4 times the rate permanently. + LLViewerTexture::sBiasTexturesUpdated += update_count; + } update_count = llmin(update_count, (U32) mUUIDMap.size()); { // copy entries out of UUID map to avoid iterator invalidation from deletion inside updateImageDecodeProiroty or updateFetch below @@ -1586,18 +1301,6 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) { updateImageDecodePriority(imagep); imagep->updateFetch(); - - // Fast-pump membership: textures with an active fetch get - // updateFetch every frame (in updateImages) instead of waiting - // ~a sweep period per state transition - that wait, times the - // 2-4 transitions a load needs, was the measured throughput - // ceiling. Purely additive: the sweep still pumps everything, - // so fetches started by any other path can never strand. - if ((imagep->isFetching() || imagep->hasFetcher()) && !imagep->mInFastFetchList) - { - imagep->mInFastFetchList = true; - mFastFetchList.push_back(imagep); - } } if (timer.getElapsedTimeF32() > max_time) |
