diff options
Diffstat (limited to 'indra/newview')
131 files changed, 2817 insertions, 1310 deletions
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 0f9f025fe4..991d8e5c5f 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.1.12 +7.1.13 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4293fa3034..0c83355a81 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7794,6 +7794,17 @@      <key>Value</key>      <integer>0</integer>    </map> +  <key>RenderTextureVRAMDivisor</key> +  <map> +      <key>Comment</key> +      <string>Divisor for maximum amount of VRAM the viewer will use for textures. 1 = all the VRAM.  Used in conjunction with RenderMaxVRAMBudget.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>U32</string> +      <key>Value</key> +      <integer>2</integer> +  </map>    <key>RenderMinFreeMainMemoryThreshold</key>    <map>      <key>Comment</key> @@ -9065,6 +9076,17 @@      <key>Value</key>      <integer>1</integer>    </map> +    <key>RenderReflectionProbeCount</key> +    <map> +        <key>Comment</key> +        <string>Number of probes to render.  Maximum of 256.  Clamps to the nearest power of 2.</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>U32</string> +        <key>Value</key> +        <integer>256</integer> +    </map>    <key>RenderReflectionProbeResolution</key>    <map>      <key>Comment</key> @@ -11618,7 +11640,7 @@          <key>Type</key>          <string>F32</string>          <key>Value</key> -        <real>0.04</real> +        <real>0.0095</real>      </map>      <key>TextureScaleMaxAreaFactor</key>      <map> diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl index 94711be473..dba9c46332 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl @@ -80,6 +80,18 @@ vec3 atmosFragLightingLinear(vec3 light, vec3 additive, vec3 atten);  vec4 decodeNormal(vec4 norm); +vec3 clampHDRRange(vec3 color) +{ +    // Why do this? +    // There are situations where the color range will go to something insane - potentially producing infs and NaNs even. +    // This is a safety measure to prevent that. +    // As to the specific number there - allegedly some HDR displays expect values to be in the 0-11.2 range. Citation needed. +    // -Geenz 2025-03-05 +    color = mix(color, vec3(1), isinf(color)); +    color = mix(color, vec3(0.0), isnan(color)); +    return clamp(color, vec3(0.0), vec3(11.2)); +} +  float calcLegacyDistanceAttenuation(float distance, float falloff)  {      float dist_atten = 1.0 - clamp((distance + falloff)/(1.0 + falloff), 0.0, 1.0); diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl index 9797bcd2ce..4e737492a7 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl @@ -69,6 +69,8 @@ void dofSampleNear(inout vec4 diff, inout float w, float min_sc, vec2 tc)      w += wg;  } +vec3 clampHDRRange(vec3 color); +  void main()  {      vec2 tc = vary_fragcoord.xy; @@ -120,5 +122,6 @@ void main()          diff /= w;      } +    diff.rgb = clampHDRRange(diff.rgb);      frag_color = diff;  } diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl index befd2ae6da..4ccc6f54a8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl @@ -43,6 +43,8 @@ vec3 legacyGamma(vec3 color)      return c;  } +vec3 clampHDRRange(vec3 color); +  void main()  {      //this is the one of the rare spots where diffuseRect contains linear color values (not sRGB) @@ -53,6 +55,7 @@ void main()      diff.rgb = legacyGamma(diff.rgb);  #endif -    frag_color = max(diff, vec4(0)); +    diff.rgb = clampHDRRange(diff.rgb); +    frag_color = diff;  } diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl index 32b0a1ac8e..c05b4eed7a 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFF.glsl @@ -71,6 +71,7 @@ float noise(vec2 x) {  //============================= +vec3 clampHDRRange(vec3 color);  void main() @@ -84,6 +85,7 @@ void main()      diff.rgb += nz*0.003;  #endif +    diff.rgb = clampHDRRange(diff.rgb);      frag_color = diff;      gl_FragDepth = texture(depthMap, vary_fragcoord.xy).r; diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl index fc6d4d7727..1f01c7f16a 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl @@ -28,138 +28,13 @@  out vec4 frag_color;  uniform sampler2D diffuseRect; -uniform sampler2D exposureMap; -uniform vec2 screen_res;  in vec2 vary_fragcoord;  vec3 linear_to_srgb(vec3 cl); +vec3 toneMap(vec3 color); -//=============================================================== -// tone mapping taken from Khronos sample implementation -//=============================================================== - -// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT -const mat3 ACESInputMat = mat3 -( -    0.59719, 0.07600, 0.02840, -    0.35458, 0.90834, 0.13383, -    0.04823, 0.01566, 0.83777 -); - - -// ODT_SAT => XYZ => D60_2_D65 => sRGB -const mat3 ACESOutputMat = mat3 -( -    1.60475, -0.10208, -0.00327, -    -0.53108,  1.10813, -0.07276, -    -0.07367, -0.00605,  1.07602 -); - -// ACES tone map (faster approximation) -// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ -vec3 toneMapACES_Narkowicz(vec3 color) -{ -    const float A = 2.51; -    const float B = 0.03; -    const float C = 2.43; -    const float D = 0.59; -    const float E = 0.14; -    return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0); -} - - -// ACES filmic tone map approximation -// see https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl -vec3 RRTAndODTFit(vec3 color) -{ -    vec3 a = color * (color + 0.0245786) - 0.000090537; -    vec3 b = color * (0.983729 * color + 0.4329510) + 0.238081; -    return a / b; -} - - -// tone mapping -vec3 toneMapACES_Hill(vec3 color) -{ -    color = ACESInputMat * color; - -    // Apply RRT and ODT -    color = RRTAndODTFit(color); - -    color = ACESOutputMat * color; - -    // Clamp to [0, 1] -    color = clamp(color, 0.0, 1.0); - -    return color; -} - -// Khronos Neutral tonemapping -// https://github.com/KhronosGroup/ToneMapping/tree/main -// Input color is non-negative and resides in the Linear Rec. 709 color space. -// Output color is also Linear Rec. 709, but in the [0, 1] range. -vec3 PBRNeutralToneMapping( vec3 color ) -{ -  const float startCompression = 0.8 - 0.04; -  const float desaturation = 0.15; - -  float x = min(color.r, min(color.g, color.b)); -  float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; -  color -= offset; - -  float peak = max(color.r, max(color.g, color.b)); -  if (peak < startCompression) return color; - -  const float d = 1. - startCompression; -  float newPeak = 1. - d * d / (peak + d - startCompression); -  color *= newPeak / peak; - -  float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.); -  return mix(color, newPeak * vec3(1, 1, 1), g); -} - -uniform float exposure; -uniform float tonemap_mix; -uniform int tonemap_type; - -vec3 toneMap(vec3 color) -{ -#ifndef NO_POST -    float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r; - -    color *= exposure * exp_scale; - -    vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0)); - -    switch(tonemap_type) -    { -    case 0: -        color = PBRNeutralToneMapping(color); -        break; -    case 1: -        color = toneMapACES_Hill(color); -        break; -    } - -    // mix tonemapped and linear here to provide adjustment -    color = mix(clamped_color, color, tonemap_mix); -#endif - -    return color; -} - -//=============================================================== - -void debugExposure(inout vec3 color) -{ -    float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r; -    exp_scale *= 0.5; -    if (abs(vary_fragcoord.y-exp_scale) < 0.01 && vary_fragcoord.x < 0.1) -    { -        color = vec3(1,0,0); -    } -} +vec3 clampHDRRange(vec3 color);  void main()  { @@ -172,6 +47,7 @@ void main()      diff.rgb = clamp(diff.rgb, vec3(0.0), vec3(1.0));  #endif +    diff.rgb = clampHDRRange(diff.rgb);      //debugExposure(diff.rgb);      frag_color = max(diff, vec4(0));  } diff --git a/indra/newview/app_settings/shaders/class1/deferred/tonemapUtilF.glsl b/indra/newview/app_settings/shaders/class1/deferred/tonemapUtilF.glsl new file mode 100644 index 0000000000..a63b8d7c2b --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/tonemapUtilF.glsl @@ -0,0 +1,180 @@ +/** + * @file postDeferredTonemap.glsl + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +/*[EXTRA_CODE_HERE]*/ + +uniform sampler2D exposureMap; +uniform vec2 screen_res; +in vec2 vary_fragcoord; + +//=============================================================== +// tone mapping taken from Khronos sample implementation +//=============================================================== + +// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT +const mat3 ACESInputMat = mat3 +( +    0.59719, 0.07600, 0.02840, +    0.35458, 0.90834, 0.13383, +    0.04823, 0.01566, 0.83777 +); + + +// ODT_SAT => XYZ => D60_2_D65 => sRGB +const mat3 ACESOutputMat = mat3 +( +    1.60475, -0.10208, -0.00327, +    -0.53108,  1.10813, -0.07276, +    -0.07367, -0.00605,  1.07602 +); + +// ACES tone map (faster approximation) +// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +vec3 toneMapACES_Narkowicz(vec3 color) +{ +    const float A = 2.51; +    const float B = 0.03; +    const float C = 2.43; +    const float D = 0.59; +    const float E = 0.14; +    return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0); +} + + +// ACES filmic tone map approximation +// see https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl +vec3 RRTAndODTFit(vec3 color) +{ +    vec3 a = color * (color + 0.0245786) - 0.000090537; +    vec3 b = color * (0.983729 * color + 0.4329510) + 0.238081; +    return a / b; +} + + +// tone mapping +vec3 toneMapACES_Hill(vec3 color) +{ +    color = ACESInputMat * color; + +    // Apply RRT and ODT +    color = RRTAndODTFit(color); + +    color = ACESOutputMat * color; + +    // Clamp to [0, 1] +    color = clamp(color, 0.0, 1.0); + +    return color; +} + +// Khronos Neutral tonemapping +// https://github.com/KhronosGroup/ToneMapping/tree/main +// Input color is non-negative and resides in the Linear Rec. 709 color space. +// Output color is also Linear Rec. 709, but in the [0, 1] range. +vec3 PBRNeutralToneMapping( vec3 color ) +{ +  const float startCompression = 0.8 - 0.04; +  const float desaturation = 0.15; + +  float x = min(color.r, min(color.g, color.b)); +  float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; +  color -= offset; + +  float peak = max(color.r, max(color.g, color.b)); +  if (peak < startCompression) return color; + +  const float d = 1. - startCompression; +  float newPeak = 1. - d * d / (peak + d - startCompression); +  color *= newPeak / peak; + +  float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.); +  return mix(color, newPeak * vec3(1, 1, 1), g); +} + +uniform float exposure; +uniform float tonemap_mix; +uniform int tonemap_type; + +vec3 toneMap(vec3 color) +{ +#ifndef NO_POST +    float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r; + +    color *= exposure * exp_scale; + +    vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0)); + +    switch(tonemap_type) +    { +    case 0: +        color = PBRNeutralToneMapping(color); +        break; +    case 1: +        color = toneMapACES_Hill(color); +        break; +    } + +    // mix tonemapped and linear here to provide adjustment +    color = mix(clamped_color, color, tonemap_mix); +#endif + +    return color; +} + + +vec3 toneMapNoExposure(vec3 color) +{ +#ifndef NO_POST +    vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0)); + +    switch(tonemap_type) +    { +    case 0: +        color = PBRNeutralToneMapping(color); +        break; +    case 1: +        color = toneMapACES_Hill(color); +        break; +    } + +    // mix tonemapped and linear here to provide adjustment +    color = mix(clamped_color, color, tonemap_mix); +#endif + +    return color; +} + + +//=============================================================== + +void debugExposure(inout vec3 color) +{ +    float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r; +    exp_scale *= 0.5; +    if (abs(vary_fragcoord.y-exp_scale) < 0.01 && vary_fragcoord.x < 0.1) +    { +        color = vec3(1,0,0); +    } +} diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index aac75a0739..948387a6ed 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -104,6 +104,7 @@ vec3 pbrBaseLight(vec3 diffuseColor,                    vec3 atten);  GBufferInfo getGBuffer(vec2 screenpos); +vec3 clampHDRRange(vec3 color);  void adjustIrradiance(inout vec3 irradiance, float ambocc)  { @@ -278,6 +279,7 @@ void main()      float final_scale = 1;      if (classic_mode > 0)          final_scale = 1.1; -    frag_color.rgb = max(color.rgb * final_scale, vec3(0)); //output linear since local lights will be added to this shader's results + +    frag_color.rgb = clampHDRRange(color.rgb * final_scale); //output linear since local lights will be added to this shader's results      frag_color.a = 0.0;  } diff --git a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl index 7027e3796e..1b7b0c1937 100644 --- a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl +++ b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl @@ -90,21 +90,15 @@ uniform sampler2D depthMap;  uniform sampler2D exclusionTex; -uniform float sunAngle; -uniform float sunAngle2; +uniform int classic_mode;  uniform vec3 lightDir;  uniform vec3 specular; -uniform float lightExp; +uniform float blurMultiplier;  uniform float refScale;  uniform float kd; -uniform vec2 screenRes;  uniform vec3 normScale;  uniform float fresnelScale;  uniform float fresnelOffset; -uniform float blurMultiplier; -uniform vec4 waterFogColor; -uniform vec3 waterFogColorLinear; -  //bigWave is (refCoord.w, view.w);  in vec4 refCoord; @@ -126,6 +120,7 @@ vec3 linear_to_srgb(vec3 col);  vec3 atmosLighting(vec3 light);  vec3 scaleSoftClip(vec3 light); +vec3 toneMapNoExposure(vec3 color);  vec3 vN, vT, vB; @@ -171,18 +166,18 @@ void calculateFresnelFactors(out vec3 df3, out vec2 df2, vec3 viewVec, vec3 wave      // We calculate the fresnel here.      // We do this by getting the dot product for each sets of waves, and applying scale and offset. -    df3 = vec3( +    df3 = max(vec3(0), vec3(          dot(viewVec, wave1),          dot(viewVec, (wave2 + wave3) * 0.5),          dot(viewVec, wave3) -    ) * fresnelScale + fresnelOffset; +    ) * fresnelScale + fresnelOffset);      df3 *= df3; -    df2 = vec2( +    df2 = max(vec2(0), vec2(          df3.x + df3.y + df3.z,          dot(viewVec, wavef) * fresnelScale + fresnelOffset -    ); +    ));  }  void main() @@ -194,6 +189,7 @@ void main()      vB = cross(vN, vT);      vec3 pos = vary_position.xyz; +    float linear_depth = 1 / -pos.z;      float dist = length(pos.xyz); @@ -216,6 +212,12 @@ void main()      vec3 df3 = vec3(0);      vec2 df2 = vec2(0); +    vec3 sunlit; +    vec3 amblit; +    vec3 additive; +    vec3 atten; +    calcAtmosphericVarsLinear(pos.xyz, wavef, vary_light_dir, sunlit, amblit, additive, atten); +      calculateFresnelFactors(df3, df2, normalize(view.xyz), wave1, wave2, wave3, wavef);      vec3 waver = wavef*3; @@ -230,7 +232,7 @@ void main()      vec3 norm = transform_normal(normalize(wavef));      vdu = clamp(vdu, 0, 1); -    wavef.z *= max(vdu*vdu*vdu, 0.1); +    //wavef.z *= max(vdu*vdu*vdu, 0.1);      wavef = normalize(wavef); @@ -245,11 +247,6 @@ void main()      distort2 = clamp(distort2, vec2(0), vec2(0.999)); -    vec3 sunlit; -    vec3 amblit; -    vec3 additive; -    vec3 atten; -      float shadow = 1.0f;      float water_mask = texture(exclusionTex, distort).r; @@ -258,17 +255,20 @@ void main()      shadow = sampleDirectionalShadow(pos.xyz, norm.xyz, distort);  #endif -    calcAtmosphericVarsLinear(pos.xyz, wavef, vary_light_dir, sunlit, amblit, additive, atten); - -    vec3 sunlit_linear = srgb_to_linear(sunlit); -    float fade = 0; +    vec3 sunlit_linear = sunlit; +    float fade = 1;  #ifdef TRANSPARENT_WATER      float depth = texture(depthMap, distort).r;      vec3 refPos = getPositionWithNDC(vec3(distort*2.0-vec2(1.0), depth*2.0-1.0));      // Calculate some distance fade in the water to better assist with refraction blending and reducing the refraction texture's "disconnect". -    fade = max(0,min(1, (pos.z - refPos.z) / 10)) * water_mask; +#ifdef SHORELINE_FADE +    fade = max(0,min(1, (pos.z - refPos.z) / 10)) +#else +    fade = 1 * water_mask; +#endif +      distort2 = mix(distort, distort2, min(1, fade * 10));      depth = texture(depthMap, distort2).r; @@ -289,8 +289,8 @@ void main()  #endif      float metallic = 1.0; -    float perceptualRoughness = 0.1; -    float gloss      = 0.95; +    float perceptualRoughness = blurMultiplier; +    float gloss      = 1 - perceptualRoughness;      vec3  irradiance = vec3(0);      vec3  radiance  = vec3(0); @@ -300,7 +300,7 @@ void main()  #ifdef WATER_MINIMAL      sampleReflectionProbesWater(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss, amblit);  #elif WATER_MINIMAL_PLUS -    sampleReflectionProbes(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, 1, false, amblit); +    sampleReflectionProbes(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss, false, amblit);  #endif      vec3 diffuseColor = vec3(0); @@ -322,21 +322,27 @@ void main()      pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, normalize(wavef+up*max(dist, 32.0)/32.0*(1.0-vdu)), v, normalize(light_dir), nl, diffPunc, specPunc); -    vec3 punctual = clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)) * sunlit_linear * shadow; - +    vec3 punctual = clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)) * sunlit_linear * shadow * atten; +    radiance *= df2.y; +    //radiance = toneMapNoExposure(radiance);      vec3 color = vec3(0); -    color = mix(fb.rgb, radiance * df2.y, df2.x * 0.99999) + punctual.rgb; +    color = mix(fb.rgb, radiance, min(1, df2.x)) + punctual.rgb; + +    float water_haze_scale = 4; + +    if (classic_mode > 0) +        water_haze_scale = 1;      // This looks super janky, but we do this to restore water haze in the distance.      // These values were finagled in to try and bring back some of the distant brightening on legacy water.  Also works reasonably well on PBR skies such as PBR midday. -    color += color * min(vec3(4),pow(1 - atten, vec3(1.35)) * 16 * fade); +    // color = mix(color, additive * water_haze_scale, (1 - atten));      // We shorten the fade here at the shoreline so it doesn't appear too soft from a distance.      fade *= 60;      fade = min(1, fade);      color = mix(fb.rgb, color, fade); -    float spec = min(max(max(punctual.r, punctual.g), punctual.b), 0.05); +    float spec = min(max(max(punctual.r, punctual.g), punctual.b), 0);      frag_color = min(vec4(1),max(vec4(color.rgb, spec * water_mask), vec4(0)));  } diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index cb79410d72..883963d558 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -86,6 +86,7 @@ RenderTonemapType			1   1  RenderTonemapMix			1   1  RenderDisableVintageMode           1   1  RenderMaxTextureResolution         1   2048 +RenderReflectionProbeCount  1   256  //  // Low Graphics Settings @@ -128,6 +129,7 @@ RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderDisableVintageMode           1   0  RenderMaxTextureResolution         1   512 +RenderReflectionProbeCount  1   1  //  // Medium Low Graphics Settings @@ -170,6 +172,7 @@ RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderDisableVintageMode           1   0  RenderMaxTextureResolution         1   1024 +RenderReflectionProbeCount  1   16  //  // Medium Graphics Settings (standard) @@ -211,6 +214,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   32  //  // Medium High Graphics Settings @@ -252,6 +256,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   64  //  // High Graphics Settings (SSAO + sun shadows) @@ -293,6 +298,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   128  //  // High Ultra Graphics Settings (deferred + SSAO + all shadows) @@ -334,6 +340,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   256  //  // Ultra graphics (REALLY PURTY!) @@ -375,6 +382,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   256  //  // Class Unknown Hardware (unknown) @@ -408,6 +416,7 @@ RenderReflectionProbeDetail	0	-1  RenderMirrors				0	0  RenderDisableVintageMode           1   0  RenderMaxTextureResolution         1   2048 +RenderReflectionProbeCount  0   0  list Intel  RenderAnisotropic			1	0 @@ -429,6 +438,7 @@ RenderMirrors				0	0  RenderGLMultiThreadedTextures 0 0  RenderGLMultiThreadedMedia 0 0  RenderDisableVintageMode           1   0 +RenderReflectionProbeCount  0   0  list TexUnit16orLess  RenderTerrainPBRDetail      1   -1 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index dc9473b042..103d24a26d 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -86,6 +86,7 @@ RenderTonemapMix			1   1  RenderDisableVintageMode           1   1  RenderDownScaleMethod       1   0  RenderMaxTextureResolution         1   2048 +RenderReflectionProbeCount  1   256  //  // Low Graphics Settings @@ -128,6 +129,7 @@ RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderDisableVintageMode           1   0  RenderMaxTextureResolution         1   512 +RenderReflectionProbeCount  1   1  //  // Medium Low Graphics Settings @@ -170,6 +172,7 @@ RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderDisableVintageMode           1   0  RenderMaxTextureResolution         1   1024 +RenderReflectionProbeCount  1   16  //  // Medium Graphics Settings (standard) @@ -211,6 +214,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   32  //  // Medium High Graphics Settings @@ -252,6 +256,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   64  //  // High Graphics Settings (SSAO + sun shadows) @@ -293,6 +298,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   128  //  // High Ultra Graphics Settings (SSAO + all shadows) @@ -334,6 +340,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   256  //  // Ultra graphics (REALLY PURTY!) @@ -375,6 +382,7 @@ RenderExposure				1   1  RenderTonemapType			1   1  RenderTonemapMix			1   0.7  RenderMaxTextureResolution  1   2048 +RenderReflectionProbeCount  1   256  //  // Class Unknown Hardware (unknown) @@ -407,6 +415,7 @@ RenderShadowDetail			0	0  RenderMirrors				0	0  RenderDisableVintageMode           1   0  RenderMaxTextureResolution         1   2048 +RenderReflectionProbeCount  0   0  list TexUnit8orLess  RenderDeferredSSAO			0	0 @@ -447,6 +456,7 @@ RenderReflectionProbeDetail	0	0  RenderReflectionsEnabled    0   0  RenderMirrors				0	0  RenderDisableVintageMode           1   0 +RenderReflectionProbeCount  0   0  list VaryingVectors16orLess  RenderTerrainPBRPlanarSampleCount 1   1 diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 7863d25696..c33d15228c 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -356,8 +356,9 @@ void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id)      llassert(obj->getVolume()->getParams().getSculptID() == gltf_id);      llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF); -    if (obj->mGLTFAsset) -    { // object already has a GLTF asset, don't reload it +    if (obj->mGLTFAsset || obj->mIsGLTFAssetMissing ) +    { +        // object already has a GLTF asset or load failed, don't reload it          // TODO: below assertion fails on dupliate requests for assets -- possibly need to touch up asset loading state machine          // llassert(std::find(mObjects.begin(), mObjects.end(), obj) != mObjects.end()); @@ -398,16 +399,19 @@ void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::ETyp                              }                              else                              { -                                LL_WARNS("GLTF") << "Failed to prepare GLTF asset: " << id << LL_ENDL; +                                LL_WARNS("GLTF") << "Failed to prepare GLTF asset: " << id << ". Marking as missing." << LL_ENDL; +                                obj->mIsGLTFAssetMissing = true;                                  obj->mGLTFAsset = nullptr;                              }                          }                      } +                    obj->unref(); // todo: use LLPointer                  }              }              else              { -                LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL; +                LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << ". Marking as missing." << LL_ENDL; +                obj->mIsGLTFAssetMissing = true;                  obj->unref();              }          }); @@ -446,7 +450,8 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a                  }                  else                  { -                    LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << LL_ENDL; +                    LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << " for asset id: " << id << ". Marking as missing." << LL_ENDL; +                    obj->mIsGLTFAssetMissing = true;                      obj->unref();                      return;                  } @@ -455,7 +460,8 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a      }      else      { -        LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL; +        LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << ". Marking as missing." << LL_ENDL; +        obj->mIsGLTFAssetMissing = true;          obj->unref();      }  } @@ -517,6 +523,7 @@ void GLTFSceneManager::update()                          if (mUploadingObject)                          {                              mUploadingObject->mGLTFAsset = nullptr; +                            mUploadingObject->mIsGLTFAssetMissing = false;                              mUploadingObject->setGLTFAsset(assetId);                              mUploadingObject->markForUpdate();                              mUploadingObject = nullptr; diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 8756baa04a..2161dbe19e 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -56,6 +56,7 @@  #include "llgroupmgr.h"  #include "llhudmanager.h"  #include "lljoystickbutton.h" +#include "lllandmarkactions.h"  #include "llmorphview.h"  #include "llmoveview.h"  #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state @@ -2501,7 +2502,10 @@ void LLAgent::endAnimationUpdateUI()          gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode());      } -    gFloaterTools->dirty(); +    if (gFloaterTools) +    { +        gFloaterTools->dirty(); +    }      // Don't let this be called more than once if the camera      // mode hasn't changed.  --JC @@ -4317,8 +4321,22 @@ void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)  void LLAgent::doTeleportViaLandmark(const LLUUID& landmark_asset_id)  { -    LLViewerRegion *regionp = getRegion(); -    if(regionp && teleportCore()) +    LLViewerRegion* regionp = getRegion(); +    if (!regionp) +    { +        LL_WARNS("Teleport") << "called when agent region is null" << LL_ENDL; +        return; +    } + +    bool is_local(false); +    if (LLLandmark* landmark = gLandmarkList.getAsset(landmark_asset_id, NULL)) +    { +        LLVector3d pos_global; +        landmark->getGlobalPos(pos_global); +        is_local = (regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY])); +    } + +    if(regionp && teleportCore(is_local))      {          LL_INFOS("Teleport") << "Sending TeleportLandmarkRequest. Current region handle " << regionp->getHandle()                               << " region id " << regionp->getRegionID() diff --git a/indra/newview/llagentpicksinfo.cpp b/indra/newview/llagentpicksinfo.cpp index e2a2d2d8a9..265e4060ff 100644 --- a/indra/newview/llagentpicksinfo.cpp +++ b/indra/newview/llagentpicksinfo.cpp @@ -28,6 +28,7 @@  #include "llagentpicksinfo.h"  #include "llagent.h" +#include "llagentbenefits.h"  #include "llavatarpropertiesprocessor.h"  const S32 MAX_AVATAR_PICKS = 10; @@ -85,10 +86,9 @@ private:  LLAgentPicksInfo::LLAgentPicksInfo()   : mAgentPicksObserver(NULL) - , mMaxNumberOfPicks(MAX_AVATAR_PICKS)   // Disable Pick creation until we get number of Picks from server - in case   // avatar has maximum number of Picks. - , mNumberOfPicks(mMaxNumberOfPicks) + , mNumberOfPicks(S32_MAX)  {  } @@ -110,7 +110,13 @@ void LLAgentPicksInfo::requestNumberOfPicks()      mAgentPicksObserver->sendAgentPicksRequest();  } -bool LLAgentPicksInfo::isPickLimitReached() +// static +S32 LLAgentPicksInfo::getMaxNumberOfPicks() +{ +    return LLAgentBenefitsMgr::current().getPicksLimit(); +} + +bool LLAgentPicksInfo::isPickLimitReached() const  {      return getNumberOfPicks() >= getMaxNumberOfPicks();  } diff --git a/indra/newview/llagentpicksinfo.h b/indra/newview/llagentpicksinfo.h index 9bc105a655..3514ade65d 100644 --- a/indra/newview/llagentpicksinfo.h +++ b/indra/newview/llagentpicksinfo.h @@ -52,17 +52,17 @@ public:      /**       * Returns number of Picks.       */ -    S32 getNumberOfPicks() { return mNumberOfPicks; } +    S32 getNumberOfPicks() const { return mNumberOfPicks; }      /**       * Returns maximum number of Picks.       */ -    S32 getMaxNumberOfPicks() { return mMaxNumberOfPicks; } +    static S32 getMaxNumberOfPicks();      /**       * Returns true if Agent has maximum allowed number of Picks.       */ -    bool isPickLimitReached(); +    bool isPickLimitReached() const;      /**       * After creating or deleting a Pick we can assume operation on server will be @@ -83,15 +83,9 @@ private:      */      void setNumberOfPicks(S32 number) { mNumberOfPicks = number; } -    /** -    * Sets maximum number of Picks. -    */ -    void setMaxNumberOfPicks(S32 max_picks) { mMaxNumberOfPicks = max_picks; } -  private:      LLAgentPicksObserver* mAgentPicksObserver; -    S32 mMaxNumberOfPicks;      S32 mNumberOfPicks;  }; diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index ce4e8e9392..11c5ffecb6 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -1385,8 +1385,6 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)                       && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN                       && version > curr_cat->getVersion())              { -                // Potentially should new_cat->setVersion(unknown) here, -                // but might be waiting for a callback that would increment                  LL_DEBUGS("Inventory") << "Category " << category_id                      << " is stale. Known version: " << curr_cat->getVersion()                      << " server version: " << version << LL_ENDL; diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index 1090888c1c..d4b05dde72 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -301,7 +301,8 @@ struct AttachmentInfo          AttachmentInfo(metadata.logFilePathname,      "text/plain"),          AttachmentInfo(metadata.userSettingsPathname, "text/xml"),          AttachmentInfo(metadata.accountSettingsPathname, "text/xml"), -        AttachmentInfo(metadata.staticDebugPathname,  "text/xml") +        AttachmentInfo(metadata.staticDebugPathname,  "text/xml"), +        AttachmentInfo(metadata.attributesPathname,  "text/xml")      };      secondLogPath = metadata.secondLogFilePathname; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9392d111f2..1251a900b0 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -349,10 +349,6 @@ std::string gLastVersionChannel;  LLVector3 gWindVec(3.0, 3.0, 0.0);  LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); -U32 gPacketsIn = 0; - -bool gPrintMessagesThisFrame = false; -  bool gRandomizeFramerate = false;  bool gPeriodicSlowFrame = false; @@ -361,6 +357,7 @@ bool gLLErrorActivated = false;  bool gLogoutInProgress = false;  bool gSimulateMemLeak = false; +bool gDoDisconnect = false;  // We don't want anyone, especially threads working on the graphics pipeline,  // to have to block due to this WorkQueue being full. @@ -374,7 +371,6 @@ const std::string MARKER_FILE_NAME("SecondLife.exec_marker");  const std::string START_MARKER_FILE_NAME("SecondLife.start_marker");  const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");  const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker"); -static bool gDoDisconnect = false;  static std::string gLaunchFileOnQuit;  // Used on Win32 for other apps to identify our window (eg, win_setup) @@ -1500,9 +1496,9 @@ bool LLAppViewer::doFrame()          {              LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout"); -        pingMainloopTimeout("Main:Sleep"); +            pingMainloopTimeout("Main:Sleep"); -        pauseMainloopTimeout(); +            pauseMainloopTimeout();          }          // Sleep and run background threads @@ -2359,6 +2355,14 @@ void LLAppViewer::initLoggingAndGetLastDuration()          {              LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL;          } + +        std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.log"); +        if (gDirUtilp->fileExists(user_data_path_cef_log)) +        { +            std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old"); +            LLFile::remove(user_data_path_cef_old, ENOENT); +            LLFile::rename(user_data_path_cef_log, user_data_path_cef_old); +        }      }  } @@ -2986,9 +2990,10 @@ void LLAppViewer::initStrings()      std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file);      if (strings_path_full.empty() || !LLFile::isfile(strings_path_full))      { +        std::string crash_reason;          if (strings_path_full.empty())          { -            LL_WARNS() << "The file '" << strings_file << "' is not found" << LL_ENDL; +            crash_reason = "The file '" + strings_file + "' is not found";          }          else          { @@ -2996,24 +3001,23 @@ void LLAppViewer::initStrings()              int rc = LLFile::stat(strings_path_full, &st);              if (rc != 0)              { -                LL_WARNS() << "The file '" << strings_path_full << "' failed to get status. Error code: " << rc << LL_ENDL; +                crash_reason = "The file '" + strings_path_full + "' failed to get status. Error code: " + std::to_string(rc);              }              else if (S_ISDIR(st.st_mode))              { -                LL_WARNS() << "The filename '" << strings_path_full << "' is a directory name" << LL_ENDL; +                crash_reason = "The filename '" + strings_path_full + "' is a directory name";              }              else              { -                LL_WARNS() << "The filename '" << strings_path_full << "' doesn't seem to be a regular file name" << LL_ENDL; +                crash_reason = "The filename '" + strings_path_full + "' doesn't seem to be a regular file name";              }          }          // initial check to make sure files are there failed          gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);          LLError::LLUserWarningMsg::showMissingFiles(); -        LL_ERRS() << "Viewer failed to find localization and UI files." -            << " Please reinstall viewer from https://secondlife.com/support/downloads" -            << " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL; +        LL_ERRS() << "Viewer failed to open some of localization and UI files." +            << " " << crash_reason << "." << LL_ENDL;      }      LLTransUtil::parseStrings(strings_file, default_trans_args);      LLTransUtil::parseLanguageStrings("language_settings.xml"); @@ -4254,7 +4258,7 @@ U32 LLAppViewer::getTextureCacheVersion()  U32 LLAppViewer::getDiskCacheVersion()  {      // Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes. -    const U32 DISK_CACHE_VERSION = 1; +    const U32 DISK_CACHE_VERSION = 2;      return DISK_CACHE_VERSION ;  } @@ -4573,11 +4577,32 @@ void LLAppViewer::saveFinalSnapshot()      }  } +static const char PRODUCTION_CACHE_FORMAT_STRING[] = "%s.%s"; +static const char GRID_CACHE_FORMAT_STRING[] = "%s.%s.%s"; +std::string get_name_cache_filename(const std::string &base_file, const std::string& extention) +{ +    std::string filename; +    std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, base_file)); +    if (LLGridManager::getInstance()->isInProductionGrid()) +    { +        filename = llformat(PRODUCTION_CACHE_FORMAT_STRING, path.c_str(), extention.c_str()); +    } +    else +    { +        // NOTE: The inventory cache filenames now include the grid name. +        // Add controls against directory traversal or problematic pathname lengths +        // if your viewer uses grid names from an untrusted source. +        const std::string& grid_id_str   = LLGridManager::getInstance()->getGridId(); +        const std::string& grid_id_lower = utf8str_tolower(grid_id_str); +        filename                         = llformat(GRID_CACHE_FORMAT_STRING, path.c_str(), grid_id_lower.c_str(), extention.c_str()); +    } +    return filename; +} +  void LLAppViewer::loadNameCache()  {      // display names cache -    std::string filename = -        gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); +    std::string filename = get_name_cache_filename("avatar_name_cache", "xml");      LL_INFOS("AvNameCache") << filename << LL_ENDL;      llifstream name_cache_stream(filename.c_str());      if(name_cache_stream.is_open()) @@ -4592,8 +4617,8 @@ void LLAppViewer::loadNameCache()      if (!gCacheName) return; -    std::string name_cache; -    name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); +    // is there a reason for the "cache" extention? +    std::string name_cache = get_name_cache_filename("name", "cache");      llifstream cache_file(name_cache.c_str());      if(cache_file.is_open())      { @@ -4604,8 +4629,7 @@ void LLAppViewer::loadNameCache()  void LLAppViewer::saveNameCache()  {      // display names cache -    std::string filename = -        gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); +    std::string filename = get_name_cache_filename("avatar_name_cache", "xml");      llofstream name_cache_stream(filename.c_str());      if(name_cache_stream.is_open())      { @@ -4615,8 +4639,7 @@ void LLAppViewer::saveNameCache()      // real names cache      if (gCacheName)      { -        std::string name_cache; -        name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); +        std::string name_cache = get_name_cache_filename("name", "cache");          llofstream cache_file(name_cache.c_str());          if(cache_file.is_open())          { @@ -4894,6 +4917,20 @@ void LLAppViewer::idle()      if (gTeleportDisplay)      { +        if (gAgent.getTeleportState() == LLAgent::TELEPORT_ARRIVING) +        { +            // Teleported, but waiting for things to load, start processing surface data +            { +                LL_RECORD_BLOCK_TIME(FTM_NETWORK); +                gVLManager.unpackData(); +            } +            { +                LL_RECORD_BLOCK_TIME(FTM_REGION_UPDATE); +                const F32 max_region_update_time = .001f; // 1ms +                LLWorld::getInstance()->updateRegions(max_region_update_time); +            } +        } +          return;      } @@ -5330,12 +5367,9 @@ void LLAppViewer::idleNameCache()  // Handle messages, and all message related stuff  // -#define TIME_THROTTLE_MESSAGES -#ifdef TIME_THROTTLE_MESSAGES -#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) +constexpr F32 CHECK_MESSAGES_DEFAULT_MAX_TIME = 0.020f; // 50 ms = 50 fps (just for messages!)  static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -#endif  static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network");  static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks"); @@ -5362,6 +5396,7 @@ void LLAppViewer::idleNetwork()          F32 total_time = 0.0f;          { +            bool needs_drain = false;              LockMessageChecker lmc(gMessageSystem);              while (lmc.checkAllMessages(frame_count, gServicePump))              { @@ -5374,54 +5409,44 @@ void LLAppViewer::idleNetwork()                  }                  total_decoded++; -                gPacketsIn++;                  if (total_decoded > MESSAGE_MAX_PER_FRAME)                  { +                    needs_drain = true;                      break;                  } -#ifdef TIME_THROTTLE_MESSAGES                  // Prevent slow packets from completely destroying the frame rate.                  // This usually happens due to clumps of avatars taking huge amount                  // of network processing time (which needs to be fixed, but this is                  // a good limit anyway).                  total_time = check_message_timer.getElapsedTimeF32();                  if (total_time >= CheckMessagesMaxTime) +                { +                    needs_drain = true;                      break; -#endif +                } +            } +            if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0) +            { +                // Rather than allow packets to silently backup on the socket +                // we drain them into our own buffer so we know how many exist. +                S32 num_buffered_packets = gMessageSystem->drainUdpSocket(); +                if (num_buffered_packets > 0) +                { +                    // Increase CheckMessagesMaxTime so that we will eventually catch up +                    CheckMessagesMaxTime *= 1.035f; // 3.5% ~= 2x in 20 frames, ~8x in 60 frames +                } +            } +            else +            { +                // Reset CheckMessagesMaxTime to default value +                CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;              }              // Handle per-frame message system processing.              lmc.processAcks(gSavedSettings.getF32("AckCollectTime"));          } - -#ifdef TIME_THROTTLE_MESSAGES -        if (total_time >= CheckMessagesMaxTime) -        { -            // Increase CheckMessagesMaxTime so that we will eventually catch up -            CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames -        } -        else -        { -            // Reset CheckMessagesMaxTime to default value -            CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -        } -#endif - -        // Decode enqueued messages... -        S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; - -        if( remaining_possible_decodes <= 0 ) -        { -            LL_INFOS() << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << LL_ENDL; -        } - -        if (gPrintMessagesThisFrame) -        { -            LL_INFOS() << "Decoded " << total_decoded << " msgs this frame!" << LL_ENDL; -            gPrintMessagesThisFrame = false; -        }      }      add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects); @@ -5604,6 +5629,27 @@ void LLAppViewer::forceErrorCoroutineCrash()      LLCoros::instance().launch("LLAppViewer::crashyCoro", [] {throw LLException("A deliberate crash from LLCoros"); });  } +void LLAppViewer::forceErrorCoroprocedureCrash() +{ +    LL_WARNS() << "Forcing a crash in LLCoprocedureManager" << LL_ENDL; +    LLCoprocedureManager::instance().enqueueCoprocedure("Upload", "DeliberateCrash", +        [](LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t&, const LLUUID&) +    { +        LL_WARNS() << "Forcing a deliberate bad memory access from LLCoprocedureManager" << LL_ENDL; +        S32* crash = NULL; +        *crash = 0xDEADBEEF; +    }); +} + +void LLAppViewer::forceErrorWorkQueueCrash() +{ +    LL::WorkQueue::ptr_t workqueue = LL::WorkQueue::getInstance("General"); +    if (workqueue) +    { +        workqueue->post([]() { throw LLException("This is a deliberate crash from General Queue"); }); +    } +} +  void LLAppViewer::forceErrorThreadCrash()  {      class LLCrashTestThread : public LLThread diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index b4756eecd6..3da0246ccf 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -157,9 +157,6 @@ public:      void loadNameCache();      void saveNameCache(); -    void loadExperienceCache(); -    void saveExperienceCache(); -      void removeMarkerFiles();      void recordSessionToMarker(); @@ -175,6 +172,8 @@ public:      virtual void forceErrorOSSpecificException();      virtual void forceErrorDriverCrash();      virtual void forceErrorCoroutineCrash(); +    virtual void forceErrorCoroprocedureCrash(); +    virtual void forceErrorWorkQueueCrash();      virtual void forceErrorThreadCrash();      // The list is found in app_settings/settings_files.xml @@ -411,11 +410,10 @@ extern std::string gLastVersionChannel;  extern LLVector3 gWindVec;  extern LLVector3 gRelativeWindVec; -extern U32  gPacketsIn; -extern bool gPrintMessagesThisFrame;  extern bool gRandomizeFramerate;  extern bool gPeriodicSlowFrame; +extern bool gDoDisconnect;  extern bool gSimulateMemLeak; diff --git a/indra/newview/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h index 5a69cd93fc..c3f1e35872 100644 --- a/indra/newview/llappviewermacosx-for-objc.h +++ b/indra/newview/llappviewermacosx-for-objc.h @@ -41,6 +41,7 @@ void clearDumpLogsDir();  struct CrashMetadata  {      std::string logFilePathname; +    std::string attributesPathname;      std::string userSettingsPathname;      std::string accountSettingsPathname;      std::string staticDebugPathname; diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index f497a3cdf3..b074c40c17 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -172,6 +172,8 @@ CrashMetadataSingleton::CrashMetadataSingleton()      // Note: we depend on being able to read the static_debug_info.log file      // from the *previous* run before we overwrite it with the new one for      // *this* run. LLAppViewer initialization must happen in the Right Order. + +    // Todo: consider converting static file into bugspalt attributes file      staticDebugPathname = *gViewerAppPtr->getStaticDebugFile();      std::ifstream static_file(staticDebugPathname);      LLSD info; @@ -215,7 +217,32 @@ CrashMetadataSingleton::CrashMetadataSingleton()                  }              }          } + +        // Populate bugsplat attributes +        LLXMLNodePtr out_node = new LLXMLNode("XmlCrashContext", false); + +        out_node->createChild("OS", false)->setValue(OSInfo); +        out_node->createChild("AppState", false)->setValue(info["StartupState"].asString()); +        out_node->createChild("GraphicsCard", false)->setValue(info["GraphicsCard"].asString()); +        out_node->createChild("GLVersion", false)->setValue(info["GLInfo"]["GLVersion"].asString()); +        out_node->createChild("GLRenderer", false)->setValue(info["GLInfo"]["GLRenderer"].asString()); +        out_node->createChild("RAM", false)->setValue(info["RAMInfo"]["Physical"].asString()); + +        if (!out_node->isNull()) +        { +            attributesPathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "CrashContext.xml"); +            LLFILE* fp = LLFile::fopen(attributesPathname, "w"); + +            if (fp != NULL) +            { +                LLXMLNode::writeHeaderToFile(fp); +                out_node->writeToFile(fp); + +                fclose(fp); +            } +        }      } +    // else Todo: consider fillig at least some values, like OS  }  // Avoid having to compile all of our LLSingleton machinery in Objective-C++. diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 724182a929..0eb7f7fdb7 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -804,14 +804,16 @@ bool LLAppViewerWin32::cleanup()      return result;  } -void LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo) +bool LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)  {  #if defined(LL_BUGSPLAT)      if (sBugSplatSender)      {          sBugSplatSender->createReport((EXCEPTION_POINTERS*)pExcepInfo); +        return true;      }  #endif // LL_BUGSPLAT +    return false;  }  void LLAppViewerWin32::initLoggingAndGetLastDuration() diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h index 2242c95b06..250e72edf3 100644 --- a/indra/newview/llappviewerwin32.h +++ b/indra/newview/llappviewerwin32.h @@ -43,7 +43,7 @@ public:      bool init() override; // Override to do application initialization      bool cleanup() override; -    void reportCrashToBugsplat(void* pExcepInfo) override; +    bool reportCrashToBugsplat(void* pExcepInfo) override;  protected:      void initLoggingAndGetLastDuration() override; // Override to clean stack_trace info. diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index 6277e65b2d..7dce627044 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -669,6 +669,7 @@ void LLAvatarPropertiesProcessor::sendClassifiedInfoUpdate(const LLAvatarClassif  void LLAvatarPropertiesProcessor::sendPickInfoRequest(const LLUUID& creator_id, const LLUUID& pick_id)  { +    LL_DEBUGS("PickInfo") << " Requiesting pick info for " << pick_id << LL_ENDL;      // Must ask for a pick based on the creator id because      // the pick database is distributed to the inventory cluster. JC      std::vector<std::string> request_params{ creator_id.asString(), pick_id.asString() }; diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index e8a069dfdb..0cad51137c 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -240,10 +240,11 @@ void LLNotificationChiclet::setCounter(S32 counter)  bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification )  {      bool displayNotification; +    LLFloaterNotificationsTabbed* floater = LLFloaterNotificationsTabbed::getInstance();      if (   (notification->getName() == "ScriptDialog") // special case for scripts          // if there is no toast window for the notification, filter it          //|| (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID())) -        || (!LLFloaterNotificationsTabbed::getInstance()->findItemByID(notification->getID(), notification->getName())) +        || (floater && !floater->findItemByID(notification->getID(), notification->getName()))          )      {          displayNotification = false; diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 70ae4ee13f..4921964b35 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -33,6 +33,7 @@  // viewer includes  #include "llagent.h" +#include "llagentcamera.h"  #include "llcriticaldamp.h"  #include "llface.h"  #include "lllightconstants.h" @@ -778,6 +779,14 @@ bool LLDrawable::updateMove()      makeActive(); +    // #3256 force undampened movement for attached objects in mouselook +    // to prevent animation bork for linkset with animated parts +    if (!isRoot() && gAgentCamera.cameraMouselook() && +        !mVObjp->isRiggedMesh() && mVObjp->getAvatar() && mVObjp->getAvatar()->isSelf()) +    { +        return updateMoveUndamped(); +    } +      return isState(MOVE_UNDAMPED) ? updateMoveUndamped() : updateMoveDamped();  } diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp index c27f5ef486..7d58511d41 100644 --- a/indra/newview/lldrawpoolwater.cpp +++ b/indra/newview/lldrawpoolwater.cpp @@ -176,154 +176,133 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass)          light_diffuse *= (1.5f + (6.f * ground_proj_sq));      } -    // set up normal maps filtering -    for (auto norm_map : mWaterNormp) -        { -        if (norm_map) norm_map->setFilteringOption(has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT); -        } +    LLTexUnit::eTextureFilterOptions filter_mode = has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT;      LLColor4      specular(sun_up ? psky->getSunlightColor() : psky->getMoonlightColor());      F32           phase_time = (F32) LLFrameTimer::getElapsedSeconds() * 0.5f;      LLGLSLShader *shader     = nullptr; -    // two passes, first with standard water shader bound, second with edge water shader bound -    for (int edge = 0; edge < 2; edge++) +    // One pass, one of two shaders.  Void water and region water share state. +    // There isn't a good reason anymore to really have void water run in a separate pass. +    // It also just introduced a bunch of weird state consistency stuff that we really don't need. +    // Not to mention, re-binding the the same shader and state for that shader is kind of wasteful. +    // - Geenz 2025-02-11 +    // select shader +    if (underwater)      { -        // select shader -        if (underwater) -        { -            shader = &gUnderWaterProgram; -        } -        else -        { -            if (edge) -            { -                shader = &gWaterEdgeProgram; -            } -            else -            { -                shader = &gWaterProgram; -            } -        } - -        gPipeline.bindDeferredShader(*shader, nullptr, &gPipeline.mWaterDis); - -        //bind normal map -        S32 bumpTex = shader->enableTexture(LLViewerShaderMgr::BUMP_MAP); -        S32 bumpTex2 = shader->enableTexture(LLViewerShaderMgr::BUMP_MAP2); - -        LLViewerTexture* tex_a = mWaterNormp[0]; -        LLViewerTexture* tex_b = mWaterNormp[1]; +        shader = &gUnderWaterProgram; +    } +    else +    { +        shader = &gWaterProgram; +    } -        F32 blend_factor = (F32)pwater->getBlendFactor(); +    gPipeline.bindDeferredShader(*shader, nullptr, &gPipeline.mWaterDis); -        gGL.getTexUnit(bumpTex)->unbind(LLTexUnit::TT_TEXTURE); -        gGL.getTexUnit(bumpTex2)->unbind(LLTexUnit::TT_TEXTURE); +    LLViewerTexture* tex_a = mWaterNormp[0]; +    LLViewerTexture* tex_b = mWaterNormp[1]; -        if (tex_a && (!tex_b || (tex_a == tex_b))) -        { -            gGL.getTexUnit(bumpTex)->bind(tex_a); -            blend_factor = 0; // only one tex provided, no blending -        } -        else if (tex_b && !tex_a) -        { -            gGL.getTexUnit(bumpTex)->bind(tex_b); -            blend_factor = 0; // only one tex provided, no blending -        } -        else if (tex_b != tex_a) -        { -            gGL.getTexUnit(bumpTex)->bind(tex_a); -            gGL.getTexUnit(bumpTex2)->bind(tex_b); -        } +    F32 blend_factor = (F32)pwater->getBlendFactor(); -        shader->bindTexture(LLShaderMgr::WATER_EXCLUSIONTEX, &gPipeline.mWaterExclusionMask); +    if (tex_a && (!tex_b || (tex_a == tex_b))) +    { +        shader->bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_a); +        tex_a->setFilteringOption(filter_mode); +        blend_factor = 0; // only one tex provided, no blending +    } +    else if (tex_b && !tex_a) +    { +        shader->bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_b); +        tex_b->setFilteringOption(filter_mode); +        blend_factor = 0; // only one tex provided, no blending +    } +    else if (tex_b != tex_a) +    { +        shader->bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_a); +        tex_a->setFilteringOption(filter_mode); +        shader->bindTexture(LLViewerShaderMgr::BUMP_MAP2, tex_b); +        tex_b->setFilteringOption(filter_mode); +    } -        // bind reflection texture from RenderTarget -        S32 screentex = shader->enableTexture(LLShaderMgr::WATER_SCREENTEX); +    shader->bindTexture(LLShaderMgr::WATER_EXCLUSIONTEX, &gPipeline.mWaterExclusionMask); -        F32 screenRes[] = { 1.f / gGLViewport[2], 1.f / gGLViewport[3] }; +    shader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor); -        shader->uniform2fv(LLShaderMgr::DEFERRED_SCREEN_RES, 1, screenRes); -        shader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor); +    F32      fog_density = pwater->getModifiedWaterFogDensity(underwater); -        F32      fog_density = pwater->getModifiedWaterFogDensity(underwater); +    shader->bindTexture(LLShaderMgr::WATER_SCREENTEX, &gPipeline.mWaterDis); -        if (screentex > -1) -        { -            shader->uniform1f(LLShaderMgr::WATER_FOGDENSITY, fog_density); -            gGL.getTexUnit(screentex)->bind(&gPipeline.mWaterDis); -        } +    if (mShaderLevel == 1) +    { +        fog_color.mV[VALPHA] = (F32)(log(fog_density) / log(2)); +    } -        if (mShaderLevel == 1) -        { -            fog_color.mV[VALPHA] = (F32)(log(fog_density) / log(2)); -        } +    F32 water_height = environment.getWaterHeight(); +    F32 camera_height = LLViewerCamera::getInstance()->getOrigin().mV[2]; +    shader->uniform1f(LLShaderMgr::WATER_WATERHEIGHT, camera_height - water_height); +    shader->uniform1f(LLShaderMgr::WATER_TIME, phase_time); +    shader->uniform3fv(LLShaderMgr::WATER_EYEVEC, 1, LLViewerCamera::getInstance()->getOrigin().mV); -        F32 water_height = environment.getWaterHeight(); -        F32 camera_height = LLViewerCamera::getInstance()->getOrigin().mV[2]; -        shader->uniform1f(LLShaderMgr::WATER_WATERHEIGHT, camera_height - water_height); -        shader->uniform1f(LLShaderMgr::WATER_TIME, phase_time); -        shader->uniform3fv(LLShaderMgr::WATER_EYEVEC, 1, LLViewerCamera::getInstance()->getOrigin().mV); +    shader->uniform3fv(LLShaderMgr::WATER_SPECULAR, 1, light_diffuse.mV); -        shader->uniform4fv(LLShaderMgr::SPECULAR_COLOR, 1, specular.mV); -        shader->uniform4fv(LLShaderMgr::WATER_FOGCOLOR, 1, fog_color.mV); -        shader->uniform3fv(LLShaderMgr::WATER_FOGCOLOR_LINEAR, 1, fog_color_linear.mV); +    shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR1, 1, pwater->getWave1Dir().mV); +    shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR2, 1, pwater->getWave2Dir().mV); -        shader->uniform3fv(LLShaderMgr::WATER_SPECULAR, 1, light_diffuse.mV); -        shader->uniform1f(LLShaderMgr::WATER_SPECULAR_EXP, light_exp); +    shader->uniform3fv(LLShaderMgr::WATER_LIGHT_DIR, 1, light_dir.mV); -        shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR1, 1, pwater->getWave1Dir().mV); -        shader->uniform2fv(LLShaderMgr::WATER_WAVE_DIR2, 1, pwater->getWave2Dir().mV); +    shader->uniform3fv(LLShaderMgr::WATER_NORM_SCALE, 1, pwater->getNormalScale().mV); +    shader->uniform1f(LLShaderMgr::WATER_FRESNEL_SCALE, pwater->getFresnelScale()); +    shader->uniform1f(LLShaderMgr::WATER_FRESNEL_OFFSET, pwater->getFresnelOffset()); +    shader->uniform1f(LLShaderMgr::WATER_BLUR_MULTIPLIER, fmaxf(0, pwater->getBlurMultiplier()) * 2); -        shader->uniform3fv(LLShaderMgr::WATER_LIGHT_DIR, 1, light_dir.mV); +    static LLStaticHashedString s_exposure("exposure"); +    static LLStaticHashedString tonemap_mix("tonemap_mix"); +    static LLStaticHashedString tonemap_type("tonemap_type"); -        shader->uniform3fv(LLShaderMgr::WATER_NORM_SCALE, 1, pwater->getNormalScale().mV); -        shader->uniform1f(LLShaderMgr::WATER_FRESNEL_SCALE, pwater->getFresnelScale()); -        shader->uniform1f(LLShaderMgr::WATER_FRESNEL_OFFSET, pwater->getFresnelOffset()); -        shader->uniform1f(LLShaderMgr::WATER_BLUR_MULTIPLIER, pwater->getBlurMultiplier()); +    static LLCachedControl<F32> exposure(gSavedSettings, "RenderExposure", 1.f); -        F32 sunAngle = llmax(0.f, light_dir.mV[1]); -        F32 scaledAngle = 1.f - sunAngle; +    F32 e = llclamp(exposure(), 0.5f, 4.f); -        shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up ? 1 : 0); -        shader->uniform1f(LLShaderMgr::WATER_SUN_ANGLE, sunAngle); -        shader->uniform1f(LLShaderMgr::WATER_SCALED_ANGLE, scaledAngle); -        shader->uniform1f(LLShaderMgr::WATER_SUN_ANGLE2, 0.1f + 0.2f * sunAngle); -        shader->uniform1i(LLShaderMgr::WATER_EDGE_FACTOR, edge ? 1 : 0); +    static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", false); -        // SL-15861 This was changed from getRotatedLightNorm() as it was causing -        // lightnorm in shaders\class1\windlight\atmosphericsFuncs.glsl in have inconsistent additive lighting for 180 degrees of the FOV. -        LLVector4 rotated_light_direction = LLEnvironment::instance().getClampedLightNorm(); -        shader->uniform3fv(LLViewerShaderMgr::LIGHTNORM, 1, rotated_light_direction.mV); +    shader->uniform1f(s_exposure, e); +    static LLCachedControl<U32> tonemap_type_setting(gSavedSettings, "RenderTonemapType", 0U); +    shader->uniform1i(tonemap_type, tonemap_type_setting); +    shader->uniform1f(tonemap_mix, psky->getTonemapMix(should_auto_adjust())); -        shader->uniform3fv(LLShaderMgr::WL_CAMPOSLOCAL, 1, LLViewerCamera::getInstance()->getOrigin().mV); +    F32 sunAngle = llmax(0.f, light_dir.mV[1]); +    F32 scaledAngle = 1.f - sunAngle; -        if (LLViewerCamera::getInstance()->cameraUnderWater()) -        { -            shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleBelow()); -        } -        else -        { -            shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleAbove()); -        } +    shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up ? 1 : 0); -        LLGLDisable cullface(GL_CULL_FACE); +    // SL-15861 This was changed from getRotatedLightNorm() as it was causing +    // lightnorm in shaders\class1\windlight\atmosphericsFuncs.glsl in have inconsistent additive lighting for 180 degrees of the FOV. +    LLVector4 rotated_light_direction = LLEnvironment::instance().getClampedLightNorm(); +    shader->uniform3fv(LLViewerShaderMgr::LIGHTNORM, 1, rotated_light_direction.mV); -        pushWaterPlanes(edge); +    shader->uniform3fv(LLShaderMgr::WL_CAMPOSLOCAL, 1, LLViewerCamera::getInstance()->getOrigin().mV); -        shader->disableTexture(LLShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); -        shader->disableTexture(LLShaderMgr::WATER_SCREENTEX); -        shader->disableTexture(LLShaderMgr::BUMP_MAP); +    if (LLViewerCamera::getInstance()->cameraUnderWater()) +    { +        shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleBelow()); +    } +    else +    { +        shader->uniform1f(LLShaderMgr::WATER_REFSCALE, pwater->getScaleAbove()); +    } -        // clean up -        gPipeline.unbindDeferredShader(*shader); +    LLGLDisable cullface(GL_CULL_FACE); -        gGL.getTexUnit(bumpTex)->unbind(LLTexUnit::TT_TEXTURE); -        gGL.getTexUnit(bumpTex2)->unbind(LLTexUnit::TT_TEXTURE); -    } +    // Only push the water planes once. +    // Previously we did this twice: once for void water and one for region water. +    // However, the void water and region water shaders are the same exact shader. +    // They also had the same exact state with the sole exception setting an edge water flag. +    // That flag was not actually used anywhere in the shaders. +    // - Geenz 2025-02-11 +    pushWaterPlanes(0); -    gGL.getTexUnit(0)->activate(); -    gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); +    // clean up +    gPipeline.unbindDeferredShader(*shader);      gGL.setColorMask(true, false);  } @@ -333,22 +312,18 @@ void LLDrawPoolWater::pushWaterPlanes(int pass)      LLVOWater* water = nullptr;      for (LLFace* const& face : mDrawFace)      { -        if (!face) -            continue;          water = static_cast<LLVOWater*>(face->getViewerObject()); -        if (!water) -            continue; -        if ((bool)pass == (bool)water->getIsEdgePatch()) +        face->renderIndexed(); + +        // Note non-void water being drawn, updates required +        // Previously we had some logic to determine if this pass was also our water edge pass. +        // Now we only have one pass.  Check if we're doing a region water plane or void water plane. +        // - Geenz 2025-02-11 +        if (!water->getIsEdgePatch())          { -            face->renderIndexed(); - -            // Note non-void water being drawn, updates required -            if (!pass) // SL-16461 remove !LLPipeline::sUseOcclusion check -            { -                sNeedsReflectionUpdate = true; -                sNeedsDistortionUpdate = true; -            } +            sNeedsReflectionUpdate = true; +            sNeedsDistortionUpdate = true;          }      }  } diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index 0dd2c13bb8..5eabed86a7 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -670,6 +670,7 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF              break;          case FFLOAD_HDRI:              allowedv->push_back("exr"); +        case FFLOAD_MODEL:          case FFLOAD_COLLADA:              allowedv->push_back("dae");              break; diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index ff30c83f51..25970f8a08 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -33,6 +33,7 @@  #include "llagentui.h"  #include "llbase64.h"  #include "llcallbacklist.h" +#include "lldate.h"  #include "llenvironment.h"  #include "llimagejpeg.h"  #include "llmediactrl.h" @@ -692,7 +693,15 @@ void LLFloater360Capture::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent                                          );                  // execute the command on the page -                mWebBrowser->getMediaPlugin()->executeJavaScript(cmd); +                LLPluginClassMedia* plugin = mWebBrowser->getMediaPlugin(); +                if (plugin) +                { +                    plugin->executeJavaScript(cmd); +                } +                else +                { +                    LL_WARNS("360Capture") << "No media plugin found" << LL_ENDL; +                }              }          }          break; @@ -773,7 +782,15 @@ void LLFloater360Capture::onSaveLocalBtn()      // send it to the browser instance, triggering the equirectangular capture      // process and complimentary offer to save the image -    mWebBrowser->getMediaPlugin()->executeJavaScript(cmd); +    LLPluginClassMedia* plugin = mWebBrowser->getMediaPlugin(); +    if (plugin) +    { +        plugin->executeJavaScript(cmd); +    } +    else +    { +        LL_WARNS("360Capture") << "No media plugin found" << LL_ENDL; +    }  }  // We capture all 6 images sequentially and if parts of the world are moving @@ -863,15 +880,7 @@ const std::string LLFloater360Capture::generate_proposed_filename()      filename << "_";      // add in the current HH-MM-SS (with leading 0's) so users can easily save many shots in same folder -    std::time_t cur_epoch = std::time(nullptr); -    std::tm* tm_time = std::localtime(&cur_epoch); -    filename << std::setfill('0') << std::setw(4) << (tm_time->tm_year + 1900); -    filename << std::setfill('0') << std::setw(2) << (tm_time->tm_mon + 1); -    filename << std::setfill('0') << std::setw(2) << tm_time->tm_mday; -    filename << "_"; -    filename << std::setfill('0') << std::setw(2) << tm_time->tm_hour; -    filename << std::setfill('0') << std::setw(2) << tm_time->tm_min; -    filename << std::setfill('0') << std::setw(2) << tm_time->tm_sec; +    filename << LLDate::now().toLocalDateString("%Y%m%d_%H%M%S");      // the unusual way we save the output image (originates in the      // embedded browser and not the C++ code) means that the system diff --git a/indra/newview/llfloatercreatelandmark.cpp b/indra/newview/llfloatercreatelandmark.cpp index 2ce8a7a212..de84ce0e00 100644 --- a/indra/newview/llfloatercreatelandmark.cpp +++ b/indra/newview/llfloatercreatelandmark.cpp @@ -31,6 +31,7 @@  #include "llagent.h"  #include "llagentui.h"  #include "llcombobox.h" +#include "llfloaterreg.h"  #include "llinventoryfunctions.h"  #include "llinventoryobserver.h"  #include "lllandmarkactions.h" @@ -389,6 +390,7 @@ void LLFloaterCreateLandmark::setItem(const uuid_set_t& items)              {                  mItem = item;                  mAssetID = mItem->getAssetUUID(); +                mParentID = mItem->getParentUUID();                  setVisibleAndFrontmost(true);                  break;              } @@ -418,8 +420,7 @@ void LLFloaterCreateLandmark::updateItem(const uuid_set_t& items, U32 mask)                  closeFloater();              } -            LLUUID folder_id = mFolderCombo->getValue().asUUID(); -            if (folder_id != mItem->getParentUUID()) +            if (mParentID != mItem->getParentUUID())              {                  // user moved landmark in inventory,                  // assume that we are done all other changes should already be commited diff --git a/indra/newview/llfloatercreatelandmark.h b/indra/newview/llfloatercreatelandmark.h index fa6d001b8e..f97ade2c1b 100644 --- a/indra/newview/llfloatercreatelandmark.h +++ b/indra/newview/llfloatercreatelandmark.h @@ -69,6 +69,7 @@ private:      LLTextEditor*   mNotesEditor;      LLUUID          mLandmarksID;      LLUUID          mAssetID; +    LLUUID          mParentID;      LLLandmarksInventoryObserver*   mInventoryObserver;      LLPointer<LLInventoryItem>      mItem; diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp index cc13e5d059..7e13503103 100644 --- a/indra/newview/llfloateremojipicker.cpp +++ b/indra/newview/llfloateremojipicker.cpp @@ -57,8 +57,7 @@ static const S32 USED_EMOJIS_IMAGE_INDEX = 0x23F2;  // https://www.compart.com/en/unicode/U+1F6D1  static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1;  // The following categories should follow the required alphabetic order -static const std::string RECENTLY_USED_CATEGORY = "1 recently used"; -static const std::string FREQUENTLY_USED_CATEGORY = "2 frequently used"; +static const std::string FREQUENTLY_USED_CATEGORY = "frequently used";  // Floater state related variables  static std::list<llwchar> sRecentlyUsed; @@ -445,11 +444,10 @@ void LLFloaterEmojiPicker::fillGroups()      params.name = "all_categories";      createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX); -    // Create group and button for "Recently used" and/or "Frequently used" -    if (!sRecentlyUsed.empty() || !sFrequentlyUsed.empty()) +    // Create group and button for "Frequently used" +    if (!sFrequentlyUsed.empty())      {          std::map<std::string, std::vector<LLEmojiSearchResult>> cats; -        fillCategoryRecentlyUsed(cats);          fillCategoryFrequentlyUsed(cats);          if (!cats.empty()) @@ -482,40 +480,6 @@ void LLFloaterEmojiPicker::fillGroups()      resizeGroupButtons();  } -void LLFloaterEmojiPicker::fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats) -{ -    if (sRecentlyUsed.empty()) -        return; - -    std::vector<LLEmojiSearchResult> emojis; - -    // In case of empty mFilterPattern we'd use sRecentlyUsed directly -    if (!mFilterPattern.empty()) -    { -        // List all emojis in "Recently used" -        const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); -        std::size_t begin, end; -        for (llwchar emoji : sRecentlyUsed) -        { -            auto e2d = emoji2descr.find(emoji); -            if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) -            { -                for (const std::string& shortcode : e2d->second->ShortCodes) -                { -                if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern)) -                { -                    emojis.emplace_back(emoji, shortcode, begin, end); -                } -            } -        } -        } -        if (emojis.empty()) -            return; -    } - -    cats.emplace(std::make_pair(RECENTLY_USED_CATEGORY, emojis)); -} -  void LLFloaterEmojiPicker::fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats)  {      if (sFrequentlyUsed.empty()) @@ -756,7 +720,6 @@ void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchRes  {      // Place the category title      std::string title = -        category == RECENTLY_USED_CATEGORY ? getString("title_for_recently_used") :          category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") :          isupper(category.front()) ? category : LLStringUtil::capitalize(category);      LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, title); @@ -769,21 +732,7 @@ void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchRes      {          const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();          LLEmojiSearchResult emoji { 0, "", 0, 0 }; -        if (category == RECENTLY_USED_CATEGORY) -        { -            for (llwchar code : sRecentlyUsed) -            { -                const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code); -                if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) -                { -                    emoji.Character = code; -                    emoji.String = e2d->second->ShortCodes.front(); -                    createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params, -                        icon_rect, max_icons, bg, row, icon_index); -                } -            } -        } -        else if (category == FREQUENTLY_USED_CATEGORY) +        if (category == FREQUENTLY_USED_CATEGORY)          {              for (const auto& code : sFrequentlyUsed)              { diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h index 669683eb9e..b807adb67d 100644 --- a/indra/newview/llfloateremojipicker.h +++ b/indra/newview/llfloateremojipicker.h @@ -60,7 +60,6 @@ public:  private:      void initialize();      void fillGroups(); -    void fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats);      void fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats);      void fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index);      void createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji); diff --git a/indra/newview/llfloaterlagmeter.cpp b/indra/newview/llfloaterlagmeter.cpp index 28fa8dea9a..1377526f69 100644 --- a/indra/newview/llfloaterlagmeter.cpp +++ b/indra/newview/llfloaterlagmeter.cpp @@ -200,6 +200,7 @@ void LLFloaterLagMeter::determineNetwork()      // the network handlers are de-synched from the rendering.      F32Milliseconds client_frame_time = frame_recording.getPeriodMean(LLStatViewer::FRAME_STACKTIME); +    // Todo: account for LLPacketRing dropped packets? viewer drops those when it can't keep up      if(packet_loss >= mNetworkPacketLossCritical)      {          mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); diff --git a/indra/newview/llfloaterperformance.cpp b/indra/newview/llfloaterperformance.cpp index 315508f22b..3eb0c849d3 100644 --- a/indra/newview/llfloaterperformance.cpp +++ b/indra/newview/llfloaterperformance.cpp @@ -115,12 +115,12 @@ bool LLFloaterPerformance::postBuild()      mHUDList = mHUDsPanel->getChild<LLNameListCtrl>("hud_list");      mHUDList->setNameListType(LLNameListCtrl::SPECIAL);      mHUDList->setHoverIconName("StopReload_Off"); -    mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1)); +    mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1));      mObjectList = mComplexityPanel->getChild<LLNameListCtrl>("obj_list");      mObjectList->setNameListType(LLNameListCtrl::SPECIAL);      mObjectList->setHoverIconName("StopReload_Off"); -    mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1)); +    mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1));      mSettingsPanel->getChild<LLButton>("advanced_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickAdvanced, this));      mSettingsPanel->getChild<LLButton>("defaults_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickDefaults, this)); @@ -527,9 +527,13 @@ void LLFloaterPerformance::setFPSText()      mTextFPSLabel->setValue(fps_text);  } -void LLFloaterPerformance::detachItem(const LLUUID& item_id) +void LLFloaterPerformance::detachObject(const LLUUID& obj_id)  { -    LLAppearanceMgr::instance().removeItemFromAvatar(item_id); +    LLViewerObject* obj = gObjectList.findObject(obj_id); +    if (obj) +    { +        LLAppearanceMgr::instance().removeItemFromAvatar(obj->getAttachmentItemID()); +    }  }  void LLFloaterPerformance::onClickAdvanced() diff --git a/indra/newview/llfloaterperformance.h b/indra/newview/llfloaterperformance.h index a7100eb350..6cca85a009 100644 --- a/indra/newview/llfloaterperformance.h +++ b/indra/newview/llfloaterperformance.h @@ -48,7 +48,7 @@ public:      void hidePanels();      void showAutoadjustmentsPanel(); -    void detachItem(const LLUUID& item_id); +    void detachObject(const LLUUID& obj_id);      void onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index e673752986..fdac390e8a 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -329,6 +329,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)      mCommitCallbackRegistrar.add("Pref.AutoAdjustments",         boost::bind(&LLFloaterPreference::onClickAutoAdjustments, this));      mCommitCallbackRegistrar.add("Pref.HardwareDefaults",       boost::bind(&LLFloaterPreference::setHardwareDefaults, this));      mCommitCallbackRegistrar.add("Pref.AvatarImpostorsEnable",  boost::bind(&LLFloaterPreference::onAvatarImpostorsEnable, this)); +    mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxNonImpostors", boost::bind(&LLFloaterPreference::updateMaxNonImpostors, this));      mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxComplexity",    boost::bind(&LLFloaterPreference::updateMaxComplexity, this));      mCommitCallbackRegistrar.add("Pref.RenderOptionUpdate",     boost::bind(&LLFloaterPreference::onRenderOptionEnable, this));      mCommitCallbackRegistrar.add("Pref.WindowedMod",            boost::bind(&LLFloaterPreference::onCommitWindowedMode, this)); @@ -360,6 +361,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)      LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this );      mComplexityChangedSignal = gSavedSettings.getControl("RenderAvatarMaxComplexity")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::updateComplexityText, this)); +    mImpostorsChangedSignal = gSavedSettings.getControl("RenderAvatarMaxNonImpostors")->getSignal()->connect(boost::bind(&LLFloaterPreference::updateIndirectMaxNonImpostors, this, _2));      mCommitCallbackRegistrar.add("Pref.ClearLog",               boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));      mCommitCallbackRegistrar.add("Pref.DeleteTranscripts",      boost::bind(&LLFloaterPreference::onDeleteTranscripts, this)); @@ -542,7 +544,12 @@ void LLFloaterPreference::onDoNotDisturbResponseChanged()  LLFloaterPreference::~LLFloaterPreference()  {      LLConversationLog::instance().removeObserver(this); +    if (LLAvatarPropertiesProcessor::instanceExists()) +    { +        LLAvatarPropertiesProcessor::getInstance()->removeObserver(gAgent.getID(), this); +    }      mComplexityChangedSignal.disconnect(); +    mImpostorsChangedSignal.disconnect();  }  void LLFloaterPreference::draw() @@ -1287,6 +1294,9 @@ void LLAvatarComplexityControls::setIndirectMaxArc()  void LLFloaterPreference::refresh()  {      LLPanel::refresh(); +    setMaxNonImpostorsText( +        gSavedSettings.getU32("RenderAvatarMaxNonImpostors"), +        getChild<LLTextBox>("IndirectMaxNonImpostorsText", true));      LLAvatarComplexityControls::setText(          gSavedSettings.getU32("RenderAvatarMaxComplexity"),          getChild<LLTextBox>("IndirectMaxComplexityText", true)); @@ -1561,6 +1571,44 @@ void LLAvatarComplexityControls::setRenderTimeText(F32 value, LLTextBox* text_bo      }  } +void LLFloaterPreference::updateMaxNonImpostors() +{ +    // Called when the IndirectMaxNonImpostors control changes +    // Responsible for fixing the slider label (IndirectMaxNonImpostorsText) and setting RenderAvatarMaxNonImpostors +    LLSliderCtrl* ctrl = getChild<LLSliderCtrl>("IndirectMaxNonImpostors", true); +    U32 value = ctrl->getValue().asInteger(); + +    if (0 == value || LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER <= value) +    { +        value = 0; +    } +    gSavedSettings.setU32("RenderAvatarMaxNonImpostors", value); +    LLVOAvatar::updateImpostorRendering(value); // make it effective immediately +    setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText")); +} + +void LLFloaterPreference::updateIndirectMaxNonImpostors(const LLSD& newvalue) +{ +    U32 value = newvalue.asInteger(); +    if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors"))) +    { +        gSavedSettings.setU32("IndirectMaxNonImpostors", value); +    } +    setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText")); +} + +void LLFloaterPreference::setMaxNonImpostorsText(U32 value, LLTextBox* text_box) +{ +    if (0 == value) +    { +        text_box->setText(LLTrans::getString("no_limit")); +    } +    else +    { +        text_box->setText(llformat("%d", value)); +    } +} +  void LLFloaterPreference::updateMaxComplexity()  {      // Called when the IndirectMaxComplexity control changes diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 51ed3d8179..fa9c421a8f 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -206,6 +206,9 @@ private:      void onDeleteTranscripts();      void onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response);      void updateDeleteTranscriptsButton(); +    void updateMaxNonImpostors(); +    void updateIndirectMaxNonImpostors(const LLSD& newvalue); +    void setMaxNonImpostorsText(U32 value, LLTextBox* text_box);      void updateMaxComplexity();      void updateComplexityText();      static bool loadFromFilename(const std::string& filename, std::map<std::string, std::string> &label_map); @@ -234,6 +237,7 @@ private:      std::unique_ptr< ll::prefs::SearchData > mSearchData;      bool mSearchDataDirty; +    boost::signals2::connection	mImpostorsChangedSignal;      boost::signals2::connection mComplexityChangedSignal;      void onUpdateFilterTerm( bool force = false ); diff --git a/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp b/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp index 8e8967ee3d..a8a1e507a8 100644 --- a/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp +++ b/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp @@ -51,6 +51,8 @@ LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const L      mCommitCallbackRegistrar.add("Pref.Cancel", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnCancel, this, _2));      mCommitCallbackRegistrar.add("Pref.OK",     boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnOK, this, _2)); + +    mImpostorsChangedSignal = gSavedSettings.getControl("RenderAvatarMaxNonImpostors")->getSignal()->connect(boost::bind(&LLFloaterPreferenceGraphicsAdvanced::updateIndirectMaxNonImpostors, this, _2));  }  LLFloaterPreferenceGraphicsAdvanced::~LLFloaterPreferenceGraphicsAdvanced() @@ -58,7 +60,6 @@ LLFloaterPreferenceGraphicsAdvanced::~LLFloaterPreferenceGraphicsAdvanced()      mComplexityChangedSignal.disconnect();      mComplexityModeChangedSignal.disconnect();      mLODFactorChangedSignal.disconnect(); -    mNumImpostorsChangedSignal.disconnect();  }  bool LLFloaterPreferenceGraphicsAdvanced::postBuild() @@ -254,8 +255,8 @@ void LLFloaterPreferenceGraphicsAdvanced::updateIndirectMaxNonImpostors(const LL      if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors")))      {          gSavedSettings.setU32("IndirectMaxNonImpostors", value); -        setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));      } +    setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText"));  }  void LLFloaterPreferenceGraphicsAdvanced::setMaxNonImpostorsText(U32 value, LLTextBox* text_box) diff --git a/indra/newview/llfloaterpreferencesgraphicsadvanced.h b/indra/newview/llfloaterpreferencesgraphicsadvanced.h index 61203be068..a1a54f238d 100644 --- a/indra/newview/llfloaterpreferencesgraphicsadvanced.h +++ b/indra/newview/llfloaterpreferencesgraphicsadvanced.h @@ -61,6 +61,7 @@ protected:      void        onBtnOK(const LLSD& userdata);      void        onBtnCancel(const LLSD& userdata); +    boost::signals2::connection	mImpostorsChangedSignal;      boost::signals2::connection mComplexityChangedSignal;      boost::signals2::connection mComplexityModeChangedSignal;      boost::signals2::connection mLODFactorChangedSignal; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 3239921259..334f32d051 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -851,6 +851,13 @@ void LLPanelRegionInfo::initCtrl(const std::string& name)      getChild<LLUICtrl>(name)->setCommitCallback(boost::bind(&LLPanelRegionInfo::onChangeAnything, this));  } +void LLPanelRegionInfo::initAndSetTexCtrl(LLTextureCtrl*& ctrl, const std::string& name) +{ +    ctrl = findChild<LLTextureCtrl>(name); +    if (ctrl) +        ctrl->setOnSelectCallback([this](LLUICtrl* ctrl, const LLSD& param){ onChangeAnything(); }); +} +  template<typename CTRL>  void LLPanelRegionInfo::initAndSetCtrl(CTRL*& ctrl, const std::string& name)  { @@ -1580,7 +1587,7 @@ bool LLPanelRegionTerrainInfo::postBuild()      for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)      { -        initAndSetCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i)); +        initAndSetTexCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i));          if (mTextureDetailCtrl[i])          {              mTextureDetailCtrl[i]->setBakeTextureEnabled(false); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 201d8b0a68..a8631c36ca 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -159,6 +159,7 @@ public:  protected:      void initCtrl(const std::string& name);      template<typename CTRL> void initAndSetCtrl(CTRL*& ctrl, const std::string& name); +    void initAndSetTexCtrl(LLTextureCtrl*& ctrl, const std::string& name);      // Returns true if update sent and apply button should be      // disabled. diff --git a/indra/newview/llgiveinventory.cpp b/indra/newview/llgiveinventory.cpp index 1259b65ebe..57dd203f2f 100644 --- a/indra/newview/llgiveinventory.cpp +++ b/indra/newview/llgiveinventory.cpp @@ -414,7 +414,11 @@ void LLGiveInventory::commitGiveInventoryItem(const LLUUID& to_agent,      effectp->setTargetObject(gObjectList.findObject(to_agent));      effectp->setDuration(LL_HUD_DUR_SHORT);      effectp->setColor(LLColor4U(gAgent.getEffectColor())); -    gFloaterTools->dirty(); + +    if (gFloaterTools) +    { +        gFloaterTools->dirty(); +    }      LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY); @@ -572,7 +576,11 @@ bool LLGiveInventory::commitGiveInventoryCategory(const LLUUID& to_agent,          effectp->setTargetObject(gObjectList.findObject(to_agent));          effectp->setDuration(LL_HUD_DUR_SHORT);          effectp->setColor(LLColor4U(gAgent.getEffectColor())); -        gFloaterTools->dirty(); + +        if (gFloaterTools) +        { +            gFloaterTools->dirty(); +        }          LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY); diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp index 2ec161c093..0f3e220ae0 100644 --- a/indra/newview/llheroprobemanager.cpp +++ b/indra/newview/llheroprobemanager.cpp @@ -80,6 +80,17 @@ void LLHeroProbeManager::update()          return;      } +    // Part of a hacky workaround to fix #3331. +    // For some reason clearing shaders will cause mirrors to actually work. +    // There's likely some deeper state issue that needs to be resolved. +    // - Geenz 2025-02-25 +    if (!mInitialized && LLStartUp::getStartupState() > STATE_PRECACHE) +    { +        LLViewerShaderMgr::instance()->clearShaderCache(); +        LLViewerShaderMgr::instance()->setShaders(); +        mInitialized = true; +    } +      LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;      LL_PROFILE_GPU_ZONE("hero manager update");      llassert(!gCubeSnapshot); // assert a snapshot is not in progress diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h index 58a94a3de8..2737ec5ddf 100644 --- a/indra/newview/llheroprobemanager.h +++ b/indra/newview/llheroprobemanager.h @@ -144,6 +144,7 @@ private:      std::vector<LLPointer<LLVOVolume>>                       mHeroVOList;      LLPointer<LLVOVolume>                                 mNearestHero; - +    // Part of a hacky workaround to fix #3331. +    bool mInitialized = false;  }; diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 590cd09a31..4e8bcc4f7a 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -650,14 +650,6 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,                      asset_type = (LLAssetType::EType)(atoi((*(iter++)).c_str()));                      iter++; // wearable type if applicable, otherwise asset type                      item_name = std::string((*(iter++)).c_str()); -                    // Note There is more elements in 'tokens' ... - - -                    for (int i = 0; i < 6; i++) -                    { -                        LL_WARNS() << *(iter++) << LL_ENDL; -                        iter++; -                    }                  }              }              else diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 34a4b5b230..04e8a26008 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -3550,6 +3550,7 @@ void LLIMMgr::inviteToSession(              && voice_invite && "VoiceInviteQuestionDefault" == question_type)          {              LL_INFOS("IMVIEW") << "Rejecting voice call from initiating muted resident " << caller_name << LL_ENDL; +            payload["voice_channel_info"] = voice_channel_info;              LLIncomingCallDialog::processCallResponse(1, payload);              return;          } @@ -3598,6 +3599,7 @@ void LLIMMgr::inviteToSession(                  send_do_not_disturb_message(gMessageSystem, caller_id, session_id);              }              // silently decline the call +            payload["voice_channel_info"] = voice_channel_info;              LLIncomingCallDialog::processCallResponse(1, payload);              return;          } diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index e7584018a8..479b7c19ac 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -844,7 +844,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,              disabled_items.push_back(std::string("Copy"));          } -        if (isAgentInventory() && !single_folder_root) +        if (isAgentInventory() && !single_folder_root && !isMarketplaceListingsFolder())          {              items.push_back(std::string("New folder from selected"));              items.push_back(std::string("Subfolder Separator")); @@ -5315,7 +5315,7 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI      // Note: creation will take time, so passing folder id to callback is slightly unreliable,      // but so is collecting and passing descendants' ids -    inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb); +    inventory_func_type func = boost::bind(outfitFolderCreatedCallback, inv_cat->getUUID(), _1, cb, mInventoryPanel);      gInventory.createNewCategory(dest_id,                                   LLFolderType::FT_OUTFIT,                                   inv_cat->getName(), @@ -5323,11 +5323,25 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI                                   inv_cat->getThumbnailUUID());  } -void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb) +void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, +                                                 LLUUID cat_dest_id, +                                                 LLPointer<LLInventoryCallback> cb, +                                                 LLHandle<LLInventoryPanel> inventory_panel)  {      LLInventoryModel::cat_array_t* categories;      LLInventoryModel::item_array_t* items; -    getInventoryModel()->getDirectDescendentsOf(cat_source_id, categories, items); + +    LLInventoryPanel* panel = inventory_panel.get(); +    if (!panel) +    { +        return; +    } +    LLInventoryModel*  model = panel->getModel(); +    if (!model) +    { +        return; +    } +    model->getDirectDescendentsOf(cat_source_id, categories, items);      LLInventoryObject::const_object_list_t link_array; diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 746b79ce87..3e7f74384b 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -378,7 +378,10 @@ public:      static void staticFolderOptionsMenu();  protected: -    void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb); +    static void outfitFolderCreatedCallback(LLUUID cat_source_id, +                                            LLUUID cat_dest_id, +                                            LLPointer<LLInventoryCallback> cb, +                                            LLHandle<LLInventoryPanel> inventory_panel);      void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response);      void perform_pasteFromClipboard();      void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level); diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index d8e6bf380e..2cf1554957 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -30,6 +30,7 @@  #include "llaisapi.h"  #include "llagent.h"  #include "llappviewer.h" +#include "llappearancemgr.h"  #include "llcallbacklist.h"  #include "llinventorymodel.h"  #include "llinventorypanel.h" @@ -470,6 +471,22 @@ void LLInventoryModelBackgroundFetch::fetchCOF(nullary_func_t callback)                           callback();                           LLUUID cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);                           LLInventoryModelBackgroundFetch::getInstance()->onAISFolderCalback(cat_id, id, FT_DEFAULT); + +                         if (id.notNull()) +                         { +                             // COF might have fetched base outfit folder through a link, but it hasn't +                             // fetched base outfit's content, which doesn't nessesary match COF, +                             // so make sure it's up to date +                             LLUUID baseoutfit_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID(); +                             if (baseoutfit_id.notNull()) +                             { +                                 LLViewerInventoryCategory* cat = gInventory.getCategory(baseoutfit_id); +                                 if (!cat || cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) +                                 { +                                     LLInventoryModelBackgroundFetch::getInstance()->fetchFolderAndLinks(baseoutfit_id, no_op); +                                 } +                             } +                         }                       });      // start idle loop to track completion diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 5066a7da7f..5a067ba454 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -579,7 +579,7 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve      {          if (model_item && view_item && viewmodel_item)          { -            const LLUUID& idp = viewmodel_item->getUUID(); +            const LLUUID idp = viewmodel_item->getUUID();              view_item->destroyView();              removeItemID(idp);          } diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index bf49f33049..fbf4a7d1dd 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -892,6 +892,11 @@ bool LLLogChat::isTranscriptFileFound(std::string fullname)      return result;  } +std::string LLLogChat::getGroupChatSuffix() +{ +    return GROUP_CHAT_SUFFIX; +} +  //*TODO mark object's names in a special way so that they will be distinguishable form avatar name  //which are more strict by its nature (only firstname and secondname)  //Example, an object's name can be written like "Object <actual_object's_name>" diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index dbbd8ae1ba..39c40b985e 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -127,6 +127,8 @@ public:      static bool isAdHocTranscriptExist(std::string file_name);      static bool isTranscriptFileFound(std::string fullname); +    static std::string getGroupChatSuffix(); +      bool historyThreadsFinished(LLUUID session_id);      LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id);      LLDeleteHistoryThread* getDeleteHistoryThread(LLUUID session_id); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 93453f507c..e1fa84b4d8 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -255,12 +255,12 @@  //     mSkinInfoQ               mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0])  //     mDecompositionRequests   mMutex        rw.repo.mMutex, ro.repo.none [5]  //     mPhysicsShapeRequests    mMutex        rw.repo.mMutex, ro.repo.none [5] -//     mDecompositionQ          mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0]) +//     mDecompositionQ          mMutex        rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was:  [0])  //     mHeaderReqQ              mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex  //     mLODReqQ                 mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex -//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mMutex -//     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [5], rw.main.mMutex -//     mPendingLOD              mMutex        rw.repo.mMutex, rw.any.mMutex +//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mLoadedMutex +//     mLoadedQ                 mMutex        rw.repo.mLoadedMutex, ro.main.none [5], rw.main.mLoadedMutex +//     mPendingLOD              mMutex        rw.repo.mPendingMutex, rw.any.mPendingMutex  //     mGetMeshCapability       mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0])  //     mGetMesh2Capability      mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0])  //     mGetMeshVersion          mMutex        rw.main.mMutex, ro.repo.mMutex @@ -343,20 +343,22 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");  LLMeshRepository gMeshRepo; -const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space +constexpr U32 CACHE_PREAMBLE_VERSION = 1; +constexpr S32 CACHE_PREAMBLE_SIZE = sizeof(U32) * 3; //version, header_size, flags +constexpr S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space -const S32 REQUEST2_HIGH_WATER_MIN = 32;                 // Limits for GetMesh2 regions -const S32 REQUEST2_HIGH_WATER_MAX = 100; -const S32 REQUEST2_LOW_WATER_MIN = 16; -const S32 REQUEST2_LOW_WATER_MAX = 50; +constexpr S32 REQUEST2_HIGH_WATER_MIN = 32;                 // Limits for GetMesh2 regions +constexpr S32 REQUEST2_HIGH_WATER_MAX = 100; +constexpr S32 REQUEST2_LOW_WATER_MIN = 16; +constexpr S32 REQUEST2_LOW_WATER_MAX = 50; -const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;        // Size at which requests goes to narrow/slow queue -const long SMALL_MESH_XFER_TIMEOUT = 120L;              // Seconds to complete xfer, small mesh downloads -const long LARGE_MESH_XFER_TIMEOUT = 600L;              // Seconds to complete xfer, large downloads +constexpr U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;        // Size at which requests goes to narrow/slow queue +constexpr long SMALL_MESH_XFER_TIMEOUT = 120L;              // Seconds to complete xfer, small mesh downloads +constexpr long LARGE_MESH_XFER_TIMEOUT = 600L;              // Seconds to complete xfer, large downloads -const U32 DOWNLOAD_RETRY_LIMIT = 8; -const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds +constexpr U32 DOWNLOAD_RETRY_LIMIT = 8; +constexpr F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds  // Would normally like to retry on uploads as some  // retryable failures would be recoverable.  Unfortunately, @@ -366,7 +368,7 @@ const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds  // cap which then produces a 404 on retry destroying some  // (occasionally) useful error information.  We'll leave  // upload retries to the user as in the past.  SH-4667. -const long UPLOAD_RETRY_LIMIT = 0L; +constexpr long UPLOAD_RETRY_LIMIT = 0L;  // Maximum mesh version to support.  Three least significant digits are reserved for the minor version,  // with major version changes indicating a format change that is not backwards compatible and should not @@ -374,7 +376,7 @@ const long UPLOAD_RETRY_LIMIT = 0L;  // present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999,  // but not 1.0 (integer 1000).  // See wiki at https://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format -const S32 MAX_MESH_VERSION = 999; +constexpr S32 MAX_MESH_VERSION = 999;  U32 LLMeshRepository::sBytesReceived = 0;  U32 LLMeshRepository::sMeshRequestCount = 0; @@ -386,12 +388,12 @@ U32 LLMeshRepository::sLODProcessing = 0;  U32 LLMeshRepository::sLODPending = 0;  U32 LLMeshRepository::sCacheBytesRead = 0; -U32 LLMeshRepository::sCacheBytesWritten = 0; +std::atomic<U32> LLMeshRepository::sCacheBytesWritten = 0;  U32 LLMeshRepository::sCacheBytesHeaders = 0;  U32 LLMeshRepository::sCacheBytesSkins = 0;  U32 LLMeshRepository::sCacheBytesDecomps = 0;  U32 LLMeshRepository::sCacheReads = 0; -U32 LLMeshRepository::sCacheWrites = 0; +std::atomic<U32> LLMeshRepository::sCacheWrites = 0;  U32 LLMeshRepository::sMaxLockHoldoffs = 0;  LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false);  // true -> gather cpu metrics @@ -550,6 +552,7 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate  std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0;  std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveSkinRequests = 0;  U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;  S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN;  S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -584,6 +587,7 @@ public:          : LLCore::HttpHandler(),            mMeshParams(),            mProcessed(false), +          mHasDataOwnership(true),            mHttpHandle(LLCORE_HTTP_HANDLE_INVALID),            mOffset(offset),            mRequestedBytes(requested_bytes) @@ -607,6 +611,9 @@ public:      LLCore::HttpHandle mHttpHandle;      U32 mOffset;      U32 mRequestedBytes; + +protected: +    bool mHasDataOwnership = true;  }; @@ -659,6 +666,9 @@ public:      virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);      virtual void processFailure(LLCore::HttpStatus status); +private: +    void processLod(U8* data, S32 data_size); +  public:      S32 mLOD;  }; @@ -674,13 +684,17 @@ public:      LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes)          : LLMeshHandlerBase(offset, requested_bytes),            mMeshID(id) -    {} +    { +        LLMeshRepoThread::incActiveSkinRequests(); +    }      virtual ~LLMeshSkinInfoHandler();  protected:      LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &);       // Not defined      void operator=(const LLMeshSkinInfoHandler &);              // Not defined +    void processSkin(U8* data, S32 data_size); +  public:      virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);      virtual void processFailure(LLCore::HttpStatus status); @@ -823,6 +837,14 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,      gMeshRepo.uploadError(args);  } +void write_preamble(LLFileSystem &file, S32 header_bytes, S32 flags) +{ +    LLMeshRepository::sCacheBytesWritten += CACHE_PREAMBLE_SIZE; +    file.write((U8*)&CACHE_PREAMBLE_VERSION, sizeof(U32)); +    file.write((U8*)&header_bytes, sizeof(U32)); +    file.write((U8*)&flags, sizeof(U32)); +} +  LLMeshRepoThread::LLMeshRepoThread()  : LLThread("mesh repo"),    mHttpRequest(NULL), @@ -837,6 +859,9 @@ LLMeshRepoThread::LLMeshRepoThread()      mMutex = new LLMutex();      mHeaderMutex = new LLMutex(); +    mLoadedMutex = new LLMutex(); +    mPendingMutex = new LLMutex(); +    mSkinMapMutex = new LLMutex();      mSignal = new LLCondition();      mHttpRequest = new LLCore::HttpRequest;      mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); @@ -849,6 +874,11 @@ LLMeshRepoThread::LLMeshRepoThread()      mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH);      mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2);      mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH); + +    // Lod processing is expensive due to the number of requests +    // and a need to do expensive cacheOptimize(). +    mMeshThreadPool.reset(new LL::ThreadPool("MeshLodProcessing", 2)); +    mMeshThreadPool->start();  } @@ -875,13 +905,21 @@ LLMeshRepoThread::~LLMeshRepoThread()      }      delete mHttpRequest; -    mHttpRequest = NULL; +    mHttpRequest = nullptr;      delete mMutex; -    mMutex = NULL; +    mMutex = nullptr;      delete mHeaderMutex; -    mHeaderMutex = NULL; +    mHeaderMutex = nullptr; +    delete mLoadedMutex; +    mLoadedMutex = nullptr; +    delete mPendingMutex; +    mPendingMutex = nullptr; +    delete mSkinMapMutex; +    mSkinMapMutex = nullptr;      delete mSignal; -    mSignal = NULL; +    mSignal = nullptr; +    delete[] mDiskCacheBuffer; +    mDiskCacheBuffer = nullptr;  }  void LLMeshRepoThread::run() @@ -908,6 +946,7 @@ void LLMeshRepoThread::run()          // On the other hand, this may actually be an effective and efficient scheme...          mSignal->wait(); +        LL_PROFILE_ZONE_NAMED("mesh_thread_loop")          if (LLApp::isExiting())          { @@ -925,11 +964,55 @@ void LLMeshRepoThread::run()          }          sRequestWaterLevel = static_cast<S32>(mHttpRequestSet.size());            // Stats data update -        // NOTE: order of queue processing intentionally favors LOD requests over header requests +        // NOTE: order of queue processing intentionally favors LOD and Skin requests over header requests          // Todo: we are processing mLODReqQ, mHeaderReqQ, mSkinRequests, mDecompositionRequests and mPhysicsShapeRequests          // in relatively similar manners, remake code to simplify/unify the process,          // like processRequests(&requestQ, fetchFunction); which does same thing for each element +        if (mHttpRequestSet.size() < sRequestHighWater +            && !mSkinRequests.empty()) +        { +            if (!mSkinRequests.empty()) +            { +                std::list<UUIDBasedRequest> incomplete; +                while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) +                { + +                    mMutex->lock(); +                    auto req = mSkinRequests.front(); +                    mSkinRequests.pop_front(); +                    mMutex->unlock(); +                    if (req.isDelayed()) +                    { +                        incomplete.emplace_back(req); +                    } +                    else if (!fetchMeshSkinInfo(req.mId)) +                    { +                        if (req.canRetry()) +                        { +                            req.updateTime(); +                            incomplete.emplace_back(req); +                        } +                        else +                        { +                            LLMutexLock locker(mLoadedMutex); +                            mSkinUnavailableQ.push_back(req); +                            LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; +                        } +                    } +                } + +                if (!incomplete.empty()) +                { +                    LLMutexLock locker(mMutex); +                    for (const auto& req : incomplete) +                    { +                        mSkinRequests.push_back(req); +                    } +                } +            } +        } +          if (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)          {              std::list<LODRequest> incomplete; @@ -950,7 +1033,7 @@ void LLMeshRepoThread::run()                      // failed to load before, wait a bit                      incomplete.push_front(req);                  } -                else if (!fetchMeshLOD(req.mMeshParams, req.mLOD, req.canRetry())) +                else if (!fetchMeshLOD(req.mMeshParams, req.mLOD))                  {                      if (req.canRetry())                      { @@ -961,7 +1044,7 @@ void LLMeshRepoThread::run()                      else                      {                          // too many fails -                        LLMutexLock lock(mMutex); +                        LLMutexLock lock(mLoadedMutex);                          mUnavailableQ.push_back(req);                          LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL;                      } @@ -998,7 +1081,7 @@ void LLMeshRepoThread::run()                      // failed to load before, wait a bit                      incomplete.push_front(req);                  } -                else if (!fetchMeshHeader(req.mMeshParams, req.canRetry())) +                else if (!fetchMeshHeader(req.mMeshParams))                  {                      if (req.canRetry())                      { @@ -1028,54 +1111,13 @@ void LLMeshRepoThread::run()          // performing long-duration actions.          if (mHttpRequestSet.size() < sRequestHighWater -            && (!mSkinRequests.empty() -            || !mDecompositionRequests.empty() +            && (!mDecompositionRequests.empty()              || !mPhysicsShapeRequests.empty()))          {              // Something to do probably, lock and double-check.  We don't want              // to hold the lock long here.  That will stall main thread activities              // so we bounce it. -            if (!mSkinRequests.empty()) -            { -                std::list<UUIDBasedRequest> incomplete; -                while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) -                { - -                    mMutex->lock(); -                    auto req = mSkinRequests.front(); -                    mSkinRequests.pop_front(); -                    mMutex->unlock(); -                    if (req.isDelayed()) -                    { -                        incomplete.emplace_back(req); -                    } -                    else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) -                    { -                        if (req.canRetry()) -                        { -                            req.updateTime(); -                            incomplete.emplace_back(req); -                        } -                        else -                        { -                            LLMutexLock locker(mMutex); -                            mSkinUnavailableQ.push_back(req); -                            LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; -                        } -                    } -                } - -                if (!incomplete.empty()) -                { -                    LLMutexLock locker(mMutex); -                    for (const auto& req : incomplete) -                    { -                        mSkinRequests.push_back(req); -                    } -                } -            } -              // holding lock, try next list              // *TODO:  For UI/debug-oriented lists, we might drop the fine-              // grained locking as there's a lowered expectation of smoothness @@ -1103,7 +1145,7 @@ void LLMeshRepoThread::run()                          }                          else                          { -                            LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL; +                            LL_DEBUGS(LOG_MESH) << "mDecompositionRequests failed: " << req.mId << LL_ENDL;                          }                      }                  } @@ -1139,7 +1181,7 @@ void LLMeshRepoThread::run()                          }                          else                          { -                            LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL; +                            LL_DEBUGS(LOG_MESH) << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;                          }                      }                  } @@ -1195,40 +1237,91 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32      }  } -  void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)  { //could be called from any thread      const LLUUID& mesh_id = mesh_params.getSculptID(); -    LLMutexLock lock(mMutex); -    LLMutexLock header_lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); -    if (iter != mMeshHeader.end()) +    loadMeshLOD(mesh_id, mesh_params, lod); +} + +void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod) +{ +    if (hasHeader(mesh_id))      { //if we have the header, request LOD byte range          LODRequest req(mesh_params, lod);          { +            LLMutexLock lock(mMutex);              mLODReqQ.push(req);              LLMeshRepository::sLODProcessing++;          }      }      else      { +        LLMutexLock lock(mPendingMutex);          HeaderRequest req(mesh_params);          pending_lod_map::iterator pending = mPendingLOD.find(mesh_id);          if (pending != mPendingLOD.end()) -        { //append this lod request to existing header request -            pending->second.push_back(lod); -            llassert(pending->second.size() <= LLModel::NUM_LODS); +        { +            //append this lod request to existing header request +            if (lod < LLModel::NUM_LODS && lod >= 0) +            { +                pending->second[lod]++; +            } +            else +            { +                LL_WARNS(LOG_MESH) << "Invalid LOD request: " << lod << "for mesh" << mesh_id << LL_ENDL; +            } +            llassert_msg(lod < LLModel::NUM_LODS, "Requested lod is out of bounds");          }          else -        { //if no header request is pending, fetch header +        { +            //if no header request is pending, fetch header +            auto& array = mPendingLOD[mesh_id]; +            std::fill(array.begin(), array.end(), 0); +            array[lod]++; + +            LLMutexLock lock(mMutex);              mHeaderReqQ.push(req); -            mPendingLOD[mesh_id].push_back(lod);          }      }  } +U8* LLMeshRepoThread::getDiskCacheBuffer(S32 size) +{ +    if (mDiskCacheBufferSize < size) +    { +        const S32 MINIMUM_BUFFER_SIZE = 8192; // a minimum to avoid frequent early reallocations +        size = llmax(MINIMUM_BUFFER_SIZE, size); +        delete[] mDiskCacheBuffer; +        try +        { +            mDiskCacheBuffer = new U8[size]; +        } +        catch (std::bad_alloc&) +        { +            LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh thread's buffer, size: " << size << LL_ENDL; +            mDiskCacheBuffer = NULL; + +            // Not sure what size is reasonable +            // but if 30MB allocation failed, we definitely have issues +            const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB +            if (size < MAX_SIZE) +            { +                LLAppViewer::instance()->outOfMemorySoftQuit(); +            } // else ignore failures for anomalously large data +        } +        mDiskCacheBufferSize = size; +    } +    else +    { +        // reusing old buffer, reset heading bytes to ensure +        // old content won't be parsable if something fails. +        memset(mDiskCacheBuffer, 0, 16); +    } +    return mDiskCacheBuffer; +} +  // Mutex:  must be holding mMutex when called  void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap)  { @@ -1327,7 +1420,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,  } -bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)  {      LL_PROFILE_ZONE_SCOPED;      if (!mHeaderMutex) @@ -1337,7 +1430,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)      mHeaderMutex->lock(); -    auto header_it = mMeshHeader.find(mesh_id); +    mesh_header_map::const_iterator header_it = mMeshHeader.find(mesh_id);      if (header_it == mMeshHeader.end())      { //we have no header info for this mesh, do nothing          mHeaderMutex->unlock(); @@ -1346,23 +1439,24 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)      ++LLMeshRepository::sMeshRequestCount;      bool ret = true; -    U32 header_size = header_it->second.first; +    const LLMeshHeader& header = header_it->second; +    U32 header_size = header.mHeaderSize;      if (header_size > 0)      { -        const LLMeshHeader& header = header_it->second.second; -          S32 version = header.mVersion;          S32 offset = header_size + header.mSkinOffset;          S32 size = header.mSkinSize; +        bool in_cache = header.mSkinInCache;          mHeaderMutex->unlock();          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          {              //check cache for mesh skin info +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset + size) +            if (in_cache && file.getSize() >= disk_ofset + size)              {                  U8* buffer = new(std::nothrow) U8[size];                  if (!buffer) @@ -1370,19 +1464,19 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)                      LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;                      // Not sure what size is reasonable for skin info, -                    // but if 20MB allocation failed, we definetely have issues +                    // but if 30MB allocation failed, we definitely have issues                      const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB                      if (size < MAX_SIZE)                      {                          LLAppViewer::instance()->outOfMemorySoftQuit();                      } // else ignore failures for anomalously large data -                    LLMutexLock locker(mMutex); +                    LLMutexLock locker(mLoadedMutex);                      mSkinUnavailableQ.emplace_back(mesh_id);                      return true;                  }                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1393,14 +1487,67 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)                  }                  if (!zero) -                { //attempt to parse -                    if (skinInfoReceived(mesh_id, buffer, size)) +                { +                    //attempt to parse +                    bool posted = mMeshThreadPool->getQueue().post( +                        [mesh_id, buffer, size] +                        () +                    { +                        if (!gMeshRepo.mThread->skinInfoReceived(mesh_id, buffer, size)) +                        { +                            // either header is faulty or something else overwrote the cache +                            S32 header_size = 0; +                            U32 header_flags = 0; +                            { +                                LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; + +                                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                                auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); +                                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                                { +                                    LLMeshHeader& header = header_it->second; +                                    // for safety just mark everything as missing +                                    header.mSkinInCache = false; +                                    header.mPhysicsConvexInCache = false; +                                    header.mPhysicsMeshInCache = false; +                                    for (S32 i = 0; i < LLModel::NUM_LODS; ++i) +                                    { +                                        header.mLodInCache[i] = false; +                                    } +                                    header_size = header.mHeaderSize; +                                    header_flags = header.getFlags(); +                                } +                            } + +                            if (header_size > 0) +                            { +                                LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); +                                if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE) +                                { +                                    write_preamble(file, header_size, header_flags); +                                } +                            } + +                            { +                                LLMutexLock lock(gMeshRepo.mThread->mMutex); +                                UUIDBasedRequest req(mesh_id); +                                gMeshRepo.mThread->mSkinRequests.push_back(req); +                            } +                        } +                        delete[] buffer; +                    }); +                    if (posted) +                    { +                        // lambda owns buffer +                        return true; +                    } +                    else if (skinInfoReceived(mesh_id, buffer, size))                      {                          delete[] buffer;                          return true;                      }                  } -                  delete[] buffer;              } @@ -1420,26 +1567,21 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)                                         << LL_ENDL;                      ret = false;                  } -                else if(can_retry) +                else                  {                      handler->mHttpHandle = handle;                      mHttpRequestSet.insert(handler);                  } -                else -                { -                    LLMutexLock locker(mMutex); -                    mSkinUnavailableQ.emplace_back(mesh_id); -                }              }              else              { -                LLMutexLock locker(mMutex); +                LLMutexLock locker(mLoadedMutex);                  mSkinUnavailableQ.emplace_back(mesh_id);              }          }          else          { -            LLMutexLock locker(mMutex); +            LLMutexLock locker(mLoadedMutex);              mSkinUnavailableQ.emplace_back(mesh_id);          }      } @@ -1470,42 +1612,35 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)      }      ++LLMeshRepository::sMeshRequestCount; -    U32 header_size = header_it->second.first; +    const auto& header = header_it->second; +    U32 header_size = header.mHeaderSize;      bool ret = true;      if (header_size > 0)      { -        const auto& header = header_it->second.second;          S32 version = header.mVersion;          S32 offset = header_size + header.mPhysicsConvexOffset;          S32 size = header.mPhysicsConvexSize; +        bool in_cache = header.mPhysicsConvexInCache;          mHeaderMutex->unlock();          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          { -            //check cache for mesh skin info +            // check cache for mesh decomposition +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset+size) +            if (in_cache && file.getSize() >= disk_ofset + size)              { -                U8* buffer = new(std::nothrow) U8[size]; +                U8* buffer = getDiskCacheBuffer(size);                  if (!buffer)                  { -                    LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; - -                    // Not sure what size is reasonable for decomposition -                    // but if 20MB allocation failed, we definetely have issues -                    const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB -                    if (size < MAX_SIZE) -                    { -                        LLAppViewer::instance()->outOfMemorySoftQuit(); -                    } // else ignore failures for anomalously large decompositiions                      return true;                  }                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1519,12 +1654,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)                  { //attempt to parse                      if (decompositionReceived(mesh_id, buffer, size))                      { -                        delete[] buffer;                          return true;                      }                  } - -                delete[] buffer;              }              //reading from cache failed for whatever reason, fetch from sim @@ -1578,41 +1710,36 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)      }      ++LLMeshRepository::sMeshRequestCount; -    U32 header_size = header_it->second.first; +    const auto& header = header_it->second; +    U32 header_size = header.mHeaderSize;      bool ret = true;      if (header_size > 0)      { -        const auto& header = header_it->second.second;          S32 version = header.mVersion;          S32 offset = header_size + header.mPhysicsMeshOffset;          S32 size = header.mPhysicsMeshSize; +        bool in_cache = header.mPhysicsMeshInCache;          mHeaderMutex->unlock(); +        // todo: check header.mHasPhysicsMesh          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          {              //check cache for mesh physics shape info +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset+size) +            if (in_cache && file.getSize() >= disk_ofset +size)              {                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); -                U8* buffer = new(std::nothrow) U8[size]; + +                U8* buffer = getDiskCacheBuffer(size);                  if (!buffer)                  { -                    LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; - -                    // Not sure what size is reasonable for physcis -                    // but if 20MB allocation failed, we definetely have issues -                    const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB -                    if (size < MAX_SIZE) -                    { -                        LLAppViewer::instance()->outOfMemorySoftQuit(); -                    } // else ignore failures for anomalously large data                      return true;                  } +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1626,12 +1753,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)                  { //attempt to parse                      if (physicsShapeReceived(mesh_id, buffer, size) == MESH_OK)                      { -                        delete[] buffer;                          return true;                      }                  } - -                delete[] buffer;              }              //reading from cache failed for whatever reason, fetch from sim @@ -1699,8 +1823,22 @@ void LLMeshRepoThread::decActiveHeaderRequests()      --LLMeshRepoThread::sActiveHeaderRequests;  } +//static +void LLMeshRepoThread::incActiveSkinRequests() +{ +    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    ++LLMeshRepoThread::sActiveSkinRequests; +} + +//static +void LLMeshRepoThread::decActiveSkinRequests() +{ +    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    --LLMeshRepoThread::sActiveSkinRequests; +} +  //return false if failed to get header -bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry) +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)  {      LL_PROFILE_ZONE_SCOPED;      ++LLMeshRepository::sMeshRequestCount; @@ -1714,17 +1852,34 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c          if (size > 0)          {              // *NOTE:  if the header size is ever more than 4KB, this will break -            U8 buffer[MESH_HEADER_SIZE]; -            S32 bytes = llmin(size, MESH_HEADER_SIZE); +            constexpr S32 DISK_MINIMAL_READ = 4096; +            U8 buffer[DISK_MINIMAL_READ * 2]; +            S32 bytes = llmin(size, DISK_MINIMAL_READ);              LLMeshRepository::sCacheBytesRead += bytes;              ++LLMeshRepository::sCacheReads; +              file.read(buffer, bytes); -            if (headerReceived(mesh_params, buffer, bytes) == MESH_OK) + +            U32 version = 0; +            memcpy(&version, buffer, sizeof(U32)); +            if (version == CACHE_PREAMBLE_VERSION)              { -                LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; +                S32 header_size = 0; +                memcpy(&header_size, buffer + sizeof(U32), sizeof(S32)); +                if (header_size + CACHE_PREAMBLE_SIZE > DISK_MINIMAL_READ) +                { +                    bytes = llmin(size , DISK_MINIMAL_READ * 2); +                    file.read(buffer + DISK_MINIMAL_READ, bytes - DISK_MINIMAL_READ); +                } +                U32 flags = 0; +                memcpy(&flags, buffer + 2 * sizeof(U32), sizeof(U32)); +                if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes - CACHE_PREAMBLE_SIZE, flags) == MESH_OK) +                { +                    LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; -                // Found mesh in cache -                return true; +                    // Found mesh in cache +                    return true; +                }              }          }      } @@ -1753,7 +1908,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c                                 << LL_ENDL;              retval = false;          } -        else if (can_retry) +        else          {              handler->mHttpHandle = handle;              mHttpRequestSet.insert(handler); @@ -1764,7 +1919,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c  }  //return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry) +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)  {      LL_PROFILE_ZONE_SCOPED;      if (!mHeaderMutex) @@ -1784,41 +1939,43 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,      ++LLMeshRepository::sMeshRequestCount;      bool retval = true; -    U32 header_size = header_it->second.first; +    const auto& header = header_it->second; +    U32 header_size = header.mHeaderSize;      if (header_size > 0)      { -        const auto& header = header_it->second.second;          S32 version = header.mVersion;          S32 offset = header_size + header.mLodOffset[lod];          S32 size = header.mLodSize[lod]; +        bool in_cache = header.mLodInCache[lod];          mHeaderMutex->unlock();          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          { - +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              //check cache for mesh asset              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset+size) +            if (in_cache && (file.getSize() >= disk_ofset + size))              { -                U8* buffer = new(std::nothrow) U8[size]; +                U8* buffer = new(std::nothrow) U8[size]; // todo, make buffer thread local and read in thread?                  if (!buffer)                  {                      LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;                      // Not sure what size is reasonable for a mesh, -                    // but if 20MB allocation failed, we definetely have issues +                    // but if 30MB allocation failed, we definitely have issues                      const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB                      if (size < MAX_SIZE)                      {                          LLAppViewer::instance()->outOfMemorySoftQuit();                      } // else ignore failures for anomalously large data -                    LLMutexLock lock(mMutex); + +                    LLMutexLock lock(mLoadedMutex);                      mUnavailableQ.push_back(LODRequest(mesh_params, lod));                      return true;                  }                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1829,15 +1986,76 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,                  }                  if (!zero) -                { //attempt to parse -                    if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) +                { +                    //attempt to parse +                    const LLVolumeParams params(mesh_params); +                    bool posted = mMeshThreadPool->getQueue().post( +                        [params, mesh_id, lod, buffer, size] +                        ()                      { +                        if (gMeshRepo.mThread->lodReceived(params, lod, buffer, size) == MESH_OK) +                        { +                            LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL; +                        } +                        else +                        { +                            // either header is faulty or something else overwrote the cache +                            S32 header_size = 0; +                            U32 header_flags = 0; +                            { +                                LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; + +                                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                                auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); +                                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                                { +                                    LLMeshHeader& header = header_it->second; +                                    // for safety just mark everything as missing +                                    header.mSkinInCache = false; +                                    header.mPhysicsConvexInCache = false; +                                    header.mPhysicsMeshInCache = false; +                                    for (S32 i = 0; i < LLModel::NUM_LODS; ++i) +                                    { +                                        header.mLodInCache[i] = false; +                                    } +                                    header_size = header.mHeaderSize; +                                    header_flags = header.getFlags(); +                                } +                            } + +                            if (header_size > 0) +                            { +                                LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); +                                if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE) +                                { +                                    write_preamble(file, header_size, header_flags); +                                } +                            } + +                            { +                                LLMutexLock lock(gMeshRepo.mThread->mMutex); +                                LODRequest req(params, lod); +                                gMeshRepo.mThread->mLODReqQ.push(req); +                                LLMeshRepository::sLODProcessing++; +                            } +                        }                          delete[] buffer; +                    }); +                    if (posted) +                    { +                        // now lambda owns buffer +                        return true; +                    } +                    else if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) +                    { +                        delete[] buffer;                          LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL;                          return true;                      } +                  }                  delete[] buffer; @@ -1861,27 +2079,22 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,                                         << LL_ENDL;                      retval = false;                  } -                else if (can_retry) +                else                  { +                    // we already made a request, store the handle                      handler->mHttpHandle = handle;                      mHttpRequestSet.insert(handler); -                    // *NOTE:  Allowing a re-request, not marking as unavailable.  Is that correct? -                } -                else -                { -                    LLMutexLock lock(mMutex); -                    mUnavailableQ.push_back(LODRequest(mesh_params, lod));                  }              }              else              { -                LLMutexLock lock(mMutex); +                LLMutexLock lock(mLoadedMutex);                  mUnavailableQ.push_back(LODRequest(mesh_params, lod));              }          }          else          { -            LLMutexLock lock(mMutex); +            LLMutexLock lock(mLoadedMutex);              mUnavailableQ.push_back(LODRequest(mesh_params, lod));          }      } @@ -1893,14 +2106,19 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,      return retval;  } -EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) +EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags)  { +    LL_PROFILE_ZONE_SCOPED;      const LLUUID mesh_id = mesh_params.getSculptID();      LLSD header_data;      LLMeshHeader header;      llssize header_size = 0; +    S32 skin_offset = -1; +    S32 skin_size = -1; +    S32 lod_offset[LLModel::NUM_LODS] = { -1 }; +    S32 lod_size[LLModel::NUM_LODS] = { -1 };      if (data_size > 0)      {          llssize dsize = data_size; @@ -1933,7 +2151,40 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes          // make sure there is at least one lod, function returns -1 and marks as 404 otherwise          else if (LLMeshRepository::getActualMeshLOD(header, 0) >= 0)          { -            header_size += stream.tellg(); +            header.mHeaderSize = (S32)stream.tellg(); +            header_size += header.mHeaderSize; +            skin_offset = header.mSkinOffset; +            skin_size = header.mSkinSize; + +            memcpy(lod_offset, header.mLodOffset, sizeof(lod_offset)); +            memcpy(lod_size, header.mLodSize, sizeof(lod_size)); + +            if (flags != 0) +            { +                header.setFromFlags(flags); +            } +            else +            { +                if (header.mSkinSize > 0 && header_size + header.mSkinOffset + header.mSkinSize < data_size) +                { +                    header.mSkinInCache = true; +                } +                if (header.mPhysicsConvexSize > 0 && header_size + header.mPhysicsConvexOffset + header.mPhysicsConvexSize < data_size) +                { +                    header.mPhysicsConvexInCache = true; +                } +                if (header.mPhysicsMeshSize > 0 && header_size + header.mPhysicsMeshOffset + header.mPhysicsMeshSize < data_size) +                { +                    header.mPhysicsMeshInCache = true; +                } +                for (S32 i = 0; i < LLModel::NUM_LODS; ++i) +                { +                    if (lod_size[i] > 0 && header_size + lod_offset[i] + lod_size[i] < data_size) +                    { +                        header.mLodInCache[i] = true; +                    } +                } +            }          }      }      else @@ -1947,35 +2198,84 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes          {              LLMutexLock lock(mHeaderMutex); -            mMeshHeader[mesh_id] = { (U32)header_size, header }; +            mMeshHeader[mesh_id] = header;              LLMeshRepository::sCacheBytesHeaders += (U32)header_size;          }          // immediately request SkinInfo since we'll need it before we can render any LoD if it is present +        if (skin_offset >= 0 && skin_size > 0)          { -            LLMutexLock lock(gMeshRepo.mMeshMutex); +            { +                LLMutexLock lock(gMeshRepo.mMeshMutex); -            if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) +                if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) +                { +                    gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info +                } +            } + +            S32 offset = (S32)header_size + skin_offset; +            bool request_skin = true; +            if (offset + skin_size < data_size) +            { +                request_skin = !skinInfoReceived(mesh_id, data + offset, skin_size); +            } +            if (request_skin)              { -                gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info +                mSkinRequests.push_back(UUIDBasedRequest(mesh_id));              }          } -        fetchMeshSkinInfo(mesh_id); - -        LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. +        std::array<S32, LLModel::NUM_LODS> pending_lods; +        bool has_pending_lods = false; +        { +            LLMutexLock lock(mPendingMutex); // make sure only one thread access mPendingLOD at the same time. +            pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); +            if (iter != mPendingLOD.end()) +            { +                pending_lods = iter->second; +                mPendingLOD.erase(iter); +                has_pending_lods = true; +            } +        }          //check for pending requests -        pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); -        if (iter != mPendingLOD.end()) +        if (has_pending_lods)          { -            for (U32 i = 0; i < iter->second.size(); ++i) +            for (S32 i = 0; i < pending_lods.size(); ++i)              { -                LODRequest req(mesh_params, iter->second[i]); -                mLODReqQ.push(req); -                LLMeshRepository::sLODProcessing++; +                if (pending_lods[i] > 1) +                { +                    // mLoadingMeshes should be protecting from dupplciates, but looks +                    // like this is possible if object rezzes, unregisterMesh, then +                    // rezzes again before first request completes. +                    // mLoadingMeshes might need to change a bit to not rerequest if +                    // mesh is already pending. +                    // +                    // Todo: Improve mLoadingMeshes and once done turn this into an assert. +                    // Low priority since such situation should be relatively rare +                    LL_INFOS(LOG_MESH) << "Multiple dupplicate requests for mesd ID:  " << mesh_id << " LOD: " << i +                        << LL_ENDL; +                } +                if (pending_lods[i] > 0 && lod_size[i] > 0) +                { +                    // try to load from data we just received +                    bool request_lod = true; +                    S32 offset = (S32)header_size + lod_offset[i]; +                    if (offset + lod_size[i] <= data_size) +                    { +                        // initial request is 4096 bytes, it's big enough to fit this lod +                        request_lod = lodReceived(mesh_params, i, data + offset, lod_size[i]) != MESH_OK; +                    } +                    if (request_lod) +                    { +                        LLMutexLock lock(mMutex); +                        LODRequest req(mesh_params, i); +                        mLODReqQ.push(req); +                        LLMeshRepository::sLODProcessing++; +                    } +                }              } -            mPendingLOD.erase(iter);          }      } @@ -1995,8 +2295,16 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p          if (volume->getNumFaces() > 0)          {              // if we have a valid SkinInfo, cache per-joint bounding boxes for this LOD -            LLMeshSkinInfo* skin_info = mSkinMap[mesh_params.getSculptID()]; -            if (skin_info && isAgentAvatarValid()) +            LLPointer<LLMeshSkinInfo> skin_info = nullptr; +            { +                LLMutexLock lock(mSkinMapMutex); +                skin_map::iterator iter = mSkinMap.find(mesh_params.getSculptID()); +                if (iter != mSkinMap.end()) +                { +                    skin_info = iter->second; +                } +            } +            if (skin_info.notNull() && isAgentAvatarValid())              {                  for (S32 i = 0; i < volume->getNumFaces(); ++i)                  { @@ -2008,7 +2316,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p              LoadedMesh mesh(volume, mesh_params, lod);              { -                LLMutexLock lock(mMutex); +                LLMutexLock lock(mLoadedMutex);                  mLoadedQ.push_back(mesh);                  // LLPointer is not thread safe, since we added this pointer into                  // threaded list, make sure counter gets decreased inside mutex lock @@ -2026,6 +2334,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p  bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLSD skin;      if (data_size > 0) @@ -2060,12 +2369,15 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat          // copy the skin info for the background thread so we can use it          // to calculate per-joint bounding boxes when volumes are loaded -        mSkinMap[mesh_id] = new LLMeshSkinInfo(*info); +        { +            LLMutexLock lock(mSkinMapMutex); +            mSkinMap[mesh_id] = new LLMeshSkinInfo(*info); +        }          {              // Move the LLPointer in to the skin info queue to avoid reference              // count modification after we leave the lock -            LLMutexLock lock(mMutex); +            LLMutexLock lock(mLoadedMutex);              mSkinInfoQ.emplace_back(std::move(info));          }      } @@ -2075,6 +2387,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat  bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLSD decomp;      if (data_size > 0) @@ -2101,7 +2414,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3          LLModel::Decomposition* d = new LLModel::Decomposition(decomp);          d->mMeshID = mesh_id;          { -            LLMutexLock lock(mMutex); +            LLMutexLock lock(mLoadedMutex);              mDecompositionQ.push_back(d);          }      } @@ -2111,6 +2424,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3  EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLSD physics_shape;      LLModel::Decomposition* d = new LLModel::Decomposition(); @@ -2150,7 +2464,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_      }      { -        LLMutexLock lock(mMutex); +        LLMutexLock lock(mLoadedMutex);          mDecompositionQ.push_back(d);      }      return MESH_OK; @@ -2202,7 +2516,6 @@ LLMeshUploadThread::~LLMeshUploadThread()      mHttpRequest = NULL;      delete mMutex;      mMutex = NULL; -  }  LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread) @@ -2943,15 +3256,17 @@ void LLMeshRepoThread::notifyLoadedMeshes()          return;      } +    LL_PROFILE_ZONE_SCOPED; +      if (!mLoadedQ.empty())      {          std::deque<LoadedMesh> loaded_queue; -        mMutex->lock(); +        mLoadedMutex->lock();          if (!mLoadedQ.empty())          {              loaded_queue.swap(mLoadedQ); -            mMutex->unlock(); +            mLoadedMutex->unlock();              update_metrics = true; @@ -2960,40 +3275,47 @@ void LLMeshRepoThread::notifyLoadedMeshes()              {                  if (mesh.mVolume->getNumVolumeFaces() > 0)                  { -                    gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); +                    gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume, mesh.mLOD);                  }                  else                  { -                    gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, -                        LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); +                    gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, mesh.mLOD, LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));                  }              }          } +        else +        { +            mLoadedMutex->unlock(); +        }      }      if (!mUnavailableQ.empty())      {          std::deque<LODRequest> unavil_queue; -        mMutex->lock(); +        mLoadedMutex->lock();          if (!mUnavailableQ.empty())          {              unavil_queue.swap(mUnavailableQ); -            mMutex->unlock(); +            mLoadedMutex->unlock();              update_metrics = true;              // Process the elements free of the lock              for (const auto& req : unavil_queue)              { -                gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); +                gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD, req.mLOD);              }          } +        else +        { +            mLoadedMutex->unlock(); +        }      }      if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty())      { -        if (mMutex->trylock()) +        if (mLoadedMutex->trylock())          {              std::deque<LLPointer<LLMeshSkinInfo>> skin_info_q;              std::deque<UUIDBasedRequest> skin_info_unavail_q; @@ -3014,7 +3336,7 @@ void LLMeshRepoThread::notifyLoadedMeshes()                  decomp_q.swap(mDecompositionQ);              } -            mMutex->unlock(); +            mLoadedMutex->unlock();              // Process the elements free of the lock              while (! skin_info_q.empty()) @@ -3051,9 +3373,11 @@ S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo      if (iter != mMeshHeader.end())      { -        auto& header = iter->second.second; - -        return LLMeshRepository::getActualMeshLOD(header, lod); +        auto& header = iter->second; +        if (header.mHeaderSize > 0) +        { +            return LLMeshRepository::getActualMeshLOD(header, lod); +        }      }      return lod; @@ -3220,7 +3544,10 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo          processData(body, body_offset, data, static_cast<S32>(data_size) - body_offset); -        delete [] data; +        if (mHasDataOwnership) +        { +            delete [] data; +        }      }      // Release handler @@ -3253,7 +3580,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)                         << LL_ENDL;      // Can't get the header so none of the LODs will be available -    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);      for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)      {          gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3263,6 +3590,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)  void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,                                        U8 * data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLUUID mesh_id = mMeshParams.getSculptID();      bool success = (!MESH_HEADER_PROCESS_FAILED)          && ((data != NULL) == (data_size > 0)); // if we have data but no size or have size but no data, something is wrong; @@ -3282,7 +3610,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b                             << LL_ENDL;          // Can't get the header so none of the LODs will be available -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)          {              gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3298,8 +3626,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b          LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id);          if (iter != gMeshRepo.mThread->mMeshHeader.end())          { -            header_bytes = (S32)iter->second.first; -            header = iter->second.second; +            header = iter->second; +            header_bytes = header.mHeaderSize;          }          if (header_bytes > 0 @@ -3324,7 +3652,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b              // LLSD is smart and can work like smart pointer, is not thread safe.              gMeshRepo.mThread->mHeaderMutex->unlock(); -            S32 bytes = lod_bytes + header_bytes; +            S32 bytes = lod_bytes + header_bytes + CACHE_PREAMBLE_SIZE;              // It's possible for the remote asset to have more data than is needed for the local cache @@ -3337,6 +3665,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b                  LLMeshRepository::sCacheBytesWritten += data_size;                  ++LLMeshRepository::sCacheWrites; +                // write preamble +                U32 flags = header.getFlags(); +                write_preamble(file, header_bytes, flags); + +                // write header                  file.write(data, data_size);                  S32 remaining = bytes - file.tell(); @@ -3359,7 +3692,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b              gMeshRepo.mThread->mHeaderMutex->unlock();              // headerReceived() parsed header, but header's data is invalid so none of the LODs will be available -            LLMutexLock lock(gMeshRepo.mThread->mMutex); +            LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);              for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)              {                  gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3388,9 +3721,64 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)                         << " (" << status.toTerseString() << ").  Not retrying."                         << LL_ENDL; -    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);      gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));  } +void LLMeshLODHandler::processLod(U8* data, S32 data_size) +{ +    EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); +    if (result == MESH_OK) +    { +        // good fetch from sim, write to cache +        LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE; +        S32 size = mRequestedBytes; + +        if (file.getSize() >= offset + size) +        { +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mLodInCache[mLOD]) +                    { +                        header.mLodInCache[mLOD] = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0); +            file.write(data, size); +            LLMeshRepository::sCacheBytesWritten += size; +            ++LLMeshRepository::sCacheWrites; +        } +    } +    else +    { +        LL_WARNS(LOG_MESH) << "Error during mesh LOD processing.  ID:  " << mMeshParams.getSculptID() +            << ", Reason: " << result +            << " LOD: " << mLOD +            << " Data size: " << data_size +            << " Not retrying." +            << LL_ENDL; +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); +        gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); +    } +}  void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,                                     U8 * data, S32 data_size) @@ -3399,33 +3787,26 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body      if ((!MESH_LOD_PROCESS_FAILED)          && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong      { -        EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); -        if (result == MESH_OK) +        LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this(); +        bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post( +            [shrd_handler, data, data_size] +            ()          { -            // good fetch from sim, write to cache -            LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - -            S32 offset = mOffset; -            S32 size = mRequestedBytes; +            LLMeshLODHandler* handler = (LLMeshLODHandler * )shrd_handler.get(); +            handler->processLod(data, data_size); +            delete[] data; +        }); -            if (file.getSize() >= offset+size) -            { -                file.seek(offset); -                file.write(data, size); -                LLMeshRepository::sCacheBytesWritten += size; -                ++LLMeshRepository::sCacheWrites; -            } +        if (posted) +        { +            // ownership of data was passed to the lambda +            mHasDataOwnership = false;          }          else          { -            LL_WARNS(LOG_MESH) << "Error during mesh LOD processing.  ID:  " << mMeshParams.getSculptID() -                               << ", Reason: " << result -                               << " LOD: " << mLOD -                               << " Data size: " << data_size -                               << " Not retrying." -                               << LL_ENDL; -            LLMutexLock lock(gMeshRepo.mThread->mMutex); -            gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); +            // mesh thread dies later than event queue, so this is normal +            LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL; +            processLod(data, data_size);          }      }      else @@ -3435,7 +3816,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body                             << " LOD: " << mLOD                             << " Data size: " << data_size                             << LL_ENDL; -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));      }  } @@ -3446,6 +3827,7 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()      {          LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL;      } +    LLMeshRepoThread::decActiveSkinRequests();  }  void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) @@ -3454,38 +3836,98 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)                         << ", Reason:  " << status.toString()                         << " (" << status.toTerseString() << ").  Not retrying."                         << LL_ENDL; -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);  } -void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, -                                        U8 * data, S32 data_size) +void LLMeshSkinInfoHandler::processSkin(U8* data, S32 data_size)  { -    LL_PROFILE_ZONE_SCOPED; -    if ((!MESH_SKIN_INFO_PROCESS_FAILED) -        && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong -        && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) +    if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))      {          // good fetch from sim, write to cache          LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); -        S32 offset = mOffset; +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE;          S32 size = mRequestedBytes; -        if (file.getSize() >= offset+size) +        if (file.getSize() >= offset + size)          {              LLMeshRepository::sCacheBytesWritten += size;              ++LLMeshRepository::sCacheWrites; -            file.seek(offset); + +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mSkinInCache) +                    { +                        header.mSkinInCache = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0);              file.write(data, size);          }      }      else      {          LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID +            << ", Unknown reason.  Not retrying." +            << LL_ENDL; +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); +        gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); +    } +} + +void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, +                                        U8 * data, S32 data_size) +{ +    LL_PROFILE_ZONE_SCOPED; +    if ((!MESH_SKIN_INFO_PROCESS_FAILED) +        && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong +    { +        LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this(); +        bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post( +            [shrd_handler, data, data_size] +            () +        { +            LLMeshSkinInfoHandler* handler = (LLMeshSkinInfoHandler*)shrd_handler.get(); +            handler->processSkin(data, data_size); +            delete[] data; +        }); + +        if (posted) +        { +            // ownership of data was passed to the lambda +            mHasDataOwnership = false; +        } +        else +        { +            // mesh thread dies later than event queue, so this is normal +            LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL; +            processSkin(data, data_size); +        } +    } +    else +    { +        LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID                             << ", Unknown reason.  Not retrying."                             << LL_ENDL; -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);      }  } @@ -3519,14 +3961,39 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S          // good fetch from sim, write to cache          LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); -        S32 offset = mOffset; +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE;          S32 size = mRequestedBytes;          if (file.getSize() >= offset+size)          {              LLMeshRepository::sCacheBytesWritten += size;              ++LLMeshRepository::sCacheWrites; -            file.seek(offset); + +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mPhysicsConvexInCache) +                    { +                        header.mPhysicsConvexInCache = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0);              file.write(data, size);          }      } @@ -3567,14 +4034,39 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3          // good fetch from sim, write to cache for caching          LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); -        S32 offset = mOffset; +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE;          S32 size = mRequestedBytes;          if (file.getSize() >= offset+size)          {              LLMeshRepository::sCacheBytesWritten += size;              ++LLMeshRepository::sCacheWrites; -            file.seek(offset); + +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mPhysicsMeshInCache) +                    { +                        header.mPhysicsMeshInCache = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0);              file.write(data, size);          }      } @@ -3636,6 +4128,7 @@ void LLMeshRepository::shutdown()      }      mThread->mSignal->broadcast(); +    mThread->mMeshThreadPool->close();      while (!mThread->isStopped())      { @@ -3710,24 +4203,24 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj)      }  } -S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) +S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 new_lod, S32 last_lod)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);      // Manage time-to-load metrics for mesh download operations.      metricsProgress(1); -    if (detail < 0 || detail >= LLVolumeLODGroup::NUM_LODS) +    if (new_lod < 0 || new_lod >= LLVolumeLODGroup::NUM_LODS)      { -        return detail; +        return new_lod;      }      {          LLMutexLock lock(mMeshMutex);          //add volume to list of loading meshes          const auto& mesh_id = mesh_params.getSculptID(); -        mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id); -        if (iter != mLoadingMeshes[detail].end()) +        mesh_load_map::iterator iter = mLoadingMeshes[new_lod].find(mesh_id); +        if (iter != mLoadingMeshes[new_lod].end())          { //request pending for this mesh, append volume id to list              auto it = std::find(iter->second.begin(), iter->second.end(), vobj);              if (it == iter->second.end()) { @@ -3737,8 +4230,8 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para          else          {              //first request for this mesh -            mLoadingMeshes[detail][mesh_id].push_back(vobj); -            mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); +            mLoadingMeshes[new_lod][mesh_id].push_back(vobj); +            mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, new_lod));              LLMeshRepository::sLODPending++;          }      } @@ -3767,7 +4260,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para              }              //next, see what the next lowest LOD available might be -            for (S32 i = detail-1; i >= 0; --i) +            for (S32 i = new_lod -1; i >= 0; --i)              {                  LLVolume* lod = group->refLOD(i);                  if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) @@ -3780,7 +4273,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para              }              //no lower LOD is a available, is a higher lod available? -            for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i) +            for (S32 i = new_lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i)              {                  LLVolume* lod = group->refLOD(i);                  if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) @@ -3794,7 +4287,51 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para          }      } -    return detail; +    return new_lod; +} + +F32 calculate_score(LLVOVolume* object) +{ +    if (!object) +    { +        return -1.f; +    } +    LLDrawable* drawable = object->mDrawable; +    if (!drawable) +    { +        return -1; +    } +    if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment()) +    { +        LLVOAvatar* avatar = object->getAvatar(); +        LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr; +        if (avatar && av_drawable) +        { +            // See LLVOVolume::calcLOD() +            F32 radius; +            if (avatar->isControlAvatar()) +            { +                const LLVector3* box = avatar->getLastAnimExtents(); +                LLVector3 diag = box[1] - box[0]; +                radius = diag.magVec() * 0.5f; +            } +            else +            { +                // Volume in a rigged mesh attached to a regular avatar. +                const LLVector3* box = avatar->getLastAnimExtents(); +                LLVector3 diag = box[1] - box[0]; +                radius = diag.magVec(); + +                if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData()) +                { +                    // slightly deprioritize avatars that are still receiving data +                    radius *= 0.9f; +                } +            } +            return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); +        } +    } +    return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f);  }  void LLMeshRepository::notifyLoadedMeshes() @@ -3918,6 +4455,7 @@ void LLMeshRepository::notifyLoadedMeshes()              // erase from background thread              mThread->mWorkQueue.post([=, this]()                  { +                    LLMutexLock(mThread->mSkinMapMutex);                      mThread->mSkinMap.erase(id);                  });          } @@ -3974,8 +4512,12 @@ void LLMeshRepository::notifyLoadedMeshes()              mUploadErrorQ.pop();          } -        S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests; -        if (active_count < LLMeshRepoThread::sRequestLowWater) +        // mPendingRequests go into queues, queues go into active http requests. +        // Checking sRequestHighWater to keep queues at least somewhat populated +        // for faster transition into http +        S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests + LLMeshRepoThread::sActiveSkinRequests; +        active_count += (S32)(mThread->mLODReqQ.size() + mThread->mHeaderReqQ.size() + mThread->mSkinInfoQ.size()); +        if (active_count < LLMeshRepoThread::sRequestHighWater)          {              S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; @@ -3996,50 +4538,69 @@ void LLMeshRepository::notifyLoadedMeshes()                          F32 max_score = 0.f;                          for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)                          { -                            LLVOVolume* object = *obj_iter; -                            if (object) +                            F32 cur_score = calculate_score(*obj_iter); +                            if (cur_score >= 0.f)                              { -                                LLDrawable* drawable = object->mDrawable; -                                if (drawable) -                                { -                                    F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); -                                    max_score = llmax(max_score, cur_score); -                                } +                                max_score = llmax(max_score, cur_score);                              }                          }                          score_map[iter->first] = max_score;                      }                  } +                for (mesh_load_map::iterator iter = mLoadingSkins.begin(); iter != mLoadingSkins.end(); ++iter) +                { +                    F32 max_score = 0.f; +                    for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) +                    { +                        F32 cur_score = calculate_score(*obj_iter); +                        if (cur_score >= 0.f) +                        { +                            max_score = llmax(max_score, cur_score); +                        } +                    } + +                    score_map[iter->first] = max_score; +                }                  //set "score" for pending requests -                for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) +                for (std::unique_ptr<PendingRequestBase>& req_p : mPendingRequests)                  { -                    iter->mScore = score_map[iter->mMeshParams.getSculptID()]; +                    req_p->setScore(score_map[req_p->getId()]);                  }                  //sort by "score"                  std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, -                                  mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); +                                  mPendingRequests.end(), PendingRequestBase::CompareScoreGreater());              } -              while (!mPendingRequests.empty() && push_count > 0)              { -                LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); -                mThread->loadMeshLOD(request.mMeshParams, request.mLOD); +                std::unique_ptr<PendingRequestBase>& req_p = mPendingRequests.front(); +                switch (req_p->getRequestType()) +                { +                case MESH_REQUEST_LOD: +                    { +                        PendingRequestLOD* lod = (PendingRequestLOD*)req_p.get(); +                        mThread->loadMeshLOD(lod->mMeshParams, lod->mLOD); +                        LLMeshRepository::sLODPending--; +                        break; +                    } +                case MESH_REQUEST_SKIN: +                    { +                        PendingRequestUUID* skin = (PendingRequestUUID*)req_p.get(); +                        mThread->loadMeshSkinInfo(skin->getId()); +                        break; +                    } + +                default: +                    LL_ERRS() << "Unknown request type in LLMeshRepository::notifyLoadedMeshes" << LL_ENDL; +                    break; +                }                  mPendingRequests.erase(mPendingRequests.begin()); -                LLMeshRepository::sLODPending--;                  push_count--;              }          } -        //send skin info requests -        while (!mPendingSkinRequests.empty()) -        { -            mThread->loadMeshSkinInfo(mPendingSkinRequests.front()); -            mPendingSkinRequests.pop(); -        } -          //send decomposition requests          while (!mPendingDecompositionRequests.empty())          { @@ -4116,15 +4677,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom      }  } -void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) +void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod)  { //called from main thread -    S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());      //get list of objects waiting to be notified this mesh is loaded      const auto& mesh_id = mesh_params.getSculptID(); -    mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_id); +    mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id); -    if (volume && obj_iter != mLoadingMeshes[detail].end()) +    if (volume && obj_iter != mLoadingMeshes[lod].end())      {          //make sure target volume is still valid          if (volume->getNumVolumeFaces() <= 0) @@ -4134,6 +4694,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol          }          { //update system volume +            S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());              LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);              if (sys_volume)              { @@ -4157,22 +4718,22 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol              }          } -        mLoadingMeshes[detail].erase(obj_iter); +        mLoadingMeshes[lod].erase(obj_iter);          LLViewerStatsRecorder::instance().meshLoaded();      }  } -void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) +void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod)  { //called from main thread      //get list of objects waiting to be notified this mesh is loaded      const auto& mesh_id = mesh_params.getSculptID(); -    mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id); -    if (obj_iter != mLoadingMeshes[lod].end()) +    mesh_load_map::iterator obj_iter = mLoadingMeshes[request_lod].find(mesh_id); +    if (obj_iter != mLoadingMeshes[request_lod].end())      { -        F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); +        F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(volume_lod); -        LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, lod); +        LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, volume_lod);          if (sys_volume)          {              sys_volume->setMeshAssetUnavaliable(true); @@ -4189,12 +4750,12 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,                      obj_volume->getDetail() == detail &&                      obj_volume->getParams() == mesh_params)                  { //should force volume to find most appropriate LOD -                    vobj->setVolume(obj_volume->getParams(), lod); +                    vobj->setVolume(obj_volume->getParams(), volume_lod);                  }              }          } -        mLoadingMeshes[lod].erase(obj_iter); +        mLoadingMeshes[request_lod].erase(obj_iter);      }  } @@ -4231,7 +4792,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV              {                  //first request for this mesh                  mLoadingSkins[mesh_id].push_back(requesting_obj); -                mPendingSkinRequests.push(mesh_id); +                mPendingRequests.emplace_back(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN));              }          }      } @@ -4358,7 +4919,7 @@ bool LLMeshRepository::hasSkinInfo(const LLUUID& mesh_id)      return false;  } -bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) +bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) const  {      if (mesh_id.isNull())      { @@ -4368,13 +4929,13 @@ bool LLMeshRepository::hasHeader(const LLUUID& mesh_id)      return mThread->hasHeader(mesh_id);  } -bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) const  {      LLMutexLock lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); -    if (iter != mMeshHeader.end() && iter->second.first > 0) +    mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id); +    if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)      { -        LLMeshHeader &mesh = iter->second.second; +        const LLMeshHeader &mesh = iter->second;          if (mesh.mPhysicsMeshSize > 0)          {              return true; @@ -4384,13 +4945,13 @@ bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id)      return false;  } -bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) const  {      LLMutexLock lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); -    if (iter != mMeshHeader.end() && iter->second.first > 0) +    mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id); +    if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)      { -        LLMeshHeader& mesh = iter->second.second; +        const LLMeshHeader& mesh = iter->second;          if (mesh.mSkinOffset >= 0              && mesh.mSkinSize > 0)          { @@ -4401,10 +4962,10 @@ bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id)      return false;  } -bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) const  {      LLMutexLock lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); +    mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);      return iter != mMeshHeader.end();  } @@ -4419,16 +4980,16 @@ void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3      mUploadWaitList.push_back(thread);  } -S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) +S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) const  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;      if (mThread && mesh_id.notNull() && LLPrimitive::NO_LOD != lod)      {          LLMutexLock lock(mThread->mHeaderMutex); -        LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); -        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) +        LLMeshRepoThread::mesh_header_map::const_iterator iter = mThread->mMeshHeader.find(mesh_id); +        if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)          { -            const LLMeshHeader& header = iter->second.second; +            const LLMeshHeader& header = iter->second;              if (header.m404)              { @@ -4532,9 +5093,9 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by      {          LLMutexLock lock(mThread->mHeaderMutex);          LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); -        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) +        if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)          { -            result  = getStreamingCostLegacy(iter->second.second, radius, bytes, bytes_visible, lod, unscaled_value); +            result  = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);          }      }      if (result > 0.f) @@ -4726,7 +5287,7 @@ bool LLMeshCostData::init(const LLMeshHeader& header)  } -S32 LLMeshCostData::getSizeByLOD(S32 lod) +S32 LLMeshCostData::getSizeByLOD(S32 lod) const  {      if (llclamp(lod,0,3) != lod)      { @@ -4735,12 +5296,12 @@ S32 LLMeshCostData::getSizeByLOD(S32 lod)      return mSizeByLOD[lod];  } -S32 LLMeshCostData::getSizeTotal() +S32 LLMeshCostData::getSizeTotal() const  {      return mSizeByLOD[0] + mSizeByLOD[1] + mSizeByLOD[2] + mSizeByLOD[3];  } -F32 LLMeshCostData::getEstTrisByLOD(S32 lod) +F32 LLMeshCostData::getEstTrisByLOD(S32 lod) const  {      if (llclamp(lod,0,3) != lod)      { @@ -4749,12 +5310,12 @@ F32 LLMeshCostData::getEstTrisByLOD(S32 lod)      return mEstTrisByLOD[lod];  } -F32 LLMeshCostData::getEstTrisMax() +F32 LLMeshCostData::getEstTrisMax() const  {      return llmax(mEstTrisByLOD[0], mEstTrisByLOD[1], mEstTrisByLOD[2], mEstTrisByLOD[3]);  } -F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) +F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) const  {      F32 max_distance = 512.f; @@ -4798,7 +5359,7 @@ F32 LLMeshCostData::getRadiusWeightedTris(F32 radius)      return weighted_avg;  } -F32 LLMeshCostData::getEstTrisForStreamingCost() +F32 LLMeshCostData::getEstTrisForStreamingCost() const  {      LL_DEBUGS("StreamingCost") << "tris_by_lod: "                                 << mEstTrisByLOD[0] << ", " @@ -4808,7 +5369,7 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()      F32 charged_tris = mEstTrisByLOD[3];      F32 allowed_tris = mEstTrisByLOD[3]; -    const F32 ENFORCE_FLOOR = 64.0f; +    constexpr F32 ENFORCE_FLOOR = 64.0f;      for (S32 i=2; i>=0; i--)      {          // How many tris can we have in this LOD without affecting land impact? @@ -4825,13 +5386,13 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()      return charged_tris;  } -F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) +F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) const  {      static LLCachedControl<U32> mesh_triangle_budget(gSavedSettings, "MeshTriangleBudget");      return getRadiusWeightedTris(radius)/mesh_triangle_budget*15000.f;  } -F32 LLMeshCostData::getTriangleBasedStreamingCost() +F32 LLMeshCostData::getTriangleBasedStreamingCost() const  {      F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001f * getEstTrisForStreamingCost();      return result; @@ -4846,9 +5407,9 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data)      {          LLMutexLock lock(mThread->mHeaderMutex);          LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); -        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) +        if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)          { -            LLMeshHeader& header = iter->second.second; +            LLMeshHeader& header = iter->second;              bool header_invalid = (header.m404                                     || header.mLodSize[0] <= 0 diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 3d25a4dd78..0d9da32e27 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -63,6 +63,16 @@ typedef enum e_mesh_processing_result_enum      MESH_UNKNOWN  } EMeshProcessingResult; +typedef enum e_mesh_request_type_enum +{ +    MESH_REQUEST_HEADER, +    MESH_REQUEST_LOD, +    MESH_REQUEST_SKIN, +    MESH_REQUEST_DECOMPOSITION, +    MESH_REQUEST_PHYSICS, +    MESH_REQUEST_UKNOWN +} EMeshRequestType; +  class LLMeshUploadData  {  public: @@ -183,7 +193,8 @@ public:  class RequestStats  {  public: -    RequestStats() : mRetries(0) {}; + +    RequestStats() :mRetries(0) {};      void updateTime();      bool canRetry() const; @@ -195,6 +206,67 @@ private:      LLFrameTimer mTimer;  }; + +class PendingRequestBase +{ +public: +    struct CompareScoreGreater +    { +        bool operator()(const std::unique_ptr<PendingRequestBase>& lhs, const std::unique_ptr<PendingRequestBase>& rhs) +        { +            return lhs->mScore > rhs->mScore; // greatest = first +        } +    }; + +    PendingRequestBase() : mScore(0.f) {}; +    virtual ~PendingRequestBase() {} + +    bool operator<(const PendingRequestBase& rhs) const +    { +        return mId < rhs.mId; +    } + +    void setScore(F32 score) { mScore = score; } +    F32 getScore() const { return mScore; } +    LLUUID getId() const { return mId; } +    virtual EMeshRequestType getRequestType() const = 0; + +protected: +    F32 mScore; +    LLUUID mId; +}; + +class PendingRequestLOD : public PendingRequestBase +{ +public: +    LLVolumeParams  mMeshParams; +    S32 mLOD; + +    PendingRequestLOD(const LLVolumeParams& mesh_params, S32 lod) +        : PendingRequestBase(), mMeshParams(mesh_params), mLOD(lod) +    { +        mId = mMeshParams.getSculptID(); +    } + +    EMeshRequestType getRequestType() const override { return MESH_REQUEST_LOD; } +}; + +class PendingRequestUUID : public PendingRequestBase +{ +public: + +    PendingRequestUUID(const LLUUID& id, EMeshRequestType type) +        : PendingRequestBase(), mRequestType(type) +    { +        mId = id; +    } + +    EMeshRequestType getRequestType() const override { return mRequestType; } + +private: +    EMeshRequestType mRequestType; +}; +  class LLMeshHeader  {  public: @@ -235,19 +307,67 @@ public:          m404 = header.has("404");      } +private: + +    enum EDiskCacheFlags { +        FLAG_SKIN = 1 << LLModel::NUM_LODS, +        FLAG_PHYSCONVEX = 1 << (LLModel::NUM_LODS + 1), +        FLAG_PHYSMESH = 1 << (LLModel::NUM_LODS + 2), +    }; +public: +    U32 getFlags() +    { +        U32 flags = 0; +        for (U32 i = 0; i < LLModel::NUM_LODS; i++) +        { +            if (mLodInCache[i]) +            { +                flags |= 1 << i; +            } +        } +        if (mSkinInCache) +        { +            flags |= FLAG_SKIN; +        } +        if (mPhysicsConvexInCache) +        { +            flags |= FLAG_PHYSCONVEX; +        } +        if (mPhysicsMeshInCache) +        { +            flags |= FLAG_PHYSMESH; +        } +        return flags; +    } + +    void setFromFlags(U32 flags) +    { +        for (U32 i = 0; i < LLModel::NUM_LODS; i++) +        { +            mLodInCache[i] = (flags & (1 << i)) != 0; +        } +        mSkinInCache          = (flags & FLAG_SKIN) != 0; +        mPhysicsConvexInCache = (flags & FLAG_PHYSCONVEX) != 0; +        mPhysicsMeshInCache   = (flags & FLAG_PHYSMESH) != 0; +    }      S32 mVersion = -1;      S32 mSkinOffset = -1;      S32 mSkinSize = -1; +    bool mSkinInCache = false;      S32 mPhysicsConvexOffset = -1;      S32 mPhysicsConvexSize = -1; +    bool mPhysicsConvexInCache = false;      S32 mPhysicsMeshOffset = -1;      S32 mPhysicsMeshSize = -1; +    bool mPhysicsMeshInCache = false; -    S32 mLodOffset[4] = { -1 }; -    S32 mLodSize[4] = { -1 }; +    S32 mLodOffset[LLModel::NUM_LODS] = { -1 }; +    S32 mLodSize[LLModel::NUM_LODS] = { -1 }; +    bool mLodInCache[LLModel::NUM_LODS] = { false }; +    S32 mHeaderSize = -1;      bool m404 = false;  }; @@ -258,6 +378,7 @@ public:      static std::atomic<S32> sActiveHeaderRequests;      static std::atomic<S32> sActiveLODRequests; +    static std::atomic<S32> sActiveSkinRequests;      static U32 sMaxConcurrentRequests;      static S32 sRequestLowWater;      static S32 sRequestHighWater; @@ -265,10 +386,13 @@ public:      LLMutex*    mMutex;      LLMutex*    mHeaderMutex; +    LLMutex*    mLoadedMutex; +    LLMutex*    mPendingMutex; +    LLMutex*    mSkinMapMutex;      LLCondition* mSignal;      //map of known mesh headers -    typedef boost::unordered_map<LLUUID, std::pair<U32, LLMeshHeader>> mesh_header_map; // pair is header_size and data +    typedef boost::unordered_map<LLUUID, LLMeshHeader> mesh_header_map; // pair is header_size and data      mesh_header_map mMeshHeader;      class HeaderRequest : public RequestStats @@ -292,19 +416,10 @@ public:      public:          LLVolumeParams  mMeshParams;          S32 mLOD; -        F32 mScore;          LODRequest(const LLVolumeParams&  mesh_params, S32 lod) -            : RequestStats(), mMeshParams(mesh_params), mLOD(lod), mScore(0.f) -        { -        } -    }; - -    struct CompareScoreGreater -    { -        bool operator()(const LODRequest& lhs, const LODRequest& rhs) +            : RequestStats(), mMeshParams(mesh_params), mLOD(lod)          { -            return lhs.mScore > rhs.mScore; // greatest = first          }      }; @@ -369,7 +484,7 @@ public:      std::deque<LoadedMesh> mLoadedQ;      //map of pending header requests and currently desired LODs -    typedef std::unordered_map<LLUUID, std::vector<S32> > pending_lod_map; +    typedef std::unordered_map<LLUUID, std::array<S32, LLModel::NUM_LODS> > pending_lod_map;      pending_lod_map mPendingLOD;      // map of mesh ID to skin info (mirrors LLMeshRepository::mSkinMap) @@ -379,6 +494,8 @@ public:      // workqueue for processing generic requests      LL::WorkQueue mWorkQueue; +    // lods have their own thread due to costly cacheOptimize() calls +    std::unique_ptr<LL::ThreadPool> mMeshThreadPool;      // llcorehttp library interface objects.      LLCore::HttpStatus                  mHttpStatus; @@ -402,16 +519,16 @@ public:      void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);      void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); -    bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true); -    bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true); -    EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); +    bool fetchMeshHeader(const LLVolumeParams& mesh_params); +    bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); +    EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags = 0);      EMeshProcessingResult lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);      bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);      bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size);      EMeshProcessingResult physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size); -    bool hasPhysicsShapeInHeader(const LLUUID& mesh_id); -    bool hasSkinInfoInHeader(const LLUUID& mesh_id); -    bool hasHeader(const LLUUID& mesh_id); +    bool hasPhysicsShapeInHeader(const LLUUID& mesh_id) const; +    bool hasSkinInfoInHeader(const LLUUID& mesh_id) const; +    bool hasHeader(const LLUUID& mesh_id) const;      void notifyLoadedMeshes();      S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); @@ -422,7 +539,7 @@ public:      //send request for skin info, returns true if header info exists      //  (should hold onto mesh_id and try again later if header info does not exist) -    bool fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry = true); +    bool fetchMeshSkinInfo(const LLUUID& mesh_id);      //send request for decomposition, returns true if header info exists      //  (should hold onto mesh_id and try again later if header info does not exist) @@ -436,6 +553,8 @@ public:      static void decActiveLODRequests();      static void incActiveHeaderRequests();      static void decActiveHeaderRequests(); +    static void incActiveSkinRequests(); +    static void decActiveSkinRequests();      // Set the caps strings and preferred version for constructing      // mesh fetch URLs. @@ -456,6 +575,14 @@ private:      LLCore::HttpHandle getByteRange(const std::string & url,                                      size_t offset, size_t len,                                      const LLCore::HttpHandler::ptr_t &handler); + +    // Mutex: acquires mPendingMutex, mMutex and mHeaderMutex as needed +    void loadMeshLOD(const LLUUID &mesh_id, const LLVolumeParams& mesh_params, S32 lod); + +    // Threads:  Repo thread only +    U8* getDiskCacheBuffer(S32 size); +    S32 mDiskCacheBufferSize = 0; +    U8* mDiskCacheBuffer = nullptr;  }; @@ -568,35 +695,35 @@ public:      bool init(const LLMeshHeader& header);      // Size for given LOD -    S32 getSizeByLOD(S32 lod); +    S32 getSizeByLOD(S32 lod) const;      // Sum of all LOD sizes. -    S32 getSizeTotal(); +    S32 getSizeTotal() const;      // Estimated triangle counts for the given LOD. -    F32 getEstTrisByLOD(S32 lod); +    F32 getEstTrisByLOD(S32 lod) const;      // Estimated triangle counts for the largest LOD. Typically this      // is also the "high" LOD, but not necessarily. -    F32 getEstTrisMax(); +    F32 getEstTrisMax() const;      // Triangle count as computed by original streaming cost      // formula. Triangles in each LOD are weighted based on how      // frequently they will be seen.      // This was called "unscaled_value" in the original getStreamingCost() functions. -    F32 getRadiusWeightedTris(F32 radius); +    F32 getRadiusWeightedTris(F32 radius) const;      // Triangle count used by triangle-based cost formula. Based on      // triangles in highest LOD plus potentially partial charges for      // lower LODs depending on complexity. -    F32 getEstTrisForStreamingCost(); +    F32 getEstTrisForStreamingCost() const;      // Streaming cost. This should match the server-side calculation      // for the corresponding volume. -    F32 getRadiusBasedStreamingCost(F32 radius); +    F32 getRadiusBasedStreamingCost(F32 radius) const;      // New streaming cost formula, currently only used for animated objects. -    F32 getTriangleBasedStreamingCost(); +    F32 getTriangleBasedStreamingCost() const;  private:      // From the "size" field of the mesh header. LOD 0=lowest, 3=highest. @@ -620,12 +747,12 @@ public:      static U32 sLODPending;      static U32 sLODProcessing;      static U32 sCacheBytesRead; -    static U32 sCacheBytesWritten; +    static std::atomic<U32> sCacheBytesWritten;      static U32 sCacheBytesHeaders;      static U32 sCacheBytesSkins;      static U32 sCacheBytesDecomps;      static U32 sCacheReads; -    static U32 sCacheWrites; +    static std::atomic<U32> sCacheWrites;      static U32 sMaxLockHoldoffs;                // Maximum sequential locking failures      static LLDeadmanTimer sQuiescentTimer;      // Time-to-complete-mesh-downloads after significant events @@ -646,11 +773,11 @@ public:      void unregisterMesh(LLVOVolume* volume);      //mesh management functions -    S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); +    S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 new_lod = 0, S32 last_lod = -1);      void notifyLoadedMeshes(); -    void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume); -    void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod); +    void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod); +    void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod);      void notifySkinInfoReceived(LLMeshSkinInfo* info);      void notifySkinInfoUnavailable(const LLUUID& info);      void notifyDecompositionReceived(LLModel::Decomposition* info); @@ -662,7 +789,7 @@ public:      void fetchPhysicsShape(const LLUUID& mesh_id);      bool hasPhysicsShape(const LLUUID& mesh_id);      bool hasSkinInfo(const LLUUID& mesh_id); -    bool hasHeader(const LLUUID& mesh_id); +    bool hasHeader(const LLUUID& mesh_id) const;      void buildHull(const LLVolumeParams& params, S32 detail);      void buildPhysicsMesh(LLModel::Decomposition& decomp); @@ -676,7 +803,7 @@ public:                       LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()),                       LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>())); -    S32 getMeshSize(const LLUUID& mesh_id, S32 lod); +    S32 getMeshSize(const LLUUID& mesh_id, S32 lod) const;      // Quiescent timer management, main thread only.      static void metricsStart(); @@ -684,7 +811,7 @@ public:      static void metricsProgress(unsigned int count);      static void metricsUpdate(); -    typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map; +    typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map;      mesh_load_map mLoadingMeshes[4];      typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map; @@ -695,15 +822,13 @@ public:      LLMutex*                    mMeshMutex; -    std::vector<LLMeshRepoThread::LODRequest> mPendingRequests; +    typedef std::vector <std::unique_ptr<PendingRequestBase> > pending_requests_vec; +    pending_requests_vec mPendingRequests;      //list of mesh ids awaiting skin info -    typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map; +    typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map;      skin_load_map mLoadingSkins; -    //list of mesh ids that need to send skin info fetch requests -    std::queue<LLUUID> mPendingSkinRequests; -      //list of mesh ids awaiting decompositions      std::unordered_set<LLUUID> mLoadingDecompositions; diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 23f1f8fa5a..cdf7f05ada 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -92,10 +92,18 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type,              from = SYSTEM_FROM;          } -        // Build a new format username or firstname_lastname for legacy names -        // to use it for a history log filename. -        std::string user_name = LLCacheName::buildUsername(session_name); -        LLIMModel::instance().logToFile(user_name, from, from_id, message); +        std::string file_name; +        if (session_type == IM_SESSION_GROUP_START) +        { +            file_name = session_name + LLLogChat::getGroupChatSuffix(); +        } +        else +        { +            // Build a new format username or firstname_lastname for legacy names +            // to use it for a history log filename. +            file_name = LLCacheName::buildUsername(session_name); +        } +        LLIMModel::instance().logToFile(file_name, from, from_id, message);      }      else      { diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 0f5f7aebf8..6e666b8a4b 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -924,8 +924,8 @@ void LLOutfitListBase::onIdleRefreshList()          if (cat)          {              std::string name = cat->getName(); -        updateChangedCategoryName(cat, name); -    } +            updateChangedCategoryName(cat, name); +        }          curent_time = LLTimer::getTotalSeconds();          if (curent_time >= end_time) diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 1faf241aaa..449a670de9 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -266,6 +266,15 @@ void LLPanelClassifiedInfo::processProperties(void* data, EAvatarProcessorType t      }  } +void LLPanelClassifiedInfo::setAvatarId(const LLUUID& avatar_id) +{ +    if (mAvatarId.notNull()) +    { +        LLAvatarPropertiesProcessor::getInstance()->removeObserver(mAvatarId, this); +    } +    mAvatarId = avatar_id; +} +  void LLPanelClassifiedInfo::resetData()  {      setClassifiedName(LLStringUtil::null); diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index 266b9d222a..a429468a52 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -53,7 +53,7 @@ public:      /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); -    void setAvatarId(const LLUUID& avatar_id) { mAvatarId = avatar_id; } +    void setAvatarId(const LLUUID& avatar_id);      LLUUID& getAvatarId() { return mAvatarId; } diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index fc1bf6ca93..3a6a6a5ec3 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -68,6 +68,9 @@ LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p      {          LLScrollbar::Params sbparams;          sbparams.orientation(LLScrollbar::VERTICAL); +        sbparams.doc_size(static_cast<S32>(mTotalEmojis)); +        sbparams.doc_pos(0); +        sbparams.page_size(static_cast<S32>(mVisibleEmojis));          sbparams.change_callback([this](S32 index, LLScrollbar*) { onScrollbarChange(index); });          mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams);          addChild(mScrollbar); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index c080d72580..692bd9cc41 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -420,6 +420,7 @@ bool LLPanelFace::postBuild()      mCtrlColorTransp->setFollowsLeft();      getChildSetCommitCallback(mCheckFullbright, "checkbox fullbright", [&](LLUICtrl*, const LLSD&) { onCommitFullbright(); }); +    getChildSetCommitCallback(mCheckHideWater, "checkbox_hide_water", [&](LLUICtrl*, const LLSD&) { onCommitHideWater(); });      mLabelTexGen = getChild<LLTextBox>("tex gen");      getChildSetCommitCallback(mComboTexGen, "combobox texgen", [&](LLUICtrl*, const LLSD&) { onCommitTexGen(); }); @@ -1024,6 +1025,13 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)          LLSelectedTEMaterial::getNormalID(normmap_id, identical_norm);          LLSelectedTEMaterial::getSpecularID(specmap_id, identical_spec); +        LLColor4 color = LLColor4::white; +        bool identical_color = false; + +        LLSelectedTE::getColor(color, identical_color); +        F32 transparency  = (1.f - color.mV[VALPHA]) * 100.f; +        mExcludeWater = (id == IMG_ALPHA_GRAD) && normmap_id.isNull() && specmap_id.isNull() && (transparency == 0); +          static S32 selected_te = -1;          static LLUUID prev_obj_id;          if ((LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool()) && @@ -1098,12 +1106,26 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)          updateVisibility(objectp); +        // Water exclusion +        { +            mCheckHideWater->setEnabled(editable && !has_pbr_material && !isMediaTexSelected()); +            mCheckHideWater->set(mExcludeWater); +            if (mExcludeWater && !has_pbr_material) +            { +                mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL); +            } +            editable &= !mExcludeWater; + +            // disable controls for water exclusion face after updateVisibility, so the whole panel is not hidden +            mComboMatMedia->setEnabled(editable); +            mRadioMaterialType->setEnabled(editable); +            mRadioPbrType->setEnabled(editable); +            mCheckSyncSettings->setEnabled(editable); +        } +          // Color swatch          mLabelColor->setEnabled(editable); -        LLColor4 color = LLColor4::white; -        bool identical_color = false; -        LLSelectedTE::getColor(color, identical_color);          LLColor4 prev_color = mColorSwatch->get();          mColorSwatch->setOriginal(color);          mColorSwatch->set(color, force_set_values || (prev_color != color) || !editable); @@ -1114,7 +1136,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)          // Color transparency          mLabelColorTransp->setEnabled(editable); -        F32 transparency = (1.f - color.mV[VALPHA]) * 100.f;          mCtrlColorTransp->setValue(editable ? transparency : 0);          mCtrlColorTransp->setEnabled(editable && has_material); @@ -1986,7 +2007,8 @@ void LLPanelFace::updateCopyTexButton()      mMenuClipboardTexture->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify()                                                      && !objectp->isPermanentEnforced() && !objectp->isInventoryPending()                                                      && (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1) -                                                    && LLMaterialEditor::canClipboardObjectsMaterial()); +                                                    && LLMaterialEditor::canClipboardObjectsMaterial() +                                                    && !mExcludeWater);      std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options");      mMenuClipboardTexture->setToolTip(tooltip);  } @@ -3027,6 +3049,37 @@ void LLPanelFace::onCommitFullbright()      sendFullbright();  } +void LLPanelFace::onCommitHideWater() +{ +    if (mCheckHideWater->get()) +    { +        LLHandle<LLPanel> handle = getHandle(); +        LLNotificationsUtil::add("WaterExclusionSurfacesWarning", LLSD(), LLSD(), +            [handle](const LLSD& notification, const LLSD& response) +        { +            if(LLPanelFace* panel = (LLPanelFace*)handle.get()) +            { +                if (LLNotificationsUtil::getSelectedOption(notification, response) == 1) +                { +                    panel->mCheckHideWater->setValue(false); +                    return; +                } +                // apply invisiprim texture and reset related params to set water exclusion surface +                panel->sendBump(0); +                panel->sendShiny(0); +                LLSelectMgr::getInstance()->selectionSetAlphaOnly(1.f); +                LLSelectMgr::getInstance()->selectionSetImage(IMG_ALPHA_GRAD); +                LLSelectedTEMaterial::setDiffuseAlphaMode(panel, LLMaterial::DIFFUSE_ALPHA_MODE_BLEND); +            } +        }); +    } +    else +    { +        // reset texture to default plywood +        LLSelectMgr::getInstance()->selectionSetImage(DEFAULT_OBJECT_TEXTURE); +    } +} +  void LLPanelFace::onCommitGlow()  {      sendGlow(); @@ -4891,6 +4944,26 @@ bool LLPanelFace::isIdenticalPlanarTexgen()      return (identical_texgen && (selected_texgen == LLTextureEntry::TEX_GEN_PLANAR));  } +bool LLPanelFace::isMediaTexSelected() +{ +    LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); +    if (LLViewerObject* objectp = node->getObject()) +    { +        S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); +        for (S32 te = 0; te < num_tes; ++te) +        { +            if (node->isTESelected(te)) +            { +                if (objectp->getTE(te) && objectp->getTE(te)->hasMedia()) +                { +                    return true; +                } +            } +        } +    } +    return false; +} +  void LLPanelFace::LLSelectedTE::getFace(LLFace*& face_to_return, bool& identical_face)  {      struct LLSelectedTEGetFace : public LLSelectedTEGetFunctor<LLFace *> diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index dfd3201533..6e0d34cbd6 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -139,6 +139,8 @@ protected:      void updateMediaSettings();      void updateMediaTitle(); +    bool isMediaTexSelected(); +      void getState();      void sendTexture();            // applies and sends texture @@ -238,6 +240,7 @@ protected:      void onCommitShiny();      void onCommitAlphaMode();      void onCommitFullbright(); +    void onCommitHideWater();      void onCommitGlow();      void onCommitPlanarAlign();      void onCommitRepeatsPerMeter(); @@ -308,6 +311,7 @@ private:      LLRadioGroup* mRadioPbrType { nullptr };      LLCheckBoxCtrl* mCheckFullbright { nullptr }; +    LLCheckBoxCtrl* mCheckHideWater{ nullptr };      LLTextBox* mLabelColorTransp { nullptr };      LLSpinCtrl* mCtrlColorTransp { nullptr }; // transparency = 1 - alpha @@ -555,6 +559,7 @@ private:      LLMenuButton*   mMenuClipboardTexture;      bool mIsAlpha; +    bool mExcludeWater { false };      LLSD            mClipboardParams; diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index a5b4db0580..5bd2a53e7c 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -2030,7 +2030,11 @@ void LLPanelMainInventory::onVisibilityChange( bool new_visibility )          {              menu->setVisible(false);          } -        getActivePanel()->getRootFolder()->finishRenamingItem(); +        LLFolderView* root_folder = mActivePanel ? mActivePanel->getRootFolder() : nullptr; +        if (root_folder) +        { +            root_folder->finishRenamingItem(); +        }      }  } diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 18f8934f9d..d2ead0e134 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -294,7 +294,7 @@ void LLPanelPrimMediaControls::updateShape()      LLViewerMediaImpl* media_impl = getTargetMediaImpl();      LLViewerObject* objectp = getTargetObject(); -    if(!media_impl || gFloaterTools->getVisible()) +    if(!media_impl || (gFloaterTools && gFloaterTools->getVisible()))      {          setVisible(false);          return; diff --git a/indra/newview/llpanelprofileclassifieds.cpp b/indra/newview/llpanelprofileclassifieds.cpp index 62829b0745..3920dd82c8 100644 --- a/indra/newview/llpanelprofileclassifieds.cpp +++ b/indra/newview/llpanelprofileclassifieds.cpp @@ -78,6 +78,13 @@ class LLClassifiedHandler : public LLCommandHandler, public LLAvatarPropertiesOb  public:      // throttle calls from untrusted browsers      LLClassifiedHandler() : LLCommandHandler("classified", UNTRUSTED_THROTTLE) {} +    virtual ~LLClassifiedHandler() +    { +        if (LLAvatarPropertiesProcessor::instanceExists()) +        { +            LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID(), this); +        } +    }      std::set<LLUUID> mClassifiedIds;      std::string mRequestVerb; diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp index 08f3d3af5a..09b8011ce4 100644 --- a/indra/newview/llpanelprofilepicks.cpp +++ b/indra/newview/llpanelprofilepicks.cpp @@ -55,6 +55,8 @@  static LLPanelInjector<LLPanelProfilePicks> t_panel_profile_picks("panel_profile_picks");  static LLPanelInjector<LLPanelProfilePick> t_panel_profile_pick("panel_profile_pick"); +constexpr F32 REQUEST_TIMOUT = 60; +constexpr F32 LOCATION_CACHE_TIMOUT = 900;  class LLPickHandler : public LLCommandHandler  { @@ -306,6 +308,7 @@ void LLPanelProfilePicks::processProperties(void* data, EAvatarProcessorType typ  void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)  { +    LL_DEBUGS("PickInfo") << "Processing picks for avatar " << getAvatarId() << LL_ENDL;      LLUUID selected_id = mPickToSelectOnLoad;      bool has_selection = false;      if (mPickToSelectOnLoad.isNull()) @@ -320,6 +323,25 @@ void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)          }      } +    // Avoid pointlesly requesting parcel data, +    // store previous values +    std::map<LLUUID, std::string> parcelid_location_map; +    std::map<LLUUID, LLUUID> pickid_parcelid_map; + +    for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) +    { +        LLPanelProfilePick* pick_panel = dynamic_cast<LLPanelProfilePick*>(mTabContainer->getPanelByIndex(tab_idx)); +        if (pick_panel && pick_panel->getPickId().notNull()) +        { +            std::string location = pick_panel->getPickLocation(); +            if (!location.empty()) +            { +                parcelid_location_map[pick_panel->getParcelID()] = pick_panel->getPickLocation(); +                pickid_parcelid_map[pick_panel->getPickId()] = pick_panel->getParcelID(); +            } +        } +    } +      mTabContainer->deleteAllTabs();      LLAvatarData::picks_list_t::const_iterator it = avatar_picks->picks_list.begin(); @@ -334,6 +356,15 @@ void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)          pick_panel->setPickName(pick_name);          pick_panel->setAvatarId(getAvatarId()); +        std::map<LLUUID, LLUUID>::const_iterator found_pick = pickid_parcelid_map.find(pick_id); +        if (found_pick != pickid_parcelid_map.end()) +        { +            std::map<LLUUID, std::string>::const_iterator found = parcelid_location_map.find(found_pick->second); +            if (found != parcelid_location_map.end() && !found->second.empty()) +            { +                pick_panel->setPickLocation(found_pick->second, found->second); +            } +        }          mTabContainer->addTabPanel(              LLTabContainer::TabPanelParams().              panel(pick_panel). @@ -353,6 +384,11 @@ void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)          LLPanelProfilePick* pick_panel = LLPanelProfilePick::create();          pick_panel->setAvatarId(getAvatarId()); +        std::map<LLUUID, std::string>::const_iterator found = parcelid_location_map.find(data.parcel_id); +        if (found != parcelid_location_map.end() && !found->second.empty()) +        { +            pick_panel->setPickLocation(data.parcel_id, found->second); +        }          pick_panel->processProperties(&data);          mTabContainer->addTabPanel(              LLTabContainer::TabPanelParams(). @@ -638,9 +674,14 @@ void LLPanelProfilePick::processProperties(void* data, EAvatarProcessorType type  void LLPanelProfilePick::processProperties(const LLPickData* pick_info)  { +    LL_DEBUGS("PickInfo") << "Processing properties for pick " << mPickId << LL_ENDL;      mIsEditing = false;      mPickDescription->setParseHTML(true); -    mParcelId = pick_info->parcel_id; +    if (mParcelId != pick_info->parcel_id) +    { +        mParcelId = pick_info->parcel_id; +        mPickLocationStr.clear(); +    }      setSnapshotId(pick_info->snapshot_id);      if (!getSelfProfile())      { @@ -650,8 +691,11 @@ void LLPanelProfilePick::processProperties(const LLPickData* pick_info)      setPickDesc(pick_info->desc);      setPosGlobal(pick_info->pos_global); -    // Send remote parcel info request to get parcel name and sim (region) name. -    sendParcelInfoRequest(); +    if (mPickLocationStr.empty() || mLastRequestTimer.getElapsedTimeF32() > LOCATION_CACHE_TIMOUT) +    { +        // Send remote parcel info request to get parcel name and sim (region) name. +        sendParcelInfoRequest(); +    }      // *NOTE dzaporozhan      // We want to keep listening to APT_PICK_INFO because user may @@ -691,9 +735,17 @@ void LLPanelProfilePick::setPickDesc(const std::string& desc)      mPickDescription->setValue(desc);  } +void LLPanelProfilePick::setPickLocation(const LLUUID &parcel_id, const std::string& location) +{ +    setParcelID(parcel_id); // resets mPickLocationStr +    setPickLocation(location); +} +  void LLPanelProfilePick::setPickLocation(const std::string& location)  {      getChild<LLUICtrl>("pick_location")->setValue(location); +    mPickLocationStr = location; +    mLastRequestTimer.reset();  }  void LLPanelProfilePick::onClickMap() @@ -790,16 +842,19 @@ std::string LLPanelProfilePick::getLocationNotice()  void LLPanelProfilePick::sendParcelInfoRequest()  { -    if (mParcelId != mRequestedId) +    if (mParcelId != mRequestedId || mLastRequestTimer.getElapsedTimeF32() > REQUEST_TIMOUT)      {          if (mRequestedId.notNull())          {              LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedId, this);          } +        LL_DEBUGS("PickInfo") << "Sending parcel request " << mParcelId << " for pick " << mPickId << LL_ENDL;          LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelId, this);          LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelId);          mRequestedId = mParcelId; +        mLastRequestTimer.reset(); +        mPickLocationStr.clear();      }  } @@ -816,6 +871,20 @@ void LLPanelProfilePick::processParcelInfo(const LLParcelData& parcel_data)      }  } +void LLPanelProfilePick::setParcelID(const LLUUID& parcel_id) +{ +    if (mParcelId != parcel_id) +    { +        mParcelId = parcel_id; +        mPickLocationStr.clear(); +    } +    if (mRequestedId.notNull() && mRequestedId != parcel_id) +    { +        LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedId, this); +        mRequestedId.setNull(); +    } +} +  void LLPanelProfilePick::sendUpdate()  {      LLPickData pick_data; diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h index e3f50f5576..b4d3eb010e 100644 --- a/indra/newview/llpanelprofilepicks.h +++ b/indra/newview/llpanelprofilepicks.h @@ -117,6 +117,8 @@ public:      virtual void setPickName(const std::string& name);      const std::string getPickName(); +    virtual void setPickLocation(const LLUUID& parcel_id, const std::string& location); +    std::string getPickLocation() { return mPickLocationStr; };      void processProperties(void* data, EAvatarProcessorType type) override;      void processProperties(const LLPickData* pick_data); @@ -135,7 +137,8 @@ public:      //This stuff we got from LLRemoteParcelObserver, in the last one we intentionally do nothing      void processParcelInfo(const LLParcelData& parcel_data) override; -    void setParcelID(const LLUUID& parcel_id) override { mParcelId = parcel_id; } +    void setParcelID(const LLUUID& parcel_id) override; +    LLUUID getParcelID() const { return mParcelId; }      void setErrorStatus(S32 status, const std::string& reason) override {};    protected: @@ -230,6 +233,8 @@ protected:      LLUUID mPickId;      LLUUID mRequestedId;      std::string mPickNameStr; +    std::string mPickLocationStr; +    LLTimer mLastRequestTimer;      boost::signals2::connection mRegionCallbackConnection;      boost::signals2::connection mParcelCallbackConnection; diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp index ae5ade5f24..b67721f21c 100644 --- a/indra/newview/llreflectionmapmanager.cpp +++ b/indra/newview/llreflectionmapmanager.cpp @@ -222,13 +222,21 @@ void LLReflectionMapManager::update()          resume();      } +    static LLCachedControl<U32> probe_count(gSavedSettings, "RenderReflectionProbeCount", 256U); +    bool countReset = mReflectionProbeCount != probe_count; + +    if (countReset) +    { +        mResetFade = -0.5f; +    } +      initReflectionMaps();      static LLCachedControl<bool> render_hdr(gSavedSettings, "RenderHDREnabled", true);      if (!mRenderTarget.isComplete())      { -        U32 color_fmt = render_hdr ? GL_RGBA16F : GL_RGBA8; +        U32 color_fmt = render_hdr ? GL_R11F_G11F_B10F : GL_RGB8;          U32 targetRes = mProbeResolution * 4; // super sample          mRenderTarget.allocate(targetRes, targetRes, color_fmt, true);      } @@ -241,7 +249,7 @@ void LLReflectionMapManager::update()          mMipChain.resize(count);          for (U32 i = 0; i < count; ++i)          { -            mMipChain[i].allocate(res, res, render_hdr ? GL_RGB16F : GL_RGB8); +            mMipChain[i].allocate(res, res, render_hdr ? GL_R11F_G11F_B10F : GL_RGB8);              res /= 2;          }      } @@ -336,6 +344,13 @@ void LLReflectionMapManager::update()          }      } +    if (countReset) +    { +        mResetFade = -0.5f; +    } + +    mResetFade = llmin((F32)(mResetFade + gFrameIntervalSeconds), 1.f); +      for (unsigned int i = 0; i < mProbes.size(); ++i)      {          LLReflectionMap* probe = mProbes[i]; @@ -1016,60 +1031,18 @@ void LLReflectionMapManager::updateUniforms()      LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;      LL_PROFILE_GPU_ZONE("rmmu - uniforms") -    // structure for packing uniform buffer object -    // see class3/deferred/reflectionProbeF.glsl -    struct ReflectionProbeData -    { -        // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of -        // the box probe -        LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT]; - -        LLMatrix4 heroBox; - -        // for sphere probes, origin (xyz) and radius (w) of refmaps in clip space -        LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT]; - -        // extra parameters -        //  x - irradiance scale -        //  y - radiance scale -        //  z - fade in -        //  w - znear -        LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT]; - -        LLVector4 heroSphere; - -        // indices used by probe: -        //  [i][0] - cubemap array index for this probe -        //  [i][1] - index into "refNeighbor" for probes that intersect this probe -        //  [i][2] - number of probes  that intersect this probe, or -1 for no neighbors -        //  [i][3] - priority (probe type stored in sign bit - positive for spheres, negative for boxes) -        GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4]; - -        // list of neighbor indices -        GLint refNeighbor[4096]; - -        GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth -        // numbrer of active refmaps -        GLint refmapCount; - -        GLint     heroShape; -        GLint     heroMipCount; -        GLint     heroProbeCount; -    };      mReflectionMaps.resize(mReflectionProbeCount);      getReflectionMaps(mReflectionMaps); -    ReflectionProbeData rpd; -      F32 minDepth[256];      for (int i = 0; i < 256; ++i)      { -        rpd.refBucket[i][0] = mReflectionProbeCount; -        rpd.refBucket[i][1] = mReflectionProbeCount; -        rpd.refBucket[i][2] = mReflectionProbeCount; -        rpd.refBucket[i][3] = mReflectionProbeCount; +        mProbeData.refBucket[i][0] = mReflectionProbeCount; +        mProbeData.refBucket[i][1] = mReflectionProbeCount; +        mProbeData.refBucket[i][2] = mReflectionProbeCount; +        mProbeData.refBucket[i][3] = mReflectionProbeCount;          minDepth[i] = FLT_MAX;      } @@ -1115,7 +1088,7 @@ void LLReflectionMapManager::updateUniforms()                  if (refmap->mMinDepth < minDepth[i])                  {                      minDepth[i] = refmap->mMinDepth; -                    rpd.refBucket[i][0] = refmap->mProbeIndex; +                    mProbeData.refBucket[i][0] = refmap->mProbeIndex;                  }              }          } @@ -1143,25 +1116,25 @@ void LLReflectionMapManager::updateUniforms()                  }              }              modelview.affineTransform(refmap->mOrigin, oa); -            rpd.refSphere[count].set(oa.getF32ptr()); -            rpd.refSphere[count].mV[3] = refmap->mRadius; +            mProbeData.refSphere[count].set(oa.getF32ptr()); +            mProbeData.refSphere[count].mV[3] = refmap->mRadius;          } -        rpd.refIndex[count][0] = refmap->mCubeIndex; +        mProbeData.refIndex[count][0] = refmap->mCubeIndex;          llassert(nc % 4 == 0); -        rpd.refIndex[count][1] = nc / 4; -        rpd.refIndex[count][3] = refmap->mPriority; +        mProbeData.refIndex[count][1] = nc / 4; +        mProbeData.refIndex[count][3] = refmap->mPriority;          // for objects that are reflection probes, use the volume as the influence volume of the probe          // only possibile influence volumes are boxes and spheres, so detect boxes and treat everything else as spheres -        if (refmap->getBox(rpd.refBox[count])) +        if (refmap->getBox(mProbeData.refBox[count]))          { // negate priority to indicate this probe has a box influence volume -            rpd.refIndex[count][3] = -rpd.refIndex[count][3]; +            mProbeData.refIndex[count][3] = -mProbeData.refIndex[count][3];          } -        rpd.refParams[count].set( -            llmax(minimum_ambiance, refmap->getAmbiance())*ambscale, // ambiance scale -            radscale, // radiance scale +        mProbeData.refParams[count].set( +            llmax(minimum_ambiance, refmap->getAmbiance())*ambscale * llmax(mResetFade, 0.f), // ambiance scale +            radscale * llmax(mResetFade, 0.f), // radiance scale              refmap->mFadeIn, // fade in weight              oa.getF32ptr()[2] - refmap->mRadius); // z near @@ -1186,7 +1159,7 @@ void LLReflectionMapManager::updateUniforms()                  }                  // this neighbor may be sampled -                rpd.refNeighbor[ni++] = idx; +                mProbeData.refNeighbor[ni++] = idx;                  neighbor_count++;                  if (neighbor_count >= max_neighbors) @@ -1199,11 +1172,11 @@ void LLReflectionMapManager::updateUniforms()          if (nc == ni)          {              //no neighbors, tag as empty -            rpd.refIndex[count][1] = -1; +            mProbeData.refIndex[count][1] = -1;          }          else          { -            rpd.refIndex[count][2] = ni - nc; +            mProbeData.refIndex[count][2] = ni - nc;              // move the cursor forward              nc = ni; @@ -1241,19 +1214,19 @@ void LLReflectionMapManager::updateUniforms()      }  #endif -    rpd.refmapCount = count; +    mProbeData.refmapCount = count;      gPipeline.mHeroProbeManager.updateUniforms();      // Get the hero data. -    rpd.heroBox = gPipeline.mHeroProbeManager.mHeroData.heroBox; -    rpd.heroSphere = gPipeline.mHeroProbeManager.mHeroData.heroSphere; -    rpd.heroShape  = gPipeline.mHeroProbeManager.mHeroData.heroShape; -    rpd.heroMipCount = gPipeline.mHeroProbeManager.mHeroData.heroMipCount; -    rpd.heroProbeCount = gPipeline.mHeroProbeManager.mHeroData.heroProbeCount; +    mProbeData.heroBox = gPipeline.mHeroProbeManager.mHeroData.heroBox; +    mProbeData.heroSphere = gPipeline.mHeroProbeManager.mHeroData.heroSphere; +    mProbeData.heroShape  = gPipeline.mHeroProbeManager.mHeroData.heroShape; +    mProbeData.heroMipCount   = gPipeline.mHeroProbeManager.mHeroData.heroMipCount; +    mProbeData.heroProbeCount = gPipeline.mHeroProbeManager.mHeroData.heroProbeCount; -    //copy rpd into uniform buffer object +    //copy mProbeData into uniform buffer object      if (mUBO == 0)      {          glGenBuffers(1, &mUBO); @@ -1262,7 +1235,7 @@ void LLReflectionMapManager::updateUniforms()      {          LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer");          glBindBuffer(GL_UNIFORM_BUFFER, mUBO); -        glBufferData(GL_UNIFORM_BUFFER, sizeof(ReflectionProbeData), &rpd, GL_STREAM_DRAW); +        glBufferData(GL_UNIFORM_BUFFER, sizeof(ReflectionProbeData), &mProbeData, GL_STREAM_DRAW);          glBindBuffer(GL_UNIFORM_BUFFER, 0);      } @@ -1396,7 +1369,8 @@ void LLReflectionMapManager::renderDebug()  void LLReflectionMapManager::initReflectionMaps()  { -    U32 count = LL_MAX_REFLECTION_PROBE_COUNT; +    static LLCachedControl<U32> probe_count(gSavedSettings, "RenderReflectionProbeCount", 256U); +    U32 count = probe_count();      static LLCachedControl<U32> ref_probe_res(gSavedSettings, "RenderReflectionProbeResolution", 128U);      U32 probe_resolution = nhpo2(llclamp(ref_probe_res(), (U32)64, (U32)512)); diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h index f81fb30738..9f88776ac2 100644 --- a/indra/newview/llreflectionmapmanager.h +++ b/indra/newview/llreflectionmapmanager.h @@ -56,6 +56,51 @@ public:          REALTIME = 2      }; +    // General guidance for UBOs is to statically allocate all of these fields to make your life ever so slightly easier. +    // Then set a "max" value for the number of probes you'll ever have, and use that to index into the arrays. +    // We do this with refmapCount.  The shaders will just pick up on it there. +    // This data structure should _always_ match what's in class3/deferred/reflectionProbeF.glsl. +    // The shader can and will break otherwise. +    // -Geenz 2025-03-10 +    struct ReflectionProbeData +    { +        // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of +        // the box probe +        LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT]; + +        LLMatrix4 heroBox; + +        // for sphere probes, origin (xyz) and radius (w) of refmaps in clip space +        LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT]; + +        // extra parameters +        //  x - irradiance scale +        //  y - radiance scale +        //  z - fade in +        //  w - znear +        LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT]; + +        LLVector4 heroSphere; + +        // indices used by probe: +        //  [i][0] - cubemap array index for this probe +        //  [i][1] - index into "refNeighbor" for probes that intersect this probe +        //  [i][2] - number of probes  that intersect this probe, or -1 for no neighbors +        //  [i][3] - priority (probe type stored in sign bit - positive for spheres, negative for boxes) +        GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4]; + +        // list of neighbor indices +        GLint refNeighbor[4096]; + +        GLint refBucket[256][4]; // lookup table for which index to start with for the given Z depth +        // numbrer of active refmaps +        GLint refmapCount; + +        GLint heroShape; +        GLint heroMipCount; +        GLint heroProbeCount; +    }; +      // allocate an environment map of the given resolution      LLReflectionMapManager(); @@ -207,8 +252,12 @@ private:      // if true, reset all probe render state on the next update (for teleports and sky changes)      bool mReset = false; +    float mResetFade = 1.f; +      // if true, only update the default probe      bool mPaused = false;      F32 mResumeTime = 0.f; + +    ReflectionProbeData mProbeData;  }; diff --git a/indra/newview/llscrollingpanelparam.h b/indra/newview/llscrollingpanelparam.h index 3aba4e4e40..93daf22e76 100644 --- a/indra/newview/llscrollingpanelparam.h +++ b/indra/newview/llscrollingpanelparam.h @@ -81,7 +81,6 @@ public:  protected:      LLTimer             mMouseDownTimer;    // timer for how long mouse has been held down on a hint.      F32                 mLastHeldTime; -    bool mAllowModify;      LLButton* mLessBtn;      LLButton* mMoreBtn; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index fceb25b3d3..b307de787c 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -7253,7 +7253,10 @@ void dialog_refresh_all()      // *TODO: Eliminate all calls into outside classes below, make those      // objects register with the update signal. -    gFloaterTools->dirty(); +    if (gFloaterTools) +    { +        gFloaterTools->dirty(); +    }      gMenuObject->needsArrange(); @@ -7482,7 +7485,8 @@ void LLSelectMgr::updatePointAt()              LLVector3 select_offset;              const LLPickInfo& pick = gViewerWindow->getLastPick();              LLViewerObject *click_object = pick.getObject(); -            if (click_object && click_object->isSelected()) +            bool was_hud = pick.mPickHUD && !click_object->isHUDAttachment(); +            if (click_object && click_object->isSelected() && !was_hud)              {                  // clicked on another object in our selection group, use that as target                  select_offset.setVec(pick.mObjectOffset); diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 62df6cd275..6023f6885d 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -832,6 +832,10 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force)          {              shader->uniform3fv(LLShaderMgr::AMBIENT, LLVector3(ambient.mV));              shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, sqrtf(g)*2.0f); // use a modifier here so 1.0 maps to the "most desirable" default and the maximum value doesn't go off the rails + +            // Low quality setting +            if (!LLPipeline::sReflectionProbesEnabled) +                probe_ambiance = DEFAULT_AUTO_ADJUST_PROBE_AMBIANCE;          }          else if (psky->canAutoAdjust() && should_auto_adjust)          { // auto-adjust legacy sky to take advantage of probe ambiance diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6c3f3849ce..eb0e9ef4bc 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -297,16 +297,62 @@ void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is  // exported functionality  // +void do_startup_frame() +{ +    // Until after STATE_AGENT_SEND we don't get very many UDP packets to poll the socket, +    // and after STATE_PRECACHE the LLAppViewer::idleNetwork() will do UDP processing, +    // so we only bother to process between those two states. +    EStartupState state = LLStartUp::getStartupState(); +    if (state > STATE_AGENT_SEND && state < STATE_PRECACHE) +    { +        // drain the UDP socket... +        U64 t0 = totalTime(); +        constexpr U64 MAX_STARTUP_FRAME_TIME = 2000; // usec +        constexpr U64 MAX_STARTUP_FRAME_MESSAGES = 100; +        S32 num_messages = 0; +        bool needs_drain = false; +        LockMessageChecker lmc(gMessageSystem); +        while (lmc.checkAllMessages(gFrameCount, gServicePump)) +        { +            if (gDoDisconnect) +            { +                // We're disconnecting, don't process any more messages from the server +                // We're usually disconnecting due to either network corruption or a +                // server going down, so this is OK. +                break; +            } +            if (++num_messages >= MAX_STARTUP_FRAME_MESSAGES +                || (totalTime() - t0) > MAX_STARTUP_FRAME_TIME) +            { +                needs_drain = true; +                break; +            } +        } +        if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0) +        { +             gMessageSystem->drainUdpSocket(); +        } +        lmc.processAcks(); +    } +    // ...then call display_startup() +    display_startup(); +} +  void pump_idle_startup_network(void)  { +    // while there are message to process: +    //     process one then call display_startup() +    S32 num_messages = 0;      {          LockMessageChecker lmc(gMessageSystem);          while (lmc.checkAllMessages(gFrameCount, gServicePump))          {              display_startup(); +            ++num_messages;          }          lmc.processAcks();      } +    // finally call one last display_startup()      display_startup();  } @@ -623,21 +669,6 @@ bool idle_startup()              F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage");              msg->mPacketRing.setDropPercentage(dropPercent); - -            F32 inBandwidth = gSavedSettings.getF32("InBandwidth"); -            F32 outBandwidth = gSavedSettings.getF32("OutBandwidth"); -            if (inBandwidth != 0.f) -            { -                LL_DEBUGS("AppInit") << "Setting packetring incoming bandwidth to " << inBandwidth << LL_ENDL; -                msg->mPacketRing.setUseInThrottle(true); -                msg->mPacketRing.setInBandwidth(inBandwidth); -            } -            if (outBandwidth != 0.f) -            { -                LL_DEBUGS("AppInit") << "Setting packetring outgoing bandwidth to " << outBandwidth << LL_ENDL; -                msg->mPacketRing.setUseOutThrottle(true); -                msg->mPacketRing.setOutBandwidth(outBandwidth); -            }          }          LL_INFOS("AppInit") << "Message System Initialized." << LL_ENDL; @@ -743,7 +774,7 @@ bool idle_startup()          LL_DEBUGS("AppInit") << "STATE_BROWSER_INIT" << LL_ENDL;          std::string msg = LLTrans::getString("LoginInitializingBrowser");          set_startup_status(0.03f, msg.c_str(), gAgent.mMOTD.c_str()); -        display_startup(); +        do_startup_frame();          // LLViewerMedia::initBrowser();          LLStartUp::setStartupState( STATE_LOGIN_SHOW );          return false; @@ -808,7 +839,7 @@ bool idle_startup()                      LL_DEBUGS("AppInit") << "FirstLoginThisInstall off" << LL_ENDL;                  }              } -            display_startup(); +            do_startup_frame();              LLStartUp::setStartupState( STATE_LOGIN_WAIT );     // Wait for user input          }          else @@ -839,7 +870,7 @@ bool idle_startup()          }          LL_DEBUGS("AppInit") << "PeekMessage processed" << LL_ENDL;  #endif -        display_startup(); +        do_startup_frame();          timeout.reset();          return false;      } @@ -854,7 +885,7 @@ bool idle_startup()          // Don't do anything.  Wait for the login view to call the login_callback,          // which will push us to the next state. -        // display() function will be the one to run display_startup() +        // display() function will be the one to run do_startup_frame()          // Sleep so we don't spin the CPU          ms_sleep(1);          return false; @@ -1036,7 +1067,7 @@ bool idle_startup()          auth_desc = LLTrans::getString("LoginInProgress");          set_startup_status(progress, auth_desc, auth_message);          progress += 0.02f; -        display_startup(); +        do_startup_frame();          // Setting initial values...          LLLoginInstance* login = LLLoginInstance::getInstance(); @@ -1073,7 +1104,7 @@ bool idle_startup()          emsg << LLTrans::getString("LoginFailedHeader") << "\n";          if(LLLoginInstance::getInstance()->authFailure())          { -            LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): " +            LL_INFOS("LLStartUp") << "Login failed, LLLoginInstance::getResponse(): "                                    << LLLoginInstance::getInstance()->getResponse() << LL_ENDL;              LLSD response = LLLoginInstance::getInstance()->getResponse();              // Still have error conditions that may need some @@ -1145,7 +1176,7 @@ bool idle_startup()                  // If optional was skipped this case shouldn't                  // be reached. -                LL_INFOS("LLStartup") << "Forcing a quit due to update." << LL_ENDL; +                LL_INFOS("LLStartUp") << "Forcing a quit due to update." << LL_ENDL;                  LLLoginInstance::getInstance()->disconnect();                  LLAppViewer::instance()->forceQuit();              } @@ -1173,7 +1204,7 @@ bool idle_startup()                          }                          catch (LLCertException &cert_exception)                          { -                            LL_WARNS("LLStartup", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL; +                            LL_WARNS("LLStartUp", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL;                              LLSD args;                              args["REASON"] = LLTrans::getString(cert_exception.what()); @@ -1225,7 +1256,7 @@ bool idle_startup()                          // notificatioin message.                          LLSD args;                          args["ERROR_MESSAGE"] = emsg.str(); -                        LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; +                        LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL;                          LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);                      }                  } @@ -1248,7 +1279,7 @@ bool idle_startup()              {                  LLSD args;                  args["ERROR_MESSAGE"] = emsg.str(); -                LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; +                LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL;                  LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);                  transition_back_to_login_panel(emsg.str());                  show_connect_box = true; @@ -1264,71 +1295,71 @@ bool idle_startup()      if (STATE_WORLD_INIT == LLStartUp::getStartupState())      {          set_startup_status(0.30f, LLTrans::getString("LoginInitializingWorld"), gAgent.mMOTD); -        display_startup(); +        do_startup_frame();          // We should have an agent id by this point.          llassert(!(gAgentID == LLUUID::null));          // Finish agent initialization.  (Requires gSavedSettings, builds camera)          gAgent.init(); -        display_startup(); +        do_startup_frame();          gAgentCamera.init(); -        display_startup(); -        display_startup(); +        do_startup_frame(); +        do_startup_frame();          // Since we connected, save off the settings so the user doesn't have to          // type the name/password again if we crash.          gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true);          LLUIColorTable::instance().saveUserSettings(); -        display_startup(); +        do_startup_frame();          //          // Initialize classes w/graphics stuff.          //          LLSurface::initClasses(); -        display_startup(); +        do_startup_frame(); -        display_startup(); +        do_startup_frame();          LLDrawable::initClass(); -        display_startup(); +        do_startup_frame();          // init the shader managers          LLPostProcess::initClass(); -        display_startup(); +        do_startup_frame();          LLAvatarAppearance::initClass("avatar_lad.xml","avatar_skeleton.xml"); -        display_startup(); +        do_startup_frame();          LLViewerObject::initVOClasses(); -        display_startup(); +        do_startup_frame();          // Initialize all our tools.  Must be done after saved settings loaded.          // NOTE: This also is where gToolMgr used to be instantiated before being turned into a singleton.          LLToolMgr::getInstance()->initTools(); -        display_startup(); +        do_startup_frame();          // Pre-load floaters, like the world map, that are slow to spawn          // due to XML complexity.          gViewerWindow->initWorldUI(); -        display_startup(); +        do_startup_frame();          // This is where we used to initialize gWorldp. Original comment said:          // World initialization must be done after above window init          // User might have overridden far clip          LLWorld::getInstance()->setLandFarClip(gAgentCamera.mDrawDistance); -        display_startup(); +        do_startup_frame();          // Before we create the first region, we need to set the agent's mOriginGlobal          // This is necessary because creating objects before this is set will result in a          // bad mPositionAgent cache.          gAgent.initOriginGlobal(from_region_handle(gFirstSimHandle)); -        display_startup(); +        do_startup_frame();          LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim); -        display_startup(); +        do_startup_frame();          LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(gFirstSimHandle);          LL_INFOS("AppInit") << "Adding initial simulator " << regionp->getOriginGlobal() << LL_ENDL; @@ -1337,18 +1368,18 @@ bool idle_startup()          << gFirstSimSeedCap << LL_ENDL;          regionp->setSeedCapability(gFirstSimSeedCap);          LL_DEBUGS("AppInit") << "Waiting for seed grant ...." << LL_ENDL; -        display_startup(); +        do_startup_frame();          // Set agent's initial region to be the one we just created.          gAgent.setRegion(regionp); -        display_startup(); +        do_startup_frame();          // Set agent's initial position, which will be read by LLVOAvatar when the avatar          // object is created.  I think this must be done after setting the region.  JC          gAgent.setPositionAgent(agent_start_position_region); -        display_startup(); +        do_startup_frame();          LLStartUp::initExperiences(); -        display_startup(); +        do_startup_frame();          // If logging should be enebled, turns it on and loads history from disk          // Note: does not happen on init of singleton because preferences can use @@ -1369,7 +1400,7 @@ bool idle_startup()      {          LLStartUp::multimediaInit();          LLStartUp::setStartupState( STATE_FONT_INIT ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1378,7 +1409,7 @@ bool idle_startup()      {          LLStartUp::fontInit();          LLStartUp::setStartupState( STATE_SEED_GRANTED_WAIT ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1450,7 +1481,7 @@ bool idle_startup()                  set_startup_status(0.4f, LLTrans::getString("LoginRequestSeedCapGrant"), gAgent.mMOTD.c_str());              }          } -        display_startup(); +        do_startup_frame();          return false;      } @@ -1461,7 +1492,7 @@ bool idle_startup()      //---------------------------------------------------------------------      if (STATE_SEED_CAP_GRANTED == LLStartUp::getStartupState())      { -        display_startup(); +        do_startup_frame();          // These textures are not warrantied to be cached, so needs          // to hapen with caps granted @@ -1470,9 +1501,9 @@ bool idle_startup()          // will init images, should be done with caps, but before gSky.init()          LLEnvironment::getInstance()->initSingleton(); -        display_startup(); +        do_startup_frame();          update_texture_fetch(); -        display_startup(); +        do_startup_frame();          if ( gViewerWindow != NULL)          {   // This isn't the first logon attempt, so show the UI @@ -1480,15 +1511,15 @@ bool idle_startup()          }          gLoginMenuBarView->setVisible( false );          gLoginMenuBarView->setEnabled( false ); -        display_startup(); +        do_startup_frame();          // direct logging to the debug console's line buffer          LLError::logToFixedBuffer(gDebugView->mDebugConsolep); -        display_startup(); +        do_startup_frame();          // set initial visibility of debug console          gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole")); -        display_startup(); +        do_startup_frame();          //          // Set message handlers @@ -1497,23 +1528,23 @@ bool idle_startup()          // register callbacks for messages. . . do this after initial handshake to make sure that we don't catch any unwanted          register_viewer_callbacks(gMessageSystem); -        display_startup(); +        do_startup_frame();          // Debugging info parameters          gMessageSystem->setMaxMessageTime( 0.5f );          // Spam if decoding all msgs takes more than 500 ms -        display_startup(); +        do_startup_frame();          #ifndef LL_RELEASE_FOR_DOWNLOAD              gMessageSystem->setTimeDecodes( true );             // Time the decode of each msg              gMessageSystem->setTimeDecodesSpamThreshold( 0.05f );  // Spam if a single msg takes over 50ms to decode          #endif -        display_startup(); +        do_startup_frame();          gXferManager->registerCallbacks(gMessageSystem); -        display_startup(); +        do_startup_frame();          LLStartUp::initNameCache(); -        display_startup(); +        do_startup_frame();          // update the voice settings *after* gCacheName initialization          // so that we can construct voice UI that relies on the name cache @@ -1521,7 +1552,7 @@ bool idle_startup()          {              LLVoiceClient::getInstance()->updateSettings();          } -        display_startup(); +        do_startup_frame();          // create a container's instance for start a controlling conversation windows          // by the voice's events @@ -1542,12 +1573,12 @@ bool idle_startup()          // register null callbacks for audio until the audio system is initialized          gMessageSystem->setHandlerFuncFast(_PREHASH_SoundTrigger, null_message_callback, NULL);          gMessageSystem->setHandlerFuncFast(_PREHASH_AttachedSound, null_message_callback, NULL); -        display_startup(); +        do_startup_frame();          //reset statistics          LLViewerStats::instance().resetStats(); -        display_startup(); +        do_startup_frame();          //          // Set up region and surface defaults          // @@ -1572,7 +1603,7 @@ bool idle_startup()          LLViewerCamera::getInstance()->setAspect(gViewerWindow->getWorldViewAspectRatio());          // Initialize FOV          LLViewerCamera::getInstance()->setDefaultFOV(gSavedSettings.getF32("CameraAngle")); -        display_startup(); +        do_startup_frame();          // Move agent to starting location. The position handed to us by          // the space server is in global coordinates, but the agent frame @@ -1583,7 +1614,7 @@ bool idle_startup()          gAgent.resetAxes(gAgentStartLookAt);          gAgentCamera.stopCameraAnimation();          gAgentCamera.resetCamera(); -        display_startup(); +        do_startup_frame();          // Initialize global class data needed for surfaces (i.e. textures)          LL_DEBUGS("AppInit") << "Initializing sky..." << LL_ENDL; @@ -1594,7 +1625,7 @@ bool idle_startup()          LLGLState::checkStates(); -        display_startup(); +        do_startup_frame();          LL_DEBUGS("AppInit") << "Decoding images..." << LL_ENDL;          // For all images pre-loaded into viewer cache, init @@ -1608,12 +1639,12 @@ bool idle_startup()          {              F32 frac = (F32)i / (F32)DECODE_TIME_SEC;              set_startup_status(0.45f + frac*0.1f, LLTrans::getString("LoginDecodingImages"), gAgent.mMOTD); -            display_startup(); +            do_startup_frame();              gTextureList.decodeAllImages(1.f);          }          LLStartUp::setStartupState( STATE_WORLD_WAIT ); -        display_startup(); +        do_startup_frame();          // JC - Do this as late as possible to increase likelihood Purify          // will run. @@ -1642,7 +1673,7 @@ bool idle_startup()              NULL);          timeout.reset(); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1669,7 +1700,7 @@ bool idle_startup()      {          LL_DEBUGS("AppInit") << "Connecting to region..." << LL_ENDL;          set_startup_status(0.60f, LLTrans::getString("LoginConnectingToRegion"), gAgent.mMOTD); -        display_startup(); +        do_startup_frame();          // register with the message system so it knows we're          // expecting this message          LLMessageSystem* msg = gMessageSystem; @@ -1683,7 +1714,7 @@ bool idle_startup()              gAssetStorage->setUpstream(regionp->getHost());              gCacheName->setUpstream(regionp->getHost());          } -        display_startup(); +        do_startup_frame();          // Create login effect          // But not on first login, because you can't see your avatar then @@ -1698,7 +1729,7 @@ bool idle_startup()          LLStartUp::setStartupState( STATE_AGENT_WAIT );     // Go to STATE_AGENT_WAIT          timeout.reset(); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1707,35 +1738,13 @@ bool idle_startup()      //---------------------------------------------------------------------      if (STATE_AGENT_WAIT == LLStartUp::getStartupState())      { -        { -            LockMessageChecker lmc(gMessageSystem); -            while (lmc.checkAllMessages(gFrameCount, gServicePump)) -            { -                if (gAgentMovementCompleted) -                { -                    // Sometimes we have more than one message in the -                    // queue. break out of this loop and continue -                    // processing. If we don't, then this could skip one -                    // or more login steps. -                    break; -                } -                else -                { -                    LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got " -                                         << gMessageSystem->getMessageName() << LL_ENDL; -                } -                display_startup(); -            } -            lmc.processAcks(); -        } - -        display_startup(); +        do_startup_frame();          if (gAgentMovementCompleted)          {              LLStartUp::setStartupState( STATE_INVENTORY_SEND );          } -        display_startup(); +        do_startup_frame();          if (!gAgentMovementCompleted && timeout.getElapsedTimeF32() > STATE_AGENT_WAIT_TIMEOUT)          { @@ -1768,7 +1777,7 @@ bool idle_startup()      if (STATE_INVENTORY_SEND == LLStartUp::getStartupState())      {          LL_PROFILE_ZONE_NAMED("State inventory send") -        display_startup(); +        do_startup_frame();          // request mute list          LL_INFOS() << "Requesting Mute List" << LL_ENDL; @@ -1778,12 +1787,12 @@ bool idle_startup()          LL_INFOS() << "Requesting Money Balance" << LL_ENDL;          LLStatusBar::sendMoneyBalanceRequest(); -        display_startup(); +        do_startup_frame();          // Inform simulator of our language preference          LLAgentLanguage::update(); -        display_startup(); +        do_startup_frame();          // unpack thin inventory          LLSD response = LLLoginInstance::getInstance()->getResponse();          //bool dump_buffer = false; @@ -1798,7 +1807,7 @@ bool idle_startup()                  gInventory.setLibraryRootFolderID(id.asUUID());              }          } -        display_startup(); +        do_startup_frame();          LLSD inv_lib_owner = response["inventory-lib-owner"];          if(inv_lib_owner.isDefined()) @@ -1810,9 +1819,9 @@ bool idle_startup()                  gInventory.setLibraryOwnerID(LLUUID(id.asUUID()));              }          } -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState(STATE_INVENTORY_SKEL); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1831,7 +1840,7 @@ bool idle_startup()                  LL_WARNS("AppInit") << "Problem loading inventory-skel-lib" << LL_ENDL;              }          } -        display_startup(); +        do_startup_frame();          LLSD inv_skeleton = response["inventory-skeleton"];          if (inv_skeleton.isDefined()) @@ -1842,9 +1851,9 @@ bool idle_startup()                  LL_WARNS("AppInit") << "Problem loading inventory-skel-targets" << LL_ENDL;              }          } -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState(STATE_INVENTORY_SEND2); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1890,7 +1899,7 @@ bool idle_startup()                  list[agent_id] = new LLRelationship(given_rights, has_rights, false);              }              LLAvatarTracker::instance().addBuddyList(list); -            display_startup(); +            do_startup_frame();          }          bool show_hud = false; @@ -1918,7 +1927,7 @@ bool idle_startup()                  //}              }          } -        display_startup(); +        do_startup_frame();          // Either we want to show tutorial because this is the first login          // to a Linden Help Island or the user quit with the tutorial @@ -1927,21 +1936,21 @@ bool idle_startup()          {              LLFloaterReg::showInstance("hud", LLSD(), false);          } -        display_startup(); +        do_startup_frame();          LLSD event_notifications = response["event_notifications"];          if(event_notifications.isDefined())          {              gEventNotifier.load(event_notifications);          } -        display_startup(); +        do_startup_frame();          LLSD classified_categories = response["classified_categories"];          if(classified_categories.isDefined())          {              LLClassifiedInfo::loadCategories(classified_categories);          } -        display_startup(); +        do_startup_frame();          // This method MUST be called before gInventory.findCategoryUUIDForType because of          // gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap. @@ -1960,7 +1969,7 @@ bool idle_startup()          LLInventoryModelBackgroundFetch::instance().start();          gInventory.createCommonSystemCategories();          LLStartUp::setStartupState(STATE_INVENTORY_CALLBACKS ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1972,7 +1981,7 @@ bool idle_startup()      {          if (!LLInventoryModel::isSysFoldersReady())          { -            display_startup(); +            do_startup_frame();              return false;          } @@ -1997,7 +2006,7 @@ bool idle_startup()          gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null);          gInventory.notifyObservers(); -        display_startup(); +        do_startup_frame();          // set up callbacks          LL_INFOS() << "Registering Callbacks" << LL_ENDL; @@ -2008,18 +2017,18 @@ bool idle_startup()          LLAvatarTracker::instance().registerCallbacks(msg);          LL_INFOS() << " Landmark" << LL_ENDL;          LLLandmark::registerCallbacks(msg); -        display_startup(); +        do_startup_frame();          // request all group information          LL_INFOS() << "Requesting Agent Data" << LL_ENDL;          gAgent.sendAgentDataUpdateRequest(); -        display_startup(); +        do_startup_frame();          // Create the inventory views          LL_INFOS() << "Creating Inventory Views" << LL_ENDL;          LLFloaterReg::getInstance("inventory"); -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState( STATE_MISC ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -2069,7 +2078,7 @@ bool idle_startup()              gSavedSettings.setBOOL("ShowStartLocation", true);          } -        display_startup(); +        do_startup_frame();          // Load stored local environment if needed.          LLEnvironment::instance().loadFromSettings(); @@ -2077,7 +2086,7 @@ bool idle_startup()          // *TODO : Uncomment that line once the whole grid migrated to SLM and suppress it from LLAgent::handleTeleportFinished() (llagent.cpp)          //check_merchant_status(); -        display_startup(); +        do_startup_frame();          if (gSavedSettings.getBOOL("HelpFloaterOpen"))          { @@ -2085,7 +2094,7 @@ bool idle_startup()              LLViewerHelp::instance().showTopic("");          } -        display_startup(); +        do_startup_frame();          // We're successfully logged in.          gSavedSettings.setBOOL("FirstLoginThisInstall", false); @@ -2094,12 +2103,12 @@ bool idle_startup()          LLFloaterGridStatus::getInstance()->startGridStatusTimer(); -        display_startup(); +        do_startup_frame(); -        display_startup(); +        do_startup_frame();          // JC: Initializing audio requests many sounds for download.          init_audio(); -        display_startup(); +        do_startup_frame();          // JC: Initialize "active" gestures.  This may also trigger          // many gesture downloads, if this is the user's first @@ -2137,7 +2146,7 @@ bool idle_startup()              LLGestureMgr::instance().startFetch();          }          gDisplaySwapBuffers = true; -        display_startup(); +        do_startup_frame();          LLMessageSystem* msg = gMessageSystem;          msg->setHandlerFuncFast(_PREHASH_SoundTrigger,              process_sound_trigger); @@ -2215,10 +2224,10 @@ bool idle_startup()              }          } -        display_startup(); +        do_startup_frame();          //DEV-17797.  get null folder.  Any items found here moved to Lost and Found          LLInventoryModelBackgroundFetch::instance().findLostItems(); -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState( STATE_PRECACHE );          timeout.reset(); @@ -2227,7 +2236,7 @@ bool idle_startup()      if (STATE_PRECACHE == LLStartUp::getStartupState())      { -        display_startup(); +        do_startup_frame();          F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY;          // We now have an inventory skeleton, so if this is a user's first @@ -2253,7 +2262,7 @@ bool idle_startup()              callAfterCOFFetch(set_flags_and_update_appearance);          } -        display_startup(); +        do_startup_frame();          // wait precache-delay and for agent's avatar or a lot longer.          if ((timeout_frac > 1.f) && isAgentAvatarValid()) @@ -2278,7 +2287,7 @@ bool idle_startup()              set_startup_status(0.60f + 0.30f * timeout_frac,                  LLTrans::getString("LoginPrecaching"),                      gAgent.mMOTD.c_str()); -            display_startup(); +            do_startup_frame();          }          return true; @@ -2306,7 +2315,7 @@ bool idle_startup()              LLStartUp::setStartupState( STATE_CLEANUP );          } -        display_startup(); +        do_startup_frame();          if (gAgent.isOutfitChosen() && (wearables_time > MAX_WEARABLES_TIME))          { @@ -2347,7 +2356,7 @@ bool idle_startup()      if (STATE_CLEANUP == LLStartUp::getStartupState())      {          set_startup_status(1.0, "", ""); -        display_startup(); +        do_startup_frame();          if (!mBenefitsSuccessfullyInit)          { @@ -2368,7 +2377,7 @@ bool idle_startup()          //gViewerWindow->revealIntroPanel();          gViewerWindow->setStartupComplete();          gViewerWindow->setProgressCancelButtonVisible(false); -        display_startup(); +        do_startup_frame();          // We're not away from keyboard, even though login might have taken          // a while. JC @@ -2400,7 +2409,7 @@ bool idle_startup()          // LLUserAuth::getInstance()->reset();          LLStartUp::setStartupState( STATE_STARTED ); -        display_startup(); +        do_startup_frame();          // Unmute audio if desired and setup volumes.          // This is a not-uncommon crash site, so surround it with @@ -2416,7 +2425,7 @@ bool idle_startup()          LLAgentPicksInfo::getInstance()->requestNumberOfPicks(); -        display_startup(); +        do_startup_frame();          llassert(LLPathfindingManager::getInstance() != NULL);          LLPathfindingManager::getInstance()->initSystem(); @@ -3021,9 +3030,7 @@ std::string LLStartUp::startupStateToString(EStartupState state)  // static  void LLStartUp::setStartupState( EStartupState state )  { -    LL_INFOS("AppInit") << "Startup state changing from " << -        getStartupStateString() << " to " << -        startupStateToString(state) << LL_ENDL; +    LL_INFOS("AppInit") << getStartupStateString() << " --> " << startupStateToString(state) << LL_ENDL;      getPhases().stopPhase(getStartupStateString());      gStartupState = state; @@ -3087,7 +3094,7 @@ void LLStartUp::multimediaInit()      LL_DEBUGS("AppInit") << "Initializing Multimedia...." << LL_ENDL;      std::string msg = LLTrans::getString("LoginInitializingMultimedia");      set_startup_status(0.42f, msg.c_str(), gAgent.mMOTD.c_str()); -    display_startup(); +    do_startup_frame();  }  void LLStartUp::fontInit() @@ -3095,7 +3102,7 @@ void LLStartUp::fontInit()      LL_DEBUGS("AppInit") << "Initializing fonts...." << LL_ENDL;      std::string msg = LLTrans::getString("LoginInitializingFonts");      set_startup_status(0.45f, msg.c_str(), gAgent.mMOTD.c_str()); -    display_startup(); +    do_startup_frame();      LLFontGL::loadDefaultFonts();  } @@ -3792,14 +3799,14 @@ bool process_login_success_response()      {          // We got an answer from the grid -> use that for map for the current session          gSavedSettings.setString("CurrentMapServerURL", map_server_url); -        LL_INFOS("LLStartup") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL; +        LL_INFOS("LLStartUp") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL;      }      else      {          // No answer from the grid -> use the default setting for current session          map_server_url = gSavedSettings.getString("MapServerURL");          gSavedSettings.setString("CurrentMapServerURL", map_server_url); -        LL_INFOS("LLStartup") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL; +        LL_INFOS("LLStartUp") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL;      }      // Default male and female avatars allowing the user to choose their avatar on first login. diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 9f7d09c794..087761cbd0 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -29,6 +29,7 @@  #include <iostream>  #include <map>  #include <algorithm> +#include <atomic>  #include "lltexturefetch.h" @@ -3571,29 +3572,30 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)      //if (! gViewerAssetStatsThread1)      //  return true; -    static std::atomic_bool reporting_started(false); -    static std::atomic_int report_sequence(0); +    static std::atomic<bool> reporting_started(false); +    static std::atomic<S32> report_sequence(0);      // In mStatsSD, we have a copy we own of the LLSD representation      // of the asset stats. Add some additional fields and ship it off.      static const S32 metrics_data_version = 2; -    bool initial_report = !reporting_started; +    bool initial_report = !reporting_started.load();      mStatsSD["session_id"] = mSessionID;      mStatsSD["agent_id"] = mAgentID;      mStatsSD["message"] = "ViewerAssetMetrics"; -    mStatsSD["sequence"] = report_sequence; +    mStatsSD["sequence"] = report_sequence.load();      mStatsSD["initial"] = initial_report;      mStatsSD["version"] = metrics_data_version;      mStatsSD["break"] = static_cast<bool>(LLTextureFetch::svMetricsDataBreak);      // Update sequence number -    if (S32_MAX == ++report_sequence) +    if (S32_MAX == report_sequence.fetch_add(1))      { -        report_sequence = 0; +        report_sequence.store(0);      } -    reporting_started = true; + +    reporting_started.store(true);      // Limit the size of the stats report if necessary. diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index bda53f66eb..78d930c05c 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -642,7 +642,7 @@ void LLGLTexMemBar::draw()      text = llformat("Mesh: Reqs(Tot/Htp/Big): %u/%u/%u Rtr/Err: %u/%u Cread/Cwrite: %u/%u Low/At/High: %d/%d/%d",                      LLMeshRepository::sMeshRequestCount, LLMeshRepository::sHTTPRequestCount, LLMeshRepository::sHTTPLargeRequestCount,                      LLMeshRepository::sHTTPRetryCount, LLMeshRepository::sHTTPErrorCount, -                    LLMeshRepository::sCacheReads, LLMeshRepository::sCacheWrites, +                    (U32)LLMeshRepository::sCacheReads, (U32)LLMeshRepository::sCacheWrites,                      LLMeshRepoThread::sRequestLowWater, LLMeshRepoThread::sRequestWaterLevel, LLMeshRepoThread::sRequestHighWater);      LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2,                                               text_color, LLFontGL::LEFT, LLFontGL::TOP); diff --git a/indra/newview/lltoolbrush.cpp b/indra/newview/lltoolbrush.cpp index 2fe81df4fb..cf7b123fa7 100644 --- a/indra/newview/lltoolbrush.cpp +++ b/indra/newview/lltoolbrush.cpp @@ -447,7 +447,10 @@ void LLToolBrushLand::handleSelect()  {      gEditMenuHandler = this; -    gFloaterTools->setStatusText("modifyland"); +    if (gFloaterTools) +    { +        gFloaterTools->setStatusText("modifyland"); +    }  //  if (!mBrushSelected)      {          mBrushSelected = true; diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 4efa289141..9d6f44c096 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -574,12 +574,13 @@ bool LLToolDragAndDrop::handleKey(KEY key, MASK mask)  bool LLToolDragAndDrop::handleToolTip(S32 x, S32 y, MASK mask)  { +    const F32 DRAG_N_DROP_TOOLTIP_DELAY = 0.1f;      if (!mToolTipMsg.empty())      {          LLToolTipMgr::instance().unblockToolTips();          LLToolTipMgr::instance().show(LLToolTip::Params()              .message(mToolTipMsg) -            .delay_time(gSavedSettings.getF32( "DragAndDropToolTipDelay" ))); +            .delay_time(DRAG_N_DROP_TOOLTIP_DELAY));          return true;      }      return false; @@ -1253,6 +1254,7 @@ void LLToolDragAndDrop::dropMaterial(LLViewerObject* hit_obj,          // If user dropped a material onto face it implies          // applying texture now without cancel, save to selection          if (nodep +            && gFloaterTools              && gFloaterTools->getVisible()              && nodep->mSavedGLTFMaterialIds.size() > hit_face)          { @@ -1429,10 +1431,10 @@ void LLToolDragAndDrop::dropTexture(LLViewerObject* hit_obj,          // If user dropped a texture onto face it implies          // applying texture now without cancel, save to selection -        LLPanelFace* panel_face = gFloaterTools->getPanelFace(); +        LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr;          if (nodep -            && gFloaterTools->getVisible()              && panel_face +            && gFloaterTools->getVisible()              && panel_face->getTextureDropChannel() == 0 /*texture*/              && nodep->mSavedTextures.size() > hit_face)          { @@ -1488,8 +1490,8 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,          if (allow_adding_to_override)          {              LLGLTFMaterial::TextureInfo drop_channel = LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR; -            LLPanelFace* panel_face = gFloaterTools->getPanelFace(); -            if (gFloaterTools->getVisible() && panel_face) +            LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr; +            if (panel_face && gFloaterTools->getVisible())              {                  drop_channel = panel_face->getPBRDropChannel();              } @@ -1514,9 +1516,9 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,      LLTextureEntry* tep = hit_obj->getTE(hit_face); -    LLPanelFace* panel_face = gFloaterTools->getPanelFace(); +    LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr; -    if (gFloaterTools->getVisible() && panel_face) +    if (panel_face && gFloaterTools->getVisible())      {          tex_channel = (tex_channel > -1) ? tex_channel : panel_face->getTextureDropChannel();          switch (tex_channel) @@ -1611,7 +1613,10 @@ void LLToolDragAndDrop::dropScript(LLViewerObject* hit_obj,              }          }          hit_obj->saveScript(new_script, active, true); -        gFloaterTools->dirty(); +        if (gFloaterTools) +        { +            gFloaterTools->dirty(); +        }          // VEFFECT: SetScript          LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, true); @@ -1844,7 +1849,10 @@ void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj,      effectp->setTargetObject(hit_obj);      effectp->setDuration(LL_HUD_DUR_SHORT);      effectp->setColor(LLColor4U(gAgent.getEffectColor())); -    gFloaterTools->dirty(); +    if (gFloaterTools) +    { +        gFloaterTools->dirty(); +    }  }  // accessor that looks at permissions, copyability, and names of diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp index b15bb5efd5..0d141d7545 100644 --- a/indra/newview/lltoolplacer.cpp +++ b/indra/newview/lltoolplacer.cpp @@ -527,7 +527,10 @@ bool LLToolPlacer::handleHover(S32 x, S32 y, MASK mask)  void LLToolPlacer::handleSelect()  { -    gFloaterTools->setStatusText("place"); +    if (gFloaterTools) +    { +        gFloaterTools->setStatusText("place"); +    }  }  void LLToolPlacer::handleDeselect() diff --git a/indra/newview/lltoolselectland.cpp b/indra/newview/lltoolselectland.cpp index 88553c7557..331581fd88 100644 --- a/indra/newview/lltoolselectland.cpp +++ b/indra/newview/lltoolselectland.cpp @@ -207,7 +207,10 @@ void LLToolSelectLand::render()  void LLToolSelectLand::handleSelect()  { -    gFloaterTools->setStatusText("selectland"); +    if (gFloaterTools) +    { +        gFloaterTools->setStatusText("selectland"); +    }  } diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index f0567b18c4..da7ad71336 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -407,10 +407,16 @@ void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, bool damp_w                  F32* vw = (F32*) vertex_weightsp.get();                  F32* cw = (F32*) clothing_weightsp.get(); -                S32 tc_size = (num_verts*2*sizeof(F32)+0xF) & ~0xF; -                LLVector4a::memcpyNonAliased16(tc, (F32*) mMesh->getTexCoords(), tc_size); -                S32 vw_size = (num_verts*sizeof(F32)+0xF) & ~0xF; -                LLVector4a::memcpyNonAliased16(vw, (F32*) mMesh->getWeights(), vw_size); +                //S32 tc_size = (num_verts*2*sizeof(F32)+0xF) & ~0xF; +                //LLVector4a::memcpyNonAliased16(tc, (F32*) mMesh->getTexCoords(), tc_size); +                //S32 vw_size = (num_verts*sizeof(F32)+0xF) & ~0xF; +                //LLVector4a::memcpyNonAliased16(vw, (F32*) mMesh->getWeights(), vw_size); + +                // Both allocated in LLPolyMeshSharedData::allocateVertexData(unsigned int) + +                memcpy(tc, mMesh->getTexCoords(), num_verts*2*sizeof(F32) ); +                memcpy(vw, mMesh->getWeights(), num_verts*sizeof(F32) ); +                  LLVector4a::memcpyNonAliased16(cw, (F32*) mMesh->getClothingWeights(), num_verts*4*sizeof(F32));              } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index d688d5930c..82a52e63f6 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1730,8 +1730,6 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_          std::string user_data_path_cache = gDirUtilp->getCacheDir(false);          user_data_path_cache += gDirUtilp->getDirDelimiter(); -        std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef_log.txt"); -          // See if the plugin executable exists          llstat s;          if(LLFile::stat(launcher_name, &s)) @@ -1748,6 +1746,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_          {              media_source = new LLPluginClassMedia(owner);              media_source->setSize(default_width, default_height); +            std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.log");              media_source->setUserDataPath(user_data_path_cache, gDirUtilp->getUserName(), user_data_path_cef_log);              media_source->setLanguageCode(LLUI::getLanguage());              media_source->setZoomFactor(zoom_factor); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index d92faf4d1b..1c9a892a4f 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -286,6 +286,8 @@ void force_error_software_exception();  void force_error_os_exception();  void force_error_driver_crash();  void force_error_coroutine_crash(); +void force_error_coroprocedure_crash(); +void force_error_work_queue_crash();  void force_error_thread_crash();  void handle_force_delete(); @@ -2634,6 +2636,24 @@ class LLAdvancedForceErrorCoroutineCrash : public view_listener_t      }  }; +class LLAdvancedForceErrorCoroprocedureCrash : public view_listener_t +{ +    bool handleEvent(const LLSD& userdata) +    { +        force_error_coroprocedure_crash(); +        return true; +    } +}; + +class LLAdvancedForceErrorWorkQueueCrash : public view_listener_t +{ +    bool handleEvent(const LLSD& userdata) +    { +        force_error_work_queue_crash(); +        return true; +    } +}; +  class LLAdvancedForceErrorThreadCrash : public view_listener_t  {      bool handleEvent(const LLSD& userdata) @@ -3157,7 +3177,11 @@ void handle_object_edit()      LLFloaterReg::showInstance("build");      LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); -    gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() ); + +    if (gFloaterTools) +    { +        gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() ); +    }      LLViewerJoystick::getInstance()->moveObjects(true);      LLViewerJoystick::getInstance()->setNeedsReset(true); @@ -8657,6 +8681,16 @@ void force_error_coroutine_crash()      LLAppViewer::instance()->forceErrorCoroutineCrash();  } +void force_error_coroprocedure_crash() +{ +    LLAppViewer::instance()->forceErrorCoroprocedureCrash(); +} + +void force_error_work_queue_crash() +{ +    LLAppViewer::instance()->forceErrorWorkQueueCrash(); +} +  void force_error_thread_crash()  {      LLAppViewer::instance()->forceErrorThreadCrash(); @@ -9861,6 +9895,8 @@ void initialize_menus()      view_listener_t::addMenu(new LLAdvancedForceErrorSoftwareExceptionCoro(), "Advanced.ForceErrorSoftwareExceptionCoro");      view_listener_t::addMenu(new LLAdvancedForceErrorDriverCrash(), "Advanced.ForceErrorDriverCrash");      view_listener_t::addMenu(new LLAdvancedForceErrorCoroutineCrash(), "Advanced.ForceErrorCoroutineCrash"); +    view_listener_t::addMenu(new LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash"); +    view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");      view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");      view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1d4828fd33..5fd820f91d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3147,7 +3147,9 @@ void send_agent_update(bool force_send, bool send_reliable)      LL_PROFILE_ZONE_SCOPED;      llassert(!gCubeSnapshot); -    if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) +    LLAgent::ETeleportState tp_state = gAgent.getTeleportState(); +    if (tp_state != LLAgent::TELEPORT_NONE +        && tp_state != LLAgent::TELEPORT_ARRIVING)      {          // We don't care if they want to send an agent update, they're not allowed          // until the target simulator is ready to receive them @@ -3322,7 +3324,36 @@ void send_agent_update(bool force_send, bool send_reliable)      msg->addVector3Fast(_PREHASH_CameraAtAxis, camera_at);      msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis());      msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis()); -    msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); + +    static F32 last_draw_disatance_step = 1024; +    if (tp_state == LLAgent::TELEPORT_ARRIVING || LLStartUp::getStartupState() < STATE_MISC) +    { +        // Inform interest list, prioritize closer area. +        // Reason: currently server doesn't distance sort attachments, by restricting range +        // we reduce the number of attachments sent to the viewer, thus prioritizing +        // closer ones. +        // Todo: revise and remove once server gets distance sorting. +        last_draw_disatance_step = llmax((F32)(gAgentCamera.mDrawDistance / 2.f), 50.f); +        msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step); +    } +    else if (last_draw_disatance_step < gAgentCamera.mDrawDistance) +    { +        static LLFrameTimer last_step_time; +        if (last_step_time.getElapsedTimeF32() > 1.f) +        { +            // gradually increase draw distance +            // Idealy this should be not per second, but based on how loaded +            // mesh thread is, but hopefully this is temporary. +            last_step_time.reset(); +            F32 step = gAgentCamera.mDrawDistance * 0.1f; +            last_draw_disatance_step = llmin(last_draw_disatance_step + step, gAgentCamera.mDrawDistance); +        } +        msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step); +    } +    else +    { +        msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); +    }      msg->addU32Fast(_PREHASH_ControlFlags, control_flags); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 86440fca48..030dfeaa6f 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1290,7 +1290,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  #endif                  //clear cost and linkset cost                  setObjectCostStale(); -                if (isSelected()) +                if (isSelected() && gFloaterTools)                  {                      gFloaterTools->dirty();                  } @@ -1729,7 +1729,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  #endif                  setObjectCostStale(); -                if (isSelected()) +                if (isSelected() && gFloaterTools)                  {                      gFloaterTools->dirty();                  } @@ -2917,6 +2917,17 @@ void LLViewerObject::fetchInventoryFromServer()          delete mInventory;          mInventory = NULL; +        // This will get reset by doInventoryCallback or processTaskInv +        mInvRequestState = INVENTORY_REQUEST_PENDING; + +        if (mRegionp && !mRegionp->getCapability("RequestTaskInventory").empty()) +        { +            LLCoros::instance().launch("LLViewerObject::fetchInventoryFromCapCoro()", +                                       boost::bind(&LLViewerObject::fetchInventoryFromCapCoro, mID)); +        } +        else +        { +            LL_WARNS() << "Using old task inventory path!" << LL_ENDL;          // Results in processTaskInv          LLMessageSystem* msg = gMessageSystem;          msg->newMessageFast(_PREHASH_RequestTaskInventory); @@ -2926,15 +2937,13 @@ void LLViewerObject::fetchInventoryFromServer()          msg->nextBlockFast(_PREHASH_InventoryData);          msg->addU32Fast(_PREHASH_LocalID, mLocalID);          msg->sendReliable(mRegionp->getHost()); - -        // This will get reset by doInventoryCallback or processTaskInv -        mInvRequestState = INVENTORY_REQUEST_PENDING; +        }      }  }  void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds)  { -    // unless already waiting, drop previous request and shedule an update +    // unless already waiting, drop previous request and schedule an update      if (mInvRequestState != INVENTORY_REQUEST_WAIT)      {          if (mInvRequestXFerId != 0) @@ -2965,6 +2974,80 @@ void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64      }  } +//static +void LLViewerObject::fetchInventoryFromCapCoro(const LLUUID task_inv) +{ +    LLViewerObject *obj = gObjectList.findObject(task_inv); +    if (obj) +    { +        LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); +        LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t +                                   httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TaskInventoryRequest", httpPolicy)); +        LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); +        std::string url = obj->mRegionp->getCapability("RequestTaskInventory") + "?task_id=" + obj->mID.asString(); +        // If we already have a copy of the inventory then add it so the server won't re-send something we already have. +        // We expect this case to crop up in the case of failed inventory mutations, but it might happen otherwise as well. +        if (obj->mInventorySerialNum && obj->mInventory) +            url += "&inventory_serial=" + std::to_string(obj->mInventorySerialNum); + +        obj->mInvRequestState = INVENTORY_XFER; +        LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + +        LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +        LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + +        // Object may have gone away while we were suspended, double-check that it still exists +        obj = gObjectList.findObject(task_inv); +        if (!obj) +        { +            LL_WARNS() << "Object " << task_inv << " went away while fetching inventory, dropping result" << LL_ENDL; +            return; +        } + +        bool potentially_stale = false; +        if (status) +        { +            // Dealing with inventory serials is kind of funky. They're monotonically increasing and 16 bits, +            // so we expect them to overflow, but we can use inv serial < expected serial as a signal that we may +            // have mutated the task inventory since we kicked off the request, and those mutations may have not +            // been taken into account yet. Of course, those mutations may have actually failed which would result +            // in the inv serial never increasing. +            // +            // When we detect this case, set the expected inv serial to the inventory serial we actually received +            // and kick off a re-request after a slight delay. +            S16 serial = (S16)result["inventory_serial"].asInteger(); +            potentially_stale = serial < obj->mExpectedInventorySerialNum; +            LL_INFOS() << "Inventory loaded for " << task_inv << LL_ENDL; +            obj->mInventorySerialNum = serial; +            obj->mExpectedInventorySerialNum = serial; +            obj->loadTaskInvLLSD(result); +        } +        else if (status.getType() == 304) +        { +            LL_INFOS() << "Inventory wasn't changed on server!" << LL_ENDL; +            obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; +            // Even though it wasn't necessary to send a response, we still may have mutated +            // the inventory since we kicked off the request, check for that case. +            potentially_stale = obj->mInventorySerialNum < obj->mExpectedInventorySerialNum; +            // Set this to what we already have so that we don't re-request a second time. +            obj->mExpectedInventorySerialNum = obj->mInventorySerialNum; +        } +        else +        { +            // Not sure that there's anything sensible we can do to recover here, retrying in a loop would be bad. +            LL_WARNS() << "Error status while requesting task inventory: " << status.toString() << LL_ENDL; +            obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; +        } + +        if (potentially_stale) +        { +            // Stale? I guess we can use what we got for now, but we'll have to re-request +            LL_WARNS() << "Stale inv_serial? Re-requesting." << LL_ENDL; +            obj->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED); +        } +    } +} +  LLControlAvatar *LLViewerObject::getControlAvatar()  {      return getRootEdit()->mControlAvatar.get(); @@ -3140,6 +3223,20 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)      S16 serial = 0;      msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial); +    if (object->mRegionp && !object->mRegionp->getCapability("RequestTaskInventory").empty()) +    { +        // It seems that simulator may ask us to re-download the task inventory if an update to the inventory +        // happened out-of-band while we had the object selected (like if a script is saved.) +        // +        // If we're meant to use the HTTP capability, ignore the contents of the UDP message and fetch the +        // inventory via the CAP so that we don't flow down the UDP inventory request path unconditionally here. +        // We shouldn't need to wait, as any updates should already be ready to fetch by this point. +        LL_INFOS() << "Handling unsolicited ReplyTaskInventory for " << task_id << LL_ENDL; +        object->mExpectedInventorySerialNum = serial; +        object->fetchInventoryFromServer(); +        return; +    } +      if (serial == object->mInventorySerialNum          && serial < object->mExpectedInventorySerialNum)      { @@ -3347,6 +3444,47 @@ bool LLViewerObject::loadTaskInvFile(const std::string& filename)      return true;  } +void LLViewerObject::loadTaskInvLLSD(const LLSD& inv_result) +{ +    if (inv_result.has("contents")) +    { +        if(mInventory) +        { +            mInventory->clear(); // will deref and delete it +        } +        else +        { +            mInventory = new LLInventoryObject::object_list_t; +        } + +        // Synthesize the "Contents" category, the viewer expects it, but it isn't sent. +        LLPointer<LLInventoryObject> inv = new LLInventoryObject(mID, LLUUID::null, LLAssetType::AT_CATEGORY, "Contents"); +        mInventory->push_front(inv); + +        const LLSD& inventory = inv_result["contents"]; +        for (const auto& inv_entry : llsd::inArray(inventory)) +        { +            if (inv_entry.has("item_id")) +            { +                LLPointer<LLViewerInventoryItem> inv = new LLViewerInventoryItem; +                inv->unpackMessage(inv_entry); +                mInventory->push_front(inv); +            } +            else +            { +                LL_WARNS_ONCE() << "Unknown inventory entry while reading from inventory file. Entry: '" +                                << inv_entry << "'" << LL_ENDL; +            } +        } +    } +    else +    { +        LL_WARNS() << "unable to load task inventory: " << inv_result << LL_ENDL; +        return; +    } +    doInventoryCallback(); +} +  void LLViewerObject::doInventoryCallback()  {      for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); @@ -3747,7 +3885,7 @@ void LLViewerObject::setObjectCost(F32 cost)      mObjectCost = cost;      mCostStale = false; -    if (isSelected()) +    if (isSelected() && gFloaterTools)      {          gFloaterTools->dirty();      } @@ -3767,7 +3905,7 @@ void LLViewerObject::setLinksetCost(F32 cost)          iter++;      } -    if (needs_refresh) +    if (needs_refresh && gFloaterTools)      {          gFloaterTools->dirty();      } @@ -3778,7 +3916,7 @@ void LLViewerObject::setPhysicsCost(F32 cost)      mPhysicsCost = cost;      mCostStale = false; -    if (isSelected()) +    if (isSelected() && gFloaterTools)      {          gFloaterTools->dirty();      } @@ -3789,7 +3927,7 @@ void LLViewerObject::setLinksetPhysicsCost(F32 cost)      mLinksetPhysicsCost = cost;      mCostStale = false; -    if (isSelected()) +    if (isSelected() && gFloaterTools)      {          gFloaterTools->dirty();      } diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 206840ebfb..63458e60ea 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -688,6 +688,7 @@ private:      // forms task inventory request after some time passed, marks request as pending      void fetchInventoryDelayed(const F64 &time_seconds);      static void fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds); +    static void fetchInventoryFromCapCoro(const LLUUID task_inv);  public:      // @@ -758,6 +759,7 @@ public:      // Associated GLTF Asset      std::shared_ptr<LL::GLTF::Asset> mGLTFAsset; +    bool mIsGLTFAssetMissing = false;      // Pipeline classes      LLPointer<LLDrawable> mDrawable; @@ -825,6 +827,7 @@ protected:      static void processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status);      bool loadTaskInvFile(const std::string& filename); +    void loadTaskInvLLSD(const LLSD &inv_result);      void doInventoryCallback();      bool isOnMap(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index c21aeb1d57..697433148b 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3239,6 +3239,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)      capabilityNames.append("FetchInventory2");      capabilityNames.append("FetchInventoryDescendents2");      capabilityNames.append("IncrementCOFVersion"); +    capabilityNames.append("RequestTaskInventory");      AISAPI::getCapNames(capabilityNames);      capabilityNames.append("InterestList"); diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index ac4519e593..a0a9906724 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -114,7 +114,6 @@ LLGLSLShader        gObjectAlphaMaskNoColorProgram;  //environment shaders  LLGLSLShader        gWaterProgram; -LLGLSLShader        gWaterEdgeProgram;  LLGLSLShader        gUnderWaterProgram;  //interface shaders @@ -410,7 +409,6 @@ void LLViewerShaderMgr::finalizeShaderList()      //ONLY shaders that need WL Param management should be added here      mShaderList.push_back(&gAvatarProgram);      mShaderList.push_back(&gWaterProgram); -    mShaderList.push_back(&gWaterEdgeProgram);      mShaderList.push_back(&gAvatarEyeballProgram);      mShaderList.push_back(&gImpostorProgram);      mShaderList.push_back(&gObjectBumpProgram); @@ -877,6 +875,7 @@ std::string LLViewerShaderMgr::loadBasicShaders()      index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/shadowUtil.glsl",                      1) );      index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/aoUtil.glsl",                          1) );      index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/pbrterrainUtilF.glsl",                 1) ); +    index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/tonemapUtilF.glsl",                    1) );      index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/reflectionProbeF.glsl",                has_reflection_probes ? 3 : 2) );      index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/screenSpaceReflUtil.glsl",             ssr ? 3 : 1) );      index_channels.push_back(-1);    shaders.push_back( make_pair( "lighting/lightNonIndexedF.glsl",                    mShaderLevel[SHADER_LIGHTING] ) ); @@ -909,7 +908,6 @@ bool LLViewerShaderMgr::loadShadersWater()      if (mShaderLevel[SHADER_WATER] == 0)      {          gWaterProgram.unload(); -        gWaterEdgeProgram.unload();          gUnderWaterProgram.unload();          return true;      } @@ -923,6 +921,7 @@ bool LLViewerShaderMgr::loadShadersWater()          gWaterProgram.mFeatures.hasGamma = true;          gWaterProgram.mFeatures.hasSrgb = true;          gWaterProgram.mFeatures.hasReflectionProbes = true; +        gWaterProgram.mFeatures.hasTonemap = true;          gWaterProgram.mFeatures.hasShadows = use_sun_shadow;          gWaterProgram.mShaderFiles.clear();          gWaterProgram.mShaderFiles.push_back(make_pair("environment/waterV.glsl", GL_VERTEX_SHADER)); @@ -946,36 +945,6 @@ bool LLViewerShaderMgr::loadShadersWater()      if (success)      { -    // load water shader -        gWaterEdgeProgram.mName = "Water Edge Shader"; -        gWaterEdgeProgram.mFeatures.calculatesAtmospherics = true; -        gWaterEdgeProgram.mFeatures.hasAtmospherics = true; -        gWaterEdgeProgram.mFeatures.hasGamma = true; -        gWaterEdgeProgram.mFeatures.hasSrgb = true; -        gWaterEdgeProgram.mFeatures.hasReflectionProbes = true; -        gWaterEdgeProgram.mFeatures.hasShadows = use_sun_shadow; -        gWaterEdgeProgram.mShaderFiles.clear(); -        gWaterEdgeProgram.mShaderFiles.push_back(make_pair("environment/waterV.glsl", GL_VERTEX_SHADER)); -        gWaterEdgeProgram.mShaderFiles.push_back(make_pair("environment/waterF.glsl", GL_FRAGMENT_SHADER)); -        gWaterEdgeProgram.clearPermutations(); -        gWaterEdgeProgram.addPermutation("WATER_EDGE", "1"); -        if (LLPipeline::sRenderTransparentWater) -        { -            gWaterEdgeProgram.addPermutation("TRANSPARENT_WATER", "1"); -        } - -        if (use_sun_shadow) -        { -            gWaterEdgeProgram.addPermutation("HAS_SUN_SHADOW", "1"); -        } -        gWaterEdgeProgram.mShaderGroup = LLGLSLShader::SG_WATER; -        gWaterEdgeProgram.mShaderLevel = mShaderLevel[SHADER_WATER]; -        success = gWaterEdgeProgram.createShader(); -        llassert(success); -    } - -    if (success) -    {          //load under water vertex shader          gUnderWaterProgram.mName = "Underwater Shader";          gUnderWaterProgram.mFeatures.calculatesAtmospherics = true; @@ -2484,6 +2453,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()          gDeferredPostTonemapProgram.mName = "Deferred Tonemap Post Process";          gDeferredPostTonemapProgram.mFeatures.hasSrgb = true;          gDeferredPostTonemapProgram.mFeatures.isDeferred = true; +        gDeferredPostTonemapProgram.mFeatures.hasTonemap = true;          gDeferredPostTonemapProgram.mShaderFiles.clear();          gDeferredPostTonemapProgram.clearPermutations();          gDeferredPostTonemapProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); @@ -2498,6 +2468,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()          gNoPostTonemapProgram.mName = "No Post Tonemap Post Process";          gNoPostTonemapProgram.mFeatures.hasSrgb = true;          gNoPostTonemapProgram.mFeatures.isDeferred = true; +        gNoPostTonemapProgram.mFeatures.hasTonemap = true;          gNoPostTonemapProgram.mShaderFiles.clear();          gNoPostTonemapProgram.clearPermutations();          gNoPostTonemapProgram.addPermutation("NO_POST", "1"); diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index 6326de9a6b..7ad2da9464 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -192,7 +192,6 @@ extern LLGLSLShader     gObjectAlphaMaskNoColorProgram;  //environment shaders  extern LLGLSLShader         gWaterProgram; -extern LLGLSLShader         gWaterEdgeProgram;  extern LLGLSLShader         gUnderWaterProgram;  extern LLGLSLShader         gGlowProgram;  extern LLGLSLShader         gGlowExtractProgram; diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 609ad38e96..a0723db479 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -489,7 +489,12 @@ void LLViewerTexture::updateClass()      }      LLViewerMediaTexture::updateClass(); - +    // This is a divisor used to determine how much VRAM from our overall VRAM budget to use. +    // This is **cumulative** on whatever the detected or manually set VRAM budget is. +    // If we detect 2048MB of VRAM, this will, by default, only use 1024. +    // If you set 1024MB of VRAM, this will, by default, use 512. +    // -Geenz 2025-03-03 +    static LLCachedControl<U32> tex_vram_divisor(gSavedSettings, "RenderTextureVRAMDivisor", 2);      static LLCachedControl<U32> max_vram_budget(gSavedSettings, "RenderMaxVRAMBudget", 0);      F64 texture_bytes_alloc = LLImageGL::getTextureBytesAllocated() / 1024.0 / 512.0; @@ -500,6 +505,7 @@ void LLViewerTexture::updateClass()      F32 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc);      F32 budget = max_vram_budget == 0 ? (F32)gGLManager.mVRAM : (F32)max_vram_budget; +    budget /= tex_vram_divisor;      // Try to leave at least half a GB for everyone else and for bias,      // but keep at least 768MB for ourselves diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 0c186c0ed2..ae723b4068 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -30,6 +30,7 @@  #include "llviewertexturelist.h" +#include "llagent.h"  #include "llgl.h" // fot gathering stats from GL  #include "llimagegl.h"  #include "llimagebmp.h" @@ -815,10 +816,19 @@ void LLViewerTextureList::updateImages(F32 max_time)              clearFetchingRequests();              gPipeline.clearRebuildGroups();              cleared = true; +            return;          } -        return; +        // ARRIVING is a delay to let things decode, cache and process, +        // so process textures like normal despite gTeleportDisplay +        if (gAgent.getTeleportState() != LLAgent::TELEPORT_ARRIVING) +        { +            return; +        } +    } +    else +    { +        cleared = false;      } -    cleared = false;      LLAppViewer::getTextureFetch()->setTextureBandwidth((F32)LLTrace::get_frame_recording().getPeriodMeanPerSec(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED).value()); @@ -891,7 +901,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag      if (imagep->getBoostLevel() < LLViewerFetchedTexture::BOOST_HIGH)  // don't bother checking face list for boosted textures      { -        static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.04f); +        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; @@ -900,7 +910,8 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag          U32 face_count = 0;          // get adjusted bias based on image resolution -        F32 max_discard = F32(imagep->getMaxDiscardLevel()); +        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 @@ -933,8 +944,9 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag                      // Scale desired texture resolution higher or lower depending on texture scale                      // -                    // Minimum usage examples: a 1024x1024 texture with aplhabet, runing string -                    // shows one letter at a time +                    // 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 @@ -1081,7 +1093,8 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)          imagep->mCreatePending = false;          mCreateTextureList.pop(); -        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, diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index b8404ec249..bae7518e66 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -66,6 +66,7 @@  #include "llchatentry.h"  #include "indra_constants.h"  #include "llassetstorage.h" +#include "lldate.h"  #include "llerrorcontrol.h"  #include "llfontgl.h"  #include "llmousehandler.h" @@ -79,14 +80,12 @@  #include "message.h"  #include "object_flags.h"  #include "lltimer.h" -#include "llviewermenu.h"  #include "lltooltip.h"  #include "llmediaentry.h"  #include "llurldispatcher.h"  #include "raytrace.h"  // newview includes -#include "llagent.h"  #include "llbox.h"  #include "llchicletbar.h"  #include "llconsole.h" @@ -118,7 +117,6 @@  #include "llfontfreetype.h"  #include "llgesturemgr.h"  #include "llglheaders.h" -#include "lltooltip.h"  #include "llhudmanager.h"  #include "llhudobject.h"  #include "llhudview.h" @@ -133,7 +131,6 @@  #include "llmorphview.h"  #include "llmoveview.h"  #include "llnavigationbar.h" -#include "llnotificationhandler.h"  #include "llpaneltopinfobar.h"  #include "llpopupview.h"  #include "llpreviewtexture.h" @@ -166,17 +163,13 @@  #include "lltoolselectland.h"  #include "lltrans.h"  #include "lluictrlfactory.h" -#include "llurldispatcher.h"        // SLURL from other app instance  #include "llversioninfo.h"  #include "llvieweraudio.h" -#include "llviewercamera.h"  #include "llviewergesture.h"  #include "llviewertexturelist.h"  #include "llviewerinventory.h" -#include "llviewerinput.h"  #include "llviewermedia.h"  #include "llviewermediafocus.h" -#include "llviewermenu.h"  #include "llviewermessage.h"  #include "llviewerobjectlist.h"  #include "llviewerparcelmgr.h" @@ -210,7 +203,6 @@  #include "llwindowlistener.h"  #include "llviewerwindowlistener.h" -#include "llpaneltopinfobar.h"  #include "llcleanup.h"  #if LL_WINDOWS @@ -4806,26 +4798,23 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save      }      // Look for an unused file name -    bool is_snapshot_name_loc_set = isSnapshotLocSet(); +    auto is_snapshot_name_loc_set = isSnapshotLocSet();      std::string filepath; -    S32 i = 1; -    S32 err = 0; -    std::string extension("." + image->getExtension()); +    auto i = 1; +    auto err = 0; +    auto extension("." + image->getExtension()); +    auto now = LLDate::now();      do      {          filepath = sSnapshotDir;          filepath += gDirUtilp->getDirDelimiter();          filepath += sSnapshotBaseName; - -        if (is_snapshot_name_loc_set) -        { -            filepath += llformat("_%.3d",i); -        } - +        filepath += now.toLocalDateString("_%Y-%m-%d_%H%M%S"); +        filepath += llformat("%.2d", i);          filepath += extension;          llstat stat_info; -        err = LLFile::stat( filepath, &stat_info ); +        err = LLFile::stat(filepath, &stat_info);          i++;      }      while( -1 != err  // Search until the file is not found (i.e., stat() gives an error). @@ -6178,7 +6167,7 @@ void LLPickInfo::fetchResults()              mObjectOffset = gAgentCamera.calcFocusOffset(objectp, v_intersection, mPickPt.mX, mPickPt.mY);              mObjectID = objectp->mID;              mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset; - +            mPickHUD = objectp->isHUDAttachment();              mPosGlobal = gAgent.getPosGlobalFromAgent(v_intersection); diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index fbc2c58fbf..ac0dfa3fe4 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -133,6 +133,7 @@ public:      bool            mPickParticle;      bool            mPickUnselectable;      bool            mPickReflectionProbe = false; +    bool            mPickHUD{ false };      void            getSurfaceInfo();  private: diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 7bf9c88b99..40312b7f4e 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -682,7 +682,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,      mFullyLoaded(false),      mPreviousFullyLoaded(false),      mFullyLoadedInitialized(false), -    mLastCloudAttachmentCount(0), +    mLastCloudAttachmentCount(-1),      mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN),      mLoadedCallbacksPaused(false),      mLoadedCallbackTextures(0), @@ -2794,7 +2794,10 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)      }      // attach objects that were waiting for a drawable -    lazyAttach(); +    if (!mPendingAttachment.empty()) +    { +        lazyAttach(); +    }      // animate the character      // store off last frame's root position to be consistent with camera position @@ -3226,6 +3229,7 @@ void LLVOAvatar::idleUpdateLoadingEffect()              if (mFirstFullyVisible)              {                  mFirstFullyVisible = false; +                mLastCloudAttachmentCount = (S32)mSimAttachments.size();                  mFirstDecloudTime = mFirstAppearanceMessageTimer.getElapsedTimeF32();                  if (isSelf())                  { @@ -7710,6 +7714,64 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )      }  } +bool check_object_for_mesh_loading(LLViewerObject* objectp) +{ +    if (!objectp || !objectp->getVolume()) +    { +        return false; +    } +    LLVolume* volp = objectp->getVolume(); +    const LLUUID& mesh_id = volp->getParams().getSculptID(); +    if (mesh_id.isNull()) +    { +        // No mesh nor skin info needed +        return false; +    } + +    if (volp->isMeshAssetUnavaliable()) +    { +        // Mesh failed to load, do not expect it +        return false; +    } + +    if (!objectp->mDrawable) +    { +        return false; +    } + +    LLVOVolume* pvobj = objectp->mDrawable->getVOVolume(); +    if (pvobj) +    { +        if (!pvobj->isMesh()) +        { +            // Not a mesh +            return false; +        } + +        if (!volp->isMeshAssetLoaded()) +        { +            // Waiting for mesh +            return true; +        } + +        const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo(); +        if (skin_data) +        { +            // Skin info present, done +            return false; +        } + +        if (pvobj->isSkinInfoUnavaliable()) +        { +            // Load failed or info not present, don't expect it +            return false; +        } +    } + +    // object is not ready +    return true; +} +  bool LLVOAvatar::hasPendingAttachedMeshes()  {      for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); @@ -7724,62 +7786,20 @@ bool LLVOAvatar::hasPendingAttachedMeshes()                   ++attachment_iter)              {                  LLViewerObject* objectp = attachment_iter->get(); -                if (objectp) +                if (objectp && !objectp->isDead())                  { +                    if (check_object_for_mesh_loading(objectp)) +                    { +                        return true; +                    }                      LLViewerObject::const_child_list_t& child_list = objectp->getChildren();                      for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin();                           iter1 != child_list.end(); ++iter1)                      {                          LLViewerObject* objectchild = *iter1; -                        if (objectchild && objectchild->getVolume()) +                        if (check_object_for_mesh_loading(objectchild))                          { -                            const LLUUID& mesh_id = objectchild->getVolume()->getParams().getSculptID(); -                            if (mesh_id.isNull()) -                            { -                                // No mesh nor skin info needed -                                continue; -                            } - -                            if (objectchild->getVolume()->isMeshAssetUnavaliable()) -                            { -                                // Mesh failed to load, do not expect it -                                continue; -                            } - -                            if (objectchild->mDrawable) -                            { -                                LLVOVolume* pvobj = objectchild->mDrawable->getVOVolume(); -                                if (pvobj) -                                { -                                    if (!pvobj->isMesh()) -                                    { -                                        // Not a mesh -                                        continue; -                                    } - -                                    if (!objectchild->getVolume()->isMeshAssetLoaded()) -                                    { -                                        // Waiting for mesh -                                        return true; -                                    } - -                                    const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo(); -                                    if (skin_data) -                                    { -                                        // Skin info present, done -                                        continue; -                                    } - -                                    if (pvobj->isSkinInfoUnavaliable()) -                                    { -                                        // Load failed or info not present, don't expect it -                                        continue; -                                    } -                                } - -                                // objectchild is not ready -                                return true; -                            } +                            return true;                          }                      }                  } @@ -8382,7 +8402,7 @@ bool LLVOAvatar::updateIsFullyLoaded()                    );          // compare amount of attachments to one reported by simulator -        if (!loading && !isSelf() && rez_status < 4 && mLastCloudAttachmentCount < mSimAttachments.size()) +        if (!isSelf() && mLastCloudAttachmentCount < mSimAttachments.size() && mSimAttachments.size() > 0)          {              S32 attachment_count = getAttachmentCount();              if (mLastCloudAttachmentCount != attachment_count) @@ -8400,6 +8420,11 @@ bool LLVOAvatar::updateIsFullyLoaded()                  // waiting                  loading = true;              } +            else if (!loading) +            { +                // for hasFirstFullAttachmentData +                mLastCloudAttachmentCount = (S32)mSimAttachments.size(); +            }          }      }      updateRezzedStatusTimers(rez_status); @@ -8513,6 +8538,12 @@ bool LLVOAvatar::isFullyLoaded() const      return (mRenderUnloadedAvatar || mFullyLoaded);  } +bool LLVOAvatar::hasFirstFullAttachmentData() const +{ +    return !mFirstFullyVisible // Avatar is fully visible, have all data +        || mLastCloudAttachmentCount >= (S32)mSimAttachments.size(); +} +  bool LLVOAvatar::isTooComplex() const  {      bool too_complex; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index dd1725c322..263c3dadf6 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -385,6 +385,7 @@ public:      //--------------------------------------------------------------------  public:      bool            isFullyLoaded() const; +    bool            hasFirstFullAttachmentData() const;      F32             getFirstDecloudTime() const {return mFirstDecloudTime;}      // check and return current state relative to limits diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 7d6ad743bd..6d29038a23 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1014,6 +1014,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()              std::string old_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SLVoice.old");              if (gDirUtilp->fileExists(new_log))              { +                LLFile::remove(old_log, ENOENT);                  LLFile::rename(new_log, old_log);              } @@ -2004,7 +2005,7 @@ bool LLVivoxVoiceClient::waitForChannel()              {                  recordingAndPlaybackMode();              } -            else if (mProcessChannels && (mNextAudioSession == NULL) && checkParcelChanged()) +            else if (mProcessChannels && ((mNextAudioSession == NULL) || checkParcelChanged()))              {                  // the parcel is changed, or we have no pending audio sessions,                  // so try to request the parcel voice info diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp index ab8d0d2564..38c702c4ca 100644 --- a/indra/newview/llvosky.cpp +++ b/indra/newview/llvosky.cpp @@ -100,8 +100,16 @@ LLSkyTex::LLSkyTex() :  void LLSkyTex::init(bool isShiny)  {      mIsShiny = isShiny; -    mSkyData = new LLColor4[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)]; -    mSkyDirs = new LLVector3[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)]; +    try +    { +        mSkyData = new LLColor4[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)]; +        mSkyDirs = new LLVector3[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)]; +    } +    catch (std::bad_alloc&) +    { +        LLError::LLUserWarningMsg::showOutOfMemory(); +        LL_ERRS() << "Failed to allocate memory for sky texture data" << LL_ENDL; +    }      for (S32 i = 0; i < 2; ++i)      { diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index e0d8c25731..899733ccc3 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -838,6 +838,7 @@ void LLWorld::printPacketsLost()                      << " packets lost: " << cdp->getPacketsLost() << LL_ENDL;          }      } +    LL_INFOS() << "Packets dropped by Packet Ring: " << gMessageSystem->mPacketRing.getNumDroppedPackets() << LL_ENDL;  }  void LLWorld::processCoarseUpdate(LLMessageSystem* msg, void** user_data) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 4a4b938152..46fbd16188 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1412,7 +1412,18 @@ void LLPipeline::createLUTBuffers()      {          U32 lightResX = gSavedSettings.getU32("RenderSpecularResX");          U32 lightResY = gSavedSettings.getU32("RenderSpecularResY"); -        F32* ls = new F32[lightResX*lightResY]; +        F32* ls = nullptr; +        try +        { +            ls = new F32[lightResX*lightResY]; +        } +        catch (std::bad_alloc&) +        { +            LLError::LLUserWarningMsg::showOutOfMemory(); +            // might be better to set the error into mFatalMessage and rethrow +            LL_ERRS() << "Bad memory allocation in createLUTBuffers! lightResX: " +                << lightResX << " lightResY: " << lightResY << LL_ENDL; +        }          F32 specExp = gSavedSettings.getF32("RenderSpecularExponent");          // Calculate the (normalized) blinn-phong specular lookup texture. (with a few tweaks)          for (U32 y = 0; y < lightResY; ++y) @@ -7200,7 +7211,7 @@ void LLPipeline::tonemap(LLRenderTarget* src, LLRenderTarget* dst)          LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); -        bool no_post = gSnapshotNoPost || psky->getReflectionProbeAmbiance(should_auto_adjust) == 0.f || (buildNoPost && gFloaterTools->isAvailable()); +        bool no_post = gSnapshotNoPost || psky->getReflectionProbeAmbiance(should_auto_adjust) == 0.f || (buildNoPost && gFloaterTools && gFloaterTools->isAvailable());          LLGLSLShader& shader = no_post ? gNoPostTonemapProgram : gDeferredPostTonemapProgram;          shader.bind(); diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 92f63a1820..e17321e698 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -759,6 +759,9 @@       name="SelectedOutfitTextColor"       reference="EmphasisColor" />      <color +     name="SearchableControlHighlightColor" +     value="0.5 0.1 0.1 1" /> +    <color       name="SilhouetteChildColor"       value="0.13 0.42 0.77 1" />      <color diff --git a/indra/newview/skins/default/xui/da/floater_avatar_textures.xml b/indra/newview/skins/default/xui/da/floater_avatar_textures.xml index d0d766eaab..e7fe66c73f 100644 --- a/indra/newview/skins/default/xui/da/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/da/floater_avatar_textures.xml @@ -14,7 +14,7 @@ teksturer  teksturer  			</text>  			<button label="Vis IDs på skærm" label_selected="Dump" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Hår" name="hair-baked"/>  				<texture_picker label="Hår" name="hair_grain"/>  				<texture_picker label="Alpha - hår" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/de/floater_avatar_textures.xml b/indra/newview/skins/default/xui/de/floater_avatar_textures.xml index aed9aa6e78..915ef3067f 100644 --- a/indra/newview/skins/default/xui/de/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/de/floater_avatar_textures.xml @@ -8,7 +8,7 @@ Texturen</text>  			<text name="composite_label">Zusammengesetzte  Texturen</text>  			<button label="IDs an Konsole ausgeben" label_selected="Abladen" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Haare" name="hair-baked"/>  				<texture_picker label="Haare" name="hair_grain"/>  				<texture_picker label="Alpha: Haare" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index a9e7626dc5..486d604e9f 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -31,7 +31,6 @@ Sichtweite: [DRAW_DISTANCE] m  Bandbreite: [NET_BANDWITH] kbit/s  LOD-Faktor: [LOD_FACTOR]  Darstellungsqualität: [RENDER_QUALITY] -Erweitertes Beleuchtungsmodell: [GPU_SHADERS]  Texturspeicher: [TEXTURE_MEMORY] MB</string>  	<string name="AboutOSXHiDPI">HiDPI-Anzeigemodus: [HIDPI]</string>  	<string name="AboutLibs">J2C-Decoderversion: [J2C_VERSION] diff --git a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml index 09026884b9..831368f4de 100644 --- a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml @@ -70,13 +70,13 @@ Textures       width="150" />      <panel -     name="scroll_content_panel" +     name="scroll_content_panel2"       follows="left|top"       min_height="300"       layout="topleft" -     top="43" +     top="60"       background_visible="false" -     height="930" +     height="1113"       left="0"       width="1230"> @@ -86,7 +86,7 @@ Textures       layout="topleft"       left="10"       name="hair-baked" -     top="17" +     top="3"       width="92" />      <texture_picker       height="103" diff --git a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml index e4b8f13df7..f642ca93b7 100644 --- a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml +++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml @@ -13,7 +13,6 @@      chrome="true"      height="350"      width="304"> -  <floater.string name="title_for_recently_used" value="Recently used"/>    <floater.string name="title_for_frequently_used" value="Frequently used"/>    <floater.string name="text_no_emoji_for_filter" value="No emoji found for '[FILTER]'"/>    <scroll_container diff --git a/indra/newview/skins/default/xui/en/floater_fast_timers.xml b/indra/newview/skins/default/xui/en/floater_fast_timers.xml index 00411ba20b..ae907bcb5f 100644 --- a/indra/newview/skins/default/xui/en/floater_fast_timers.xml +++ b/indra/newview/skins/default/xui/en/floater_fast_timers.xml @@ -75,7 +75,7 @@          step_size="16"          doc_pos="0"          doc_size="3000" -        page_size="0" +        page_size="50"            />      </layout_panel>      <layout_panel name="timers_panel" diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index 96de9e61cb..34ed3fa80d 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -257,7 +257,7 @@      height="16"      increment="1"      initial_value="12" -    label="Max. # of non-impostors:" +    label="Max. # animated avatars:"      label_width="185"      layout="topleft"      left="30" @@ -859,6 +859,23 @@    </combo_box>    <slider +    control_name="RenderReflectionProbeCount" +    decimal_digits="0" +    follows="left|top" +    height="16" +    increment="1" +    initial_value="256" +    label="Max. Reflection Probes:" +    label_width="145" +    layout="topleft" +    left="420" +    min_val="1" +    max_val="256" +    name="MaxProbes" +    top_delta="24" +    width="260" /> + +  <slider      control_name="RenderExposure"      decimal_digits="1"      follows="left|top" diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index b0b818cde5..5ab0177de6 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -37,7 +37,8 @@     top="16"     left="0"     right="-1" -   bottom="-1"> +   bottom="-1" +   orientation="horizontal">      <layout_panel       name="map_lp"       width="385" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 0e70e75c0b..6fb92f7b99 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2788,7 +2788,7 @@ function="World.EnvPreset"                   function="Advanced.ForceErrorOSException" />                  <menu_item_call.on_visible                   function="Advanced.EnableErrorOSException" /> -            </menu_item_call>             +            </menu_item_call>              <menu_item_call               label="Force a Crash in a Coroutine"               name="Force a Crash in a Coroutine"> @@ -2796,6 +2796,18 @@ function="World.EnvPreset"                   function="Advanced.ForceErrorCoroutineCrash" />              </menu_item_call>              <menu_item_call +             label="Force a Crash in a Coroprocedure" +             name="Force a Crash in a Coroprocedure"> +                <menu_item_call.on_click +                 function="Advanced.ForceErrorCoroprocedureCrash" /> +            </menu_item_call> +            <menu_item_call +             label="Force a Crash in a Work Queue" +             name="Force a Crash in a Work Queue"> +                <menu_item_call.on_click +                 function="Advanced.ForceErrorWorkQueueCrash" /> +            </menu_item_call> +            <menu_item_call               label="Force a Crash in a Thread"               name="Force a Crash in a Thread">                  <menu_item_call.on_click @@ -4202,16 +4214,6 @@ function="World.EnvPreset"          <menu_item_separator/> -        <menu_item_check -         label="HTTP Textures" -         name="HTTP Textures"> -            <menu_item_check.on_check -             function="CheckControl" -             parameter="ImagePipelineUseHTTP" /> -            <menu_item_check.on_click -             function="ToggleControl" -             parameter="ImagePipelineUseHTTP" /> -        </menu_item_check>          <menu_item_call           label="Compress Images"           name="Compress Images"> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index b5f742e5fb..dba48012ac 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -12580,4 +12580,16 @@ are wearing now.         name="okbutton"         yestext="OK"/>    </notification> + +  <notification +   icon="alertmodal.tga" +   name="WaterExclusionSurfacesWarning" +   type="alertmodal"> +    Checking the hide water box will overwrite the texture, bumpiness, and shininess choices. +    <tag>confirm</tag> +    <usetemplate +      name="okcancelbuttons" +      notext="Cancel" +      yestext="Continue"/> +  </notification>  </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_performance_preferences.xml b/indra/newview/skins/default/xui/en/panel_performance_preferences.xml index 7ba7b1d579..5031784644 100644 --- a/indra/newview/skins/default/xui/en/panel_performance_preferences.xml +++ b/indra/newview/skins/default/xui/en/panel_performance_preferences.xml @@ -427,6 +427,16 @@     layout="topleft"     top="80"     left="213" +   name="0_lbl" +   width="7"> +    0 +  </text> +  <text +   follows="left|top" +   font="SansSerifSmall" +   height="18" +   layout="topleft" +   left_pad="31"     name="1_lbl"     width="7">      1 @@ -436,7 +446,7 @@     font="SansSerifSmall"     height="18"     layout="topleft" -   left_pad="31" +   left_pad="30"     name="2_lbl"     width="7">      2 @@ -479,16 +489,6 @@     left_pad="30"     name="6_lbl"     width="7"> -      6 -  </text> -  <text -   follows="left|top" -   font="SansSerifSmall" -   height="18" -   layout="topleft" -   left_pad="30" -   name="7_lbl" -   width="7"> -    7 +    6    </text>  </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index 248cb04e6f..7481e10ed2 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -210,26 +210,60 @@      increment="8"      initial_value="160"      label="Draw distance:" -    label_width="90" +    label_width="187"      layout="topleft"      left="30"      min_val="64"      max_val="512"      name="DrawDistance"      top_delta="40" -    width="330" /> +    width="427" />    <text      type="string"      length="1"      follows="left|top"      height="12"      layout="topleft" -    left_delta="330" +    left_delta="427"      name="DrawDistanceMeterText2"      top_delta="0"      width="128">       m    </text> +  <slider +    control_name="IndirectMaxNonImpostors" +    name="IndirectMaxNonImpostors" +    decimal_digits="0" +    increment="1" +    initial_value="12" +    show_text="false" +    min_val="1" +    max_val="66" +    label="Maximum number of animated avatars:" +    follows="left|top" +    layout="topleft" +    height="16" +    label_width="240" +    left="30" +    top_delta="40" +    width="393"> +      <slider.commit_callback +        function="Pref.UpdateIndirectMaxNonImpostors" +        parameter="IndirectNonImpostorsText" /> +  </slider> +  <text +    type="string" +    length="1" +    follows="left|top" +    height="16" +    layout="topleft" +    top_delta="0" +    left_delta="397" +    text_readonly_color="LabelDisabledColor" +    name="IndirectMaxNonImpostorsText" +    width="65"> +      0 +  </text>    <button    height="23" diff --git a/indra/newview/skins/default/xui/en/panel_region_environment.xml b/indra/newview/skins/default/xui/en/panel_region_environment.xml index 6d23592948..6531233696 100644 --- a/indra/newview/skins/default/xui/en/panel_region_environment.xml +++ b/indra/newview/skins/default/xui/en/panel_region_environment.xml @@ -264,8 +264,7 @@                                  top_pad="1"                                  halign="right"                                  name="txt_alt1"> -                            Sky [INDEX] -                            [ALTITUDE]m +                            Sky [INDEX]
[ALTITUDE]m                          </text>                          <line_editor                                  follows="left|top" @@ -310,8 +309,7 @@                                  top_pad="1"                                  halign="right"                                  name="txt_alt2"> -                            Sky [INDEX] -                            [ALTITUDE]m +                            Sky [INDEX]
[ALTITUDE]m                          </text>                          <line_editor                                  follows="left|top" @@ -356,8 +354,7 @@                                  top_pad="1"                                  halign="right"                                  name="txt_alt3"> -                            Sky [INDEX] -                            [ALTITUDE]m +                            Sky [INDEX]
[ALTITUDE]m                          </text>                          <line_editor                                  follows="left|top" diff --git a/indra/newview/skins/default/xui/en/panel_tools_texture.xml b/indra/newview/skins/default/xui/en/panel_tools_texture.xml index e80f315259..9a19d06432 100644 --- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml +++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml @@ -123,6 +123,14 @@               name="checkbox fullbright"               top_pad="4"               width="81" /> +            <check_box +             height="19" +             label="Hide water" +             layout="topleft" +             left="172" +             top_delta="0" +             name="checkbox_hide_water" +             width="81" />              <view_border               bevel_style="none"               follows="top|left" diff --git a/indra/newview/skins/default/xui/es/floater_avatar_textures.xml b/indra/newview/skins/default/xui/es/floater_avatar_textures.xml index 0a52a70ff1..b043662975 100644 --- a/indra/newview/skins/default/xui/es/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/es/floater_avatar_textures.xml @@ -6,7 +6,7 @@  			<text name="label">Texturas obtenidas mediante bake</text>  			<text name="composite_label">Texturas compuestas</text>  			<button label="Volcar IDs a la consola" label_selected="Volcado" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Pelo" name="hair-baked"/>  				<texture_picker label="Pelo" name="hair_grain"/>  				<texture_picker label="Alfa del pelo" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index cd8e7687ae..9fcfc2daa5 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -29,7 +29,6 @@ Distancia de dibujo: [DRAW_DISTANCE]m  Ancho de banda: [NET_BANDWITH]kbit/s  Factor LOD: [LOD_FACTOR]  Calidad de renderización: [RENDER_QUALITY] -Modelo de iluminación avanzado: [GPU_SHADERS]  Memoria de textura: [TEXTURE_MEMORY]MB</string>  	<string name="AboutOSXHiDPI">Modo de visualización HiDPi: [HIDPI]</string>  	<string name="AboutLibs">Versión de descodificador J2C: [J2C_VERSION]  diff --git a/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml b/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml index a1f6d85836..e910632e50 100644 --- a/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/fr/floater_avatar_textures.xml @@ -6,7 +6,7 @@  			<text name="label">Textures figées</text>  			<text name="composite_label">Textures composites</text>  			<button label="Vider les ID dans la console" label_selected="Vider" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Cheveux" name="hair-baked"/>  				<texture_picker label="Cheveux" name="hair_grain"/>  				<texture_picker label="Alpha cheveux" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 0a3fbeb603..55f6209fe1 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -32,7 +32,6 @@ Distance de dessin : [DRAW_DISTANCE]m  Bande passante : [NET_BANDWITH] kbit/s  Facteur LOD (niveau de détail) : [LOD_FACTOR]  Qualité de rendu : [RENDER_QUALITY] -Modèle d’éclairage avancé : [GPU_SHADERS]  Mémoire textures : [TEXTURE_MEMORY] Mo</string>  	<string name="AboutOSXHiDPI">Mode d'affichage HiDPI : [HIDPI]</string>  	<string name="AboutLibs">J2C Decoder Version: [J2C_VERSION]  diff --git a/indra/newview/skins/default/xui/it/floater_avatar_textures.xml b/indra/newview/skins/default/xui/it/floater_avatar_textures.xml index 288b0a2fea..c0e56805f3 100644 --- a/indra/newview/skins/default/xui/it/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/it/floater_avatar_textures.xml @@ -8,7 +8,7 @@ texture</text>  			<text name="composite_label">Composito  Texture</text>  			<button label="Memorizza gli ID sulla console" label_selected="Dump" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Capigliature" name="hair-baked"/>  				<texture_picker label="Capigliature" name="hair_grain"/>  				<texture_picker label="Alpha dei capelli" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index 178bb90ca6..f77ab1062a 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -31,7 +31,6 @@ Distanza visualizzazione: [DRAW_DISTANCE]m  Larghezza banda: [NET_BANDWITH]kbit/s  Fattore livello di dettaglio: [LOD_FACTOR]  Qualità di rendering: [RENDER_QUALITY] -Modello illuminazione avanzato: [GPU_SHADERS]  Memoria texture: [TEXTURE_MEMORY]MB</string>  	<string name="AboutOSXHiDPI">Modalità display HiDPI: [HIDPI]</string>  	<string name="AboutLibs">J2C Versione decoder: [J2C_VERSION]  diff --git a/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml b/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml index e71ed5c599..5de3486ea1 100644 --- a/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/ja/floater_avatar_textures.xml @@ -13,7 +13,7 @@  				合成テクスチャ  			</text>  			<button label="IDをコンソールにダンプ" label_selected="ダンプ" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="髪" name="hair-baked"/>  				<texture_picker label="髪" name="hair_grain"/>  				<texture_picker label="髪のアルファ" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/ja/panel_performance_preferences.xml b/indra/newview/skins/default/xui/ja/panel_performance_preferences.xml index a031cd7176..40e7800adc 100644 --- a/indra/newview/skins/default/xui/ja/panel_performance_preferences.xml +++ b/indra/newview/skins/default/xui/ja/panel_performance_preferences.xml @@ -96,25 +96,4 @@  	<text name="photo_desc">  		(0.0~4.0の値を入れてください)  	</text> -	<text name="1_lbl"> -		1 -	</text> -	<text name="2_lbl"> -		2 -	</text> -	<text name="3_lbl"> -		3 -	</text> -	<text name="4_lbl"> -		4 -	</text> -	<text name="5_lbl"> -		5 -	</text> -	<text name="6_lbl"> -		6 -	</text> -	<text name="7_lbl"> -		7 -	</text>  </panel> diff --git a/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml b/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml index 18f5a40ea5..ee5e4d277c 100644 --- a/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/pl/floater_avatar_textures.xml @@ -14,7 +14,7 @@ prerenderowane  kompozytowe  			</text>  			<button label="Zrzuć ID do Konsoli" label_selected="Zrzuć" name="Dump" /> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Włosy" name="hair-baked" />  				<texture_picker label="Włosy" name="hair_grain" />  				<texture_picker label="Alpha włosów" name="hair_alpha" /> diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index 26ec6cc9dc..8032443020 100644 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -49,7 +49,6 @@ Pole widzenia (Draw Distance): [DRAW_DISTANCE]m  Przepustowość (Bandwidth): [NET_BANDWITH]kbit/s  Mnożnik poziomu detali (LOD Factor): [LOD_FACTOR]  Jakość wyświetlania (Render quality): [RENDER_QUALITY] -Zaawansowane oświetlenie (Advanced Lighting Model): [GPU_SHADERS]  Pamięć tekstur (Texture memory): [TEXTURE_MEMORY]MB  Pamięć podręczna dysku (Disk cache): [DISK_CACHE_INFO]  	</string> diff --git a/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml b/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml index 6cdf66cb14..3a0c2febd7 100644 --- a/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/pt/floater_avatar_textures.xml @@ -8,7 +8,7 @@ Texturas</text>  			<text name="composite_label">Compósito:  Texturas</text>  			<button label="Enviar IDs para painel" label_selected="Dump" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Cabelo" name="hair-baked"/>  				<texture_picker label="Cabelo" name="hair_grain"/>  				<texture_picker label="Cabelo alpha" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 6db5da2e89..4ce1e6d2ec 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -29,7 +29,6 @@ Estabelecer a distância: [DRAW_DISTANCE]m  Largura da banda: [NET_BANDWITH]kbit/s  LOD fator: [LOD_FACTOR]  Qualidade de renderização: [RENDER_QUALITY] -Modelo avançado de luzes: [GPU_SHADERS]  Memória de textura: [TEXTURE_MEMORY]MB</string>  	<string name="AboutOSXHiDPI">HiDPI modo de exibição: [HIDPI]</string>  	<string name="AboutLibs">Versão do J2C Decoder: [J2C_VERSION]  diff --git a/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml b/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml index cb39f66247..b8906eebbd 100644 --- a/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/ru/floater_avatar_textures.xml @@ -14,7 +14,7 @@  Текстуры  			</text>  			<button label="Вывод ID на консоль" label_selected="Вывод" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Волосы" name="hair-baked"/>  				<texture_picker label="Волосы" name="hair_grain"/>  				<texture_picker label="Альфа волос" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 61d836a2d1..0079309ba2 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -69,7 +69,6 @@ SLURL: <nolink>[SLURL]</nolink>  Ширина канала: [NET_BANDWITH] кбит/с  Коэффициент детализации: [LOD_FACTOR]  Качество визуализации: [RENDER_QUALITY] -Расширенная модель освещения: [GPU_SHADERS]  Память текстур: [TEXTURE_MEMORY] МБ  	</string>  	<string name="AboutOSXHiDPI"> diff --git a/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml b/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml index b519cfa1a9..2dabab2bca 100644 --- a/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/tr/floater_avatar_textures.xml @@ -14,7 +14,7 @@ Dokular  Dokular  			</text>  			<button label="Kimlikleri Konsole Yığ" label_selected="Yığ" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="Saç" name="hair-baked"/>  				<texture_picker label="Saç" name="hair_grain"/>  				<texture_picker label="Saç Alfası" name="hair_alpha"/> diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index e709a4c5d6..fa2fd3a802 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -69,7 +69,6 @@ UI Ölçeklendirme: [UI_SCALE]  Bant genişliği: [NET_BANDWITH]kbit/s  LOD faktörü: [LOD_FACTOR]  İşleme kalitesi: [RENDER_QUALITY] -Gelişmiş Aydınlatma Modeli: [GPU_SHADERS]  Doku belleği: [TEXTURE_MEMORY]MB  	</string>  	<string name="AboutOSXHiDPI"> diff --git a/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml b/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml index 69309c96de..260216fb15 100644 --- a/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/zh/floater_avatar_textures.xml @@ -14,7 +14,7 @@  材質  			</text>  			<button label="傾印 ID 到控制臺" label_selected="傾印" name="Dump"/> -			<panel name="scroll_content_panel"> +			<panel name="scroll_content_panel2">  				<texture_picker label="頭髮" name="hair-baked"/>  				<texture_picker label="頭髮" name="hair_grain"/>  				<texture_picker label="頭髮半透明" name="hair_alpha"/>  | 
