From 93a88e602545828c0298b20ff0375f191d1f6d9a Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Fri, 7 Feb 2025 05:55:47 -0500 Subject: Water Exclusion Surfaces (#3517) * #3455 Add support for water exclusion surfaces --- indra/llrender/llshadermgr.cpp | 1 + indra/llrender/llshadermgr.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 37697f8a15..25d8ccd4b3 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1392,6 +1392,7 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("screenTex"); mReservedUniforms.push_back("screenDepth"); mReservedUniforms.push_back("refTex"); + mReservedUniforms.push_back("exclusionTex"); mReservedUniforms.push_back("eyeVec"); mReservedUniforms.push_back("time"); mReservedUniforms.push_back("waveDir1"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 34bd73a42e..46788841a5 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -36,6 +36,8 @@ public: LLShaderMgr(); virtual ~LLShaderMgr(); + // Note: although you can use statically hashed strings to just bind a random uniform, it's generally preferably that you use this. + // Always document what the actual shader uniform is next to the shader uniform in this struct. // clang-format off typedef enum { // Shader uniform name, set in LLShaderMgr::initAttribsAndUniforms() @@ -234,6 +236,7 @@ public: WATER_SCREENTEX, // "screenTex" WATER_SCREENDEPTH, // "screenDepth" WATER_REFTEX, // "refTex" + WATER_EXCLUSIONTEX, // "exclusionTex" WATER_EYEVEC, // "eyeVec" WATER_TIME, // "time" WATER_WAVE_DIR1, // "waveDir1" -- cgit v1.3 From 3a251b1b9e753589dd12e25236e77275c9010566 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 7 Feb 2025 22:07:02 +0200 Subject: #3332 Switching screens can break text rendering --- indra/llrender/llfontgl.cpp | 1 + indra/llrender/llfontgl.h | 1 + indra/llrender/llfontvertexbuffer.cpp | 4 +++- indra/llrender/llfontvertexbuffer.h | 1 + indra/newview/llviewerwindow.cpp | 1 + 5 files changed, 7 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 4c9a062246..4d4eaf1a9a 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -58,6 +58,7 @@ F32 LLFontGL::sVertDPI = 96.f; F32 LLFontGL::sHorizDPI = 96.f; F32 LLFontGL::sScaleX = 1.f; F32 LLFontGL::sScaleY = 1.f; +S32 LLFontGL::sResolutionGeneration = 0; bool LLFontGL::sDisplayFont = true ; std::string LLFontGL::sAppDir; diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 4bb6c55c65..9b63fc7c38 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -224,6 +224,7 @@ public: static F32 sHorizDPI; static F32 sScaleX; static F32 sScaleY; + static S32 sResolutionGeneration; static bool sDisplayFont ; static std::string sAppDir; // For loading fonts diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index 5bd1ca5eed..17b23c420d 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -146,7 +146,8 @@ S32 LLFontVertexBuffer::render( || mLastScaleY != LLFontGL::sScaleY || mLastVertDPI != LLFontGL::sVertDPI || mLastHorizDPI != LLFontGL::sHorizDPI - || mLastOrigin != LLFontGL::sCurOrigin) + || mLastOrigin != LLFontGL::sCurOrigin + || mLastResGeneration != LLFontGL::sResolutionGeneration) { genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); @@ -201,6 +202,7 @@ void LLFontVertexBuffer::genBuffers( mLastVertDPI = LLFontGL::sVertDPI; mLastHorizDPI = LLFontGL::sHorizDPI; mLastOrigin = LLFontGL::sCurOrigin; + mLastResGeneration = LLFontGL::sResolutionGeneration; if (right_x) { diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index af195dfff9..f244e7fefa 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -117,6 +117,7 @@ private: F32 mLastScaleY = 1.f; F32 mLastVertDPI = 0.f; F32 mLastHorizDPI = 0.f; + S32 mLastResGeneration = 0; LLCoordGL mLastOrigin; static bool sEnableBufferCollection; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 8a5aac9b8b..8915e68298 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1750,6 +1750,7 @@ bool LLViewerWindow::handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 { LLViewerWindow::reshape(window_width, window_height); mResDirty = true; + LLFontGL::sResolutionGeneration++; return true; } else -- cgit v1.3 From 6a78b5b43cf14a30130dded0173ab914e282aa23 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 10 Feb 2025 19:20:57 +0200 Subject: #3332 Font issues with multy-byte and multy-glyps characters --- indra/llrender/llfontgl.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 4d4eaf1a9a..91242f4947 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -272,9 +272,11 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons const LLFontGlyphInfo* next_glyph = NULL; static constexpr S32 GLYPH_BATCH_SIZE = 30; - static thread_local LLVector4a vertices[GLYPH_BATCH_SIZE * 6]; - static thread_local LLVector2 uvs[GLYPH_BATCH_SIZE * 6]; - static thread_local LLColor4U colors[GLYPH_BATCH_SIZE * 6]; + // string can have more than one glyph per char, make sure last one can fit + static constexpr S32 BUFFER_SIZE = GLYPH_BATCH_SIZE * 2; + static thread_local LLVector4a vertices[BUFFER_SIZE * 6]; + static thread_local LLVector2 uvs[BUFFER_SIZE * 6]; + static thread_local LLColor4U colors[BUFFER_SIZE * 6]; LLColor4U text_color(color); // Preserve the transparency to render fading emojis in fading text (e.g. @@ -283,6 +285,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons std::pair bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1); S32 glyph_count = 0; + llwchar last_char = wstr[begin_offset]; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; @@ -300,7 +303,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons } // Per-glyph bitmap texture. std::pair next_bitmap_entry = fgi->mBitmapEntry; - if (next_bitmap_entry != bitmap_entry) + if (next_bitmap_entry != bitmap_entry || last_char != wch) { // Actually draw the queued glyphs before switching their texture; // otherwise the queued glyphs will be taken from wrong textures. @@ -317,6 +320,10 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons bitmap_entry = next_bitmap_entry; LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second); gGL.getTexUnit(0)->bind(font_image); + + // For multi-byte characters just draw each time character changes + // Might be overkill and might be better to detect multybyte + last_char = wch; } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) -- cgit v1.3 From d450295cf517784744552acb2e885a1657a60f7e Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Tue, 11 Feb 2025 13:49:24 -0500 Subject: Additional water fixes and tweaks. (#3524) * Incorporation of feedback for #3456 --- indra/llrender/llglslshader.h | 1 + indra/llrender/llshadermgr.cpp | 8 + .../class1/deferred/postDeferredTonemap.glsl | 129 +----------- .../shaders/class1/deferred/tonemapUtilF.glsl | 180 ++++++++++++++++ .../shaders/class3/environment/waterF.glsl | 55 ++--- indra/newview/lldrawpoolwater.cpp | 231 +++++++++------------ indra/newview/llviewershadermgr.cpp | 37 +--- indra/newview/llviewershadermgr.h | 1 - 8 files changed, 325 insertions(+), 317 deletions(-) create mode 100644 indra/newview/app_settings/shaders/class1/deferred/tonemapUtilF.glsl (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 58c456f134..873ab0cff5 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -59,6 +59,7 @@ public: bool attachNothing = false; bool hasHeroProbes = false; bool isPBRTerrain = false; + bool hasTonemap = false; }; // ============= Structure for caching shader uniforms =============== diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 25d8ccd4b3..4807c12226 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -291,6 +291,14 @@ bool LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) } } + if (features->hasTonemap) + { + if (!shader->attachFragmentObject("deferred/tonemapUtilF.glsl")) + { + return false; + } + } + // NOTE order of shader object attaching is VERY IMPORTANT!!! if (features->hasAtmospherics) { diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl index fc6d4d7727..c4610bffac 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredTonemap.glsl @@ -28,138 +28,11 @@ out vec4 frag_color; uniform sampler2D diffuseRect; -uniform sampler2D exposureMap; -uniform vec2 screen_res; in vec2 vary_fragcoord; vec3 linear_to_srgb(vec3 cl); - -//=============================================================== -// 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 toneMap(vec3 color); void main() { 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/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl index 7027e3796e..c56e38d016 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,8 +255,6 @@ 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; #ifdef TRANSPARENT_WATER @@ -289,8 +284,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 +295,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); @@ -323,20 +318,26 @@ 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; - + 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/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp index c27f5ef486..32de0e5ee7 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_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_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 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 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 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(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/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)); @@ -944,36 +943,6 @@ bool LLViewerShaderMgr::loadShadersWater() llassert(success); } - 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 @@ -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; -- cgit v1.3 From e708d69fb09963c4829dc6cd095b3e174e268b60 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 11 Feb 2025 22:57:53 +0200 Subject: #3332 Fix buffers not accounting for dynamic cache values And fix previous lapse of judgement with GLYPH_BATCH_SIZE --- indra/llrender/llfontbitmapcache.cpp | 4 ++-- indra/llrender/llfontfreetype.h | 1 + indra/llrender/llfontgl.cpp | 26 +++++++++++++++++++------- indra/llrender/llfontgl.h | 1 + indra/llrender/llfontvertexbuffer.cpp | 4 +++- indra/llrender/llfontvertexbuffer.h | 4 ++++ 6 files changed, 30 insertions(+), 10 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index ee9cfd0719..83f5d31186 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -107,7 +107,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyp mBitmapHeight = image_height; S32 num_components = getNumComponents(bitmap_type); - mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components)); + mImageRawVec[bitmap_idx].emplace_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components)); bitmap_num = static_cast(mImageRawVec[bitmap_idx].size()) - 1; LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num); @@ -117,7 +117,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyp } // Make corresponding GL image. - mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false, false)); + mImageGLVec[bitmap_idx].emplace_back(new LLImageGL(image_raw, false, false)); LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num); // Start at beginning of the new image. diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h index 1ad795060a..a2d925c5f6 100644 --- a/indra/llrender/llfontfreetype.h +++ b/indra/llrender/llfontfreetype.h @@ -148,6 +148,7 @@ public: void setStyle(U8 style); U8 getStyle() const; + S32 getAddedGlyphs() const { return mAddGlyphCount; } private: void resetBitmapCache(); diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 91242f4947..4037c036e5 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -110,6 +110,11 @@ S32 LLFontGL::getNumFaces(const std::string& filename) return mFontFreetype->getNumFaces(filename); } +S32 LLFontGL::getKnownGlyphCount() const +{ + return mFontFreetype ? mFontFreetype->getAddedGlyphs() : 0; +} + S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses, bool use_color) const { @@ -250,6 +255,10 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); + // This looks wrong, value is dynamic. + // LLFontBitmapCache::nextOpenPos can alter these values when + // new characters get added to cache, which affects whole string. + // Todo: Perhaps value should update after symbols were added? F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); @@ -271,12 +280,14 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons const LLFontGlyphInfo* next_glyph = NULL; + // string can have more than one glyph per char (ex: bold or shadow), + // make sure that GLYPH_BATCH_SIZE won't end up with half a symbol. + // See drawGlyph. + // Ex: with shadows it's 6 glyps per char. 30 fits exactly 5 chars. static constexpr S32 GLYPH_BATCH_SIZE = 30; - // string can have more than one glyph per char, make sure last one can fit - static constexpr S32 BUFFER_SIZE = GLYPH_BATCH_SIZE * 2; - static thread_local LLVector4a vertices[BUFFER_SIZE * 6]; - static thread_local LLVector2 uvs[BUFFER_SIZE * 6]; - static thread_local LLColor4U colors[BUFFER_SIZE * 6]; + static thread_local LLVector4a vertices[GLYPH_BATCH_SIZE * 6]; + static thread_local LLVector2 uvs[GLYPH_BATCH_SIZE * 6]; + static thread_local LLColor4U colors[GLYPH_BATCH_SIZE * 6]; LLColor4U text_color(color); // Preserve the transparency to render fading emojis in fading text (e.g. @@ -321,8 +332,9 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second); gGL.getTexUnit(0)->bind(font_image); - // For multi-byte characters just draw each time character changes - // Might be overkill and might be better to detect multybyte + // For some reason it's not enough to compare by bitmap_entry. + // Issue hits emojis, japenese and chinese glyphs, only on first run. + // Todo: figure it out, there might be a bug with raw image data. last_char = wch; } diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 9b63fc7c38..73efc6b1eb 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -90,6 +90,7 @@ public: bool loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n); S32 getNumFaces(const std::string& filename); + S32 getKnownGlyphCount() const; S32 render(const LLWString &text, S32 begin_offset, const LLRect& rect, diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index 17b23c420d..b53a841a87 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -147,7 +147,8 @@ S32 LLFontVertexBuffer::render( || mLastVertDPI != LLFontGL::sVertDPI || mLastHorizDPI != LLFontGL::sHorizDPI || mLastOrigin != LLFontGL::sCurOrigin - || mLastResGeneration != LLFontGL::sResolutionGeneration) + || mLastResGeneration != LLFontGL::sResolutionGeneration + || mLastFontGlyphCount != fontp->getKnownGlyphCount()) { genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); @@ -203,6 +204,7 @@ void LLFontVertexBuffer::genBuffers( mLastHorizDPI = LLFontGL::sHorizDPI; mLastOrigin = LLFontGL::sCurOrigin; mLastResGeneration = LLFontGL::sResolutionGeneration; + mLastFontGlyphCount = fontp->getKnownGlyphCount(); if (right_x) { diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index f244e7fefa..d5e1280dbf 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -120,6 +120,10 @@ private: S32 mLastResGeneration = 0; LLCoordGL mLastOrigin; + // Adding new characters to bitmap cache can alter value from getBitmapWidth(); + // which alters whole string. So rerender when new characters were added to cache. + S32 mLastFontGlyphCount = 0; + static bool sEnableBufferCollection; }; -- cgit v1.3