33using Basis . Scripts . Avatar ;
44using UnityEngine ;
55using UnityEngine . UI ;
6-
7- /// <summary>
8- /// Settings panel for the opt-in client-side avatar performance limits. Every limit
9- /// is a (toggle, slider) pair — the toggle flips <c>UsePerfLimit*</c> on, the slider
10- /// sets the corresponding <c>MaxPerf*</c> threshold. All values live in
11- /// <see cref="BasisSettingsDefaults"/> and are applied at runtime by
12- /// <c>SMModuleAvatarPerformanceLimits</c>, which re-evaluates every currently-loaded
13- /// remote avatar when any value changes.
14- /// </summary>
156public static class SettingsProviderPerformanceLimits
167{
17- /// <summary>
18- /// The root RectTransform of the currently-built tab. Captured once at tab
19- /// creation and read by the toggle callbacks so flipping a limit on/off can
20- /// trigger a layout rebuild — hiding/showing a slider with <c>SetActive</c>
21- /// drops it out of the layout pass, but LayoutGroups don't re-flow siblings
22- /// until somebody calls <c>LayoutRebuilder.ForceRebuildLayoutImmediate</c>.
23- /// </summary>
248 private static RectTransform _layoutRoot ;
259
2610 public static PanelTabPage PerformanceLimitsTab ( PanelTabGroup tabGroup )
@@ -33,31 +17,15 @@ public static PanelTabPage PerformanceLimitsTab(PanelTabGroup tabGroup)
3317 RectTransform container = descriptor . ContentParent ;
3418 BuildPerformanceLimitsContent ( container ) ;
3519
36- // Use the same tabKey we registered with AddLazyTab — it drives both the
37- // reset button label and the "navigate back to this tab" hop after the
38- // reset. Hardcoded English would show that literal string in JP/NL.
3920 SettingsProvider . AddResetPageButton ( container , "settings.tab.performancelimits" , ResetPerformanceLimitDefaults ) ;
4021
4122 descriptor . ForceRebuild ( ) ;
4223 return tab ;
4324 }
44-
45- /// <summary>
46- /// Builds every performance-limit group + control into <paramref name="container"/>
47- /// without adding a reset button. Used by the standalone tab and by the merged
48- /// Graphics tab so both share one source of truth.
49- /// </summary>
5025 public static void BuildPerformanceLimitsContent ( RectTransform container )
5126 {
5227 _layoutRoot = container ;
5328
54- // Session-only bypass. Lives at the top so it's the first thing users see
55- // when they want to quickly check what an avatar actually looks like
56- // unfiltered. Not wired to BasisSettingsDefaults because it deliberately
57- // doesn't persist — flipping it writes straight to the runtime flag on
58- // BasisAvatarPerformanceLimits, which fires OnBypassChanged and pulls
59- // every affected avatar through the same debounced reconcile as a normal
60- // limit change.
6129 PanelElementDescriptor bypassGroup =
6230 PanelElementDescriptor . CreateNew ( PanelElementDescriptor . ElementStyles . Group , container ) ;
6331 bypassGroup . SetTitle ( BasisLocalization . Get ( "settings.perf.sessionBypass.title" ) ) ;
@@ -71,17 +39,11 @@ public static void BuildPerformanceLimitsContent(RectTransform container)
7139 BasisAvatarPerformanceLimits . BypassAllLimits = on ;
7240 } ;
7341
74- // Header explanation. Trim limits (animators, lights, particles, trails, lines,
75- // cloth, unity colliders) destroy excess components on remote avatars at load
76- // time and ship on by default. Hard-block limits (triangles, bounds, texture
77- // memory, material slots, bones) fall back to the loading mesh when tripped;
78- // only texture memory is on by default at 512 MB.
7942 PanelElementDescriptor intro =
8043 PanelElementDescriptor . CreateNew ( PanelElementDescriptor . ElementStyles . Group , container ) ;
8144 intro . SetTitle ( BasisLocalization . Get ( "settings.perf.intro.title" ) ) ;
8245 intro . SetDescription ( BasisLocalization . Get ( "settings.perf.intro.description" ) ) ;
8346
84- // ---------------- Geometry ----------------
8547 PanelElementDescriptor geometry =
8648 PanelElementDescriptor . CreateNew ( PanelElementDescriptor . ElementStyles . Group , container ) ;
8749 geometry . SetTitle ( BasisLocalization . Get ( "settings.perf.group.geometry" ) ) ;
@@ -94,28 +56,20 @@ public static void BuildPerformanceLimitsContent(RectTransform container)
9456 BasisSettingsDefaults . MaxPerfTriangles ,
9557 1000 , 2_000_000 , true , displayMode : ValueDisplayMode . Compact ) ;
9658
97- // Minimum starts at 10 m — anything smaller rejects a normal humanoid on
98- // principle, and the bounds check is there to catch 50 m-tall monster
99- // bundles rather than tune small avatars.
10059 AddLimitPair ( geometry . ContentParent ,
10160 BasisLocalization . Get ( "settings.perf.boundsSize.toggle" ) ,
10261 BasisLocalization . Get ( "settings.perf.boundsSize.slider" ) ,
10362 BasisSettingsDefaults . UsePerfLimitBoundsSize ,
10463 BasisSettingsDefaults . MaxPerfBoundsSize ,
10564 10f , 50f , false , decimals : 1 ) ;
10665
107- // Hard-block cap. Intended as a guard rail for pathological bundles rather
108- // than a daily driver — label reflects that so users don't set it low.
109- // "per avatar" is explicit because bones is the one limit where users
110- // reasonably ask whether it sums across the room or counts per-player.
11166 AddLimitPair ( geometry . ContentParent ,
11267 BasisLocalization . Get ( "settings.perf.bones.toggle" ) ,
11368 BasisLocalization . Get ( "settings.perf.bones.slider" ) ,
11469 BasisSettingsDefaults . UsePerfLimitBones ,
11570 BasisSettingsDefaults . MaxPerfBones ,
11671 16 , 16384 , true , displayMode : ValueDisplayMode . Compact ) ;
11772
118- // ---------------- Meshes & Materials ----------------
11973 PanelElementDescriptor meshes =
12074 PanelElementDescriptor . CreateNew ( PanelElementDescriptor . ElementStyles . Group , container ) ;
12175 meshes . SetTitle ( BasisLocalization . Get ( "settings.perf.group.meshesMaterials" ) ) ;
@@ -148,7 +102,6 @@ public static void BuildPerformanceLimitsContent(RectTransform container)
148102 BasisSettingsDefaults . MaxPerfTextureMemoryMB ,
149103 8 , 4096 , true ) ;
150104
151- // ---------------- Physics ----------------
152105 PanelElementDescriptor physics =
153106 PanelElementDescriptor . CreateNew ( PanelElementDescriptor . ElementStyles . Group , container ) ;
154107 physics . SetTitle ( BasisLocalization . Get ( "settings.perf.group.physics" ) ) ;
@@ -160,15 +113,6 @@ public static void BuildPerformanceLimitsContent(RectTransform container)
160113 BasisSettingsDefaults . MaxPerfJiggleBones ,
161114 0 , 128 , true ) ;
162115
163- // JiggleColliderExample is not in AvatarContentPoliceSelector.asset, so the
164- // content police strips every instance at load time before this limit could
165- // ever run. Surface nothing for it in the UI.
166- // AddLimitPair(physics.ContentParent,
167- // "Limit Jiggle Colliders", "Max Jiggle Colliders",
168- // BasisSettingsDefaults.UsePerfLimitJiggleColliders,
169- // BasisSettingsDefaults.MaxPerfJiggleColliders,
170- // 0, 64, true);
171-
172116 AddLimitPair ( physics . ContentParent ,
173117 BasisLocalization . Get ( "settings.perf.colliders.toggle" ) ,
174118 BasisLocalization . Get ( "settings.perf.colliders.slider" ) ,
@@ -183,21 +127,10 @@ public static void BuildPerformanceLimitsContent(RectTransform container)
183127 BasisSettingsDefaults . MaxPerfCloth ,
184128 0 , 16 , true ) ;
185129
186- // ---------------- Effects ----------------
187130 PanelElementDescriptor effects =
188131 PanelElementDescriptor . CreateNew ( PanelElementDescriptor . ElementStyles . Group , container ) ;
189132 effects . SetTitle ( BasisLocalization . Get ( "settings.perf.group.effects" ) ) ;
190133
191- // UnityEngine.Light is not in AvatarContentPoliceSelector.asset, so the
192- // content police strips every Light on a downloaded remote avatar before
193- // this limit could ever run. Addressable (in-build) avatars are curated,
194- // so lights there are effectively impossible too. Surface nothing for it.
195- // AddLimitPair(effects.ContentParent,
196- // "Limit Lights", "Max Lights",
197- // BasisSettingsDefaults.UsePerfLimitLights,
198- // BasisSettingsDefaults.MaxPerfLights,
199- // 0, 32, true);
200-
201134 AddLimitPair ( effects . ContentParent ,
202135 BasisLocalization . Get ( "settings.perf.particleSystems.toggle" ) ,
203136 BasisLocalization . Get ( "settings.perf.particleSystems.slider" ) ,
@@ -219,7 +152,6 @@ public static void BuildPerformanceLimitsContent(RectTransform container)
219152 BasisSettingsDefaults . MaxPerfLineRenderers ,
220153 0 , 64 , true ) ;
221154
222- // ---------------- Runtime ----------------
223155 PanelElementDescriptor runtime =
224156 PanelElementDescriptor . CreateNew ( PanelElementDescriptor . ElementStyles . Group , container ) ;
225157 runtime . SetTitle ( BasisLocalization . Get ( "settings.perf.group.runtime" ) ) ;
@@ -232,30 +164,15 @@ public static void BuildPerformanceLimitsContent(RectTransform container)
232164 BasisSettingsDefaults . MaxPerfAnimators ,
233165 1 , 32 , true ) ;
234166
235- // Cilbox script behaviours: each CilboxProxy on a remote avatar is one
236- // sandboxed MonoBehaviour with its own per-frame Update/FixedUpdate tick.
237167 AddLimitPair ( runtime . ContentParent ,
238168 BasisLocalization . Get ( "settings.perf.cilboxBehaviours.toggle" ) ,
239169 BasisLocalization . Get ( "settings.perf.cilboxBehaviours.slider" ) ,
240170 BasisSettingsDefaults . UsePerfLimitCilboxBehaviours ,
241171 BasisSettingsDefaults . MaxPerfCilboxBehaviours ,
242172 0 , 64 , true ) ;
243173
244- // ---------------- Content Tags ----------------
245- // Sits at the bottom of the same tab so users see content-safety filters
246- // alongside perf filters — same mental model ("block this avatar before it
247- // loads"), different criteria (creator-declared category vs. measured cost).
248174 SettingsProviderContentTags . BuildContentTagsContent ( container ) ;
249175 }
250-
251- /// <summary>
252- /// Adds a toggle and slider for a single limit. The toggle controls whether the
253- /// limit is enforced; the slider sets the threshold. When the toggle is off the
254- /// slider's entire GameObject is hidden so the settings panel only shows the
255- /// knob for an active limit — matches how VRChat / NeosVR hide inactive options.
256- /// <paramref name="displayMode"/> lets large-number sliders (triangles, bones)
257- /// opt into the compact "1k / 32.5k / 2M" readout instead of raw integers.
258- /// </summary>
259176 private static void AddLimitPair (
260177 Component parent ,
261178 string toggleTitle ,
@@ -272,18 +189,8 @@ private static void AddLimitPair(
272189 toggle . Descriptor . SetTitle ( toggleTitle ) ;
273190 toggle . AssignBinding ( useBinding ) ;
274191
275- PanelSlider slider = PanelSlider . CreateEntryAndBind (
276- parent ,
277- PanelSlider . SliderSettings . Advanced ( sliderTitle , sliderMin , sliderMax , wholeNumbers , decimals , displayMode ) ,
278- maxBinding ) ;
279-
280- // Seed visibility from the saved toggle state, then keep it in sync whenever
281- // the binding flips — including programmatic changes like the reset button.
282- // SetActive on the slider GameObject removes it from the layout pass entirely
283- // so the container re-flows and only active limits take up vertical space;
284- // LayoutRebuilder.ForceRebuildLayoutImmediate is what actually closes / reopens
285- // the gap. PanelToggle.OnValueChanged only fires from user-driven SetValue,
286- // so we hook BasisSettingsBinding.OnChanged here to also catch resets.
192+ PanelSlider slider = PanelSlider . CreateEntryAndBind ( parent , PanelSlider . SliderSettings . Advanced ( sliderTitle , sliderMin , sliderMax , wholeNumbers , decimals , displayMode ) , maxBinding ) ;
193+
287194 if ( slider != null )
288195 {
289196 void Sync ( bool on )
0 commit comments