Няма описание
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DebugUI.Fields.cs 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Text.RegularExpressions;
  6. using UnityEngine.Assertions;
  7. namespace UnityEngine.Rendering
  8. {
  9. public partial class DebugUI
  10. {
  11. /// <summary>
  12. /// Generic field - will be serialized in the editor if it's not read-only
  13. /// </summary>
  14. /// <typeparam name="T">The type of data managed by the field.</typeparam>
  15. public abstract class Field<T> : Widget, IValueField
  16. {
  17. /// <summary>
  18. /// Getter for this field.
  19. /// </summary>
  20. public Func<T> getter { get; set; }
  21. /// <summary>
  22. /// Setter for this field.
  23. /// </summary>
  24. public Action<T> setter { get; set; }
  25. // This should be an `event` but they don't play nice with object initializers in the
  26. // version of C# we use.
  27. /// <summary>
  28. /// Callback used when the value of the field changes.
  29. /// </summary>
  30. public Action<Field<T>, T> onValueChanged;
  31. /// <summary>
  32. /// Function used to validate the value when updating the field.
  33. /// </summary>
  34. /// <param name="value">Input value.</param>
  35. /// <returns>Validated value.</returns>
  36. object IValueField.ValidateValue(object value)
  37. {
  38. return ValidateValue((T)value);
  39. }
  40. /// <summary>
  41. /// Function used to validate the value when updating the field.
  42. /// </summary>
  43. /// <param name="value">Input value.</param>
  44. /// <returns>Validated value.</returns>
  45. public virtual T ValidateValue(T value)
  46. {
  47. return value;
  48. }
  49. /// <summary>
  50. /// Get the value of the field.
  51. /// </summary>
  52. /// <returns>Value of the field.</returns>
  53. object IValueField.GetValue()
  54. {
  55. return GetValue();
  56. }
  57. /// <summary>
  58. /// Get the value of the field.
  59. /// </summary>
  60. /// <returns>Value of the field.</returns>
  61. public T GetValue()
  62. {
  63. Assert.IsNotNull(getter);
  64. return getter();
  65. }
  66. /// <summary>
  67. /// Set the value of the field.
  68. /// </summary>
  69. /// <param name="value">Input value.</param>
  70. public void SetValue(object value)
  71. {
  72. SetValue((T)value);
  73. }
  74. /// <summary>
  75. /// Set the value of the field.
  76. /// </summary>
  77. /// <param name="value">Input value.</param>
  78. public virtual void SetValue(T value)
  79. {
  80. Assert.IsNotNull(setter);
  81. var v = ValidateValue(value);
  82. if (v == null || !v.Equals(getter()))
  83. {
  84. setter(v);
  85. onValueChanged?.Invoke(this, v);
  86. }
  87. }
  88. }
  89. /// <summary>
  90. /// Boolean field.
  91. /// </summary>
  92. public class BoolField : Field<bool> { }
  93. /// <summary>
  94. /// An array of checkboxes that Unity displays in a horizontal row.
  95. /// </summary>
  96. public class HistoryBoolField : BoolField
  97. {
  98. /// <summary>
  99. /// History getter for this field.
  100. /// </summary>
  101. public Func<bool>[] historyGetter { get; set; }
  102. /// <summary>
  103. /// Depth of the field's history.
  104. /// </summary>
  105. public int historyDepth => historyGetter?.Length ?? 0;
  106. /// <summary>
  107. /// Get the value of the field at a certain history index.
  108. /// </summary>
  109. /// <param name="historyIndex">Index of the history to query.</param>
  110. /// <returns>Value of the field at the provided history index.</returns>
  111. public bool GetHistoryValue(int historyIndex)
  112. {
  113. Assert.IsNotNull(historyGetter);
  114. Assert.IsTrue(historyIndex >= 0 && historyIndex < historyGetter.Length, "out of range historyIndex");
  115. Assert.IsNotNull(historyGetter[historyIndex]);
  116. return historyGetter[historyIndex]();
  117. }
  118. }
  119. /// <summary>
  120. /// A slider for an integer.
  121. /// </summary>
  122. public class IntField : Field<int>
  123. {
  124. /// <summary>
  125. /// Minimum value function.
  126. /// </summary>
  127. public Func<int> min;
  128. /// <summary>
  129. /// Maximum value function.
  130. /// </summary>
  131. public Func<int> max;
  132. // Runtime-only
  133. /// <summary>
  134. /// Step increment.
  135. /// </summary>
  136. public int incStep = 1;
  137. /// <summary>
  138. /// Step increment multiplier.
  139. /// </summary>
  140. public int intStepMult = 10;
  141. /// <summary>
  142. /// Function used to validate the value when updating the field.
  143. /// </summary>
  144. /// <param name="value">Input value.</param>
  145. /// <returns>Validated value.</returns>
  146. public override int ValidateValue(int value)
  147. {
  148. if (min != null) value = Mathf.Max(value, min());
  149. if (max != null) value = Mathf.Min(value, max());
  150. return value;
  151. }
  152. }
  153. /// <summary>
  154. /// A slider for a positive integer.
  155. /// </summary>
  156. public class UIntField : Field<uint>
  157. {
  158. /// <summary>
  159. /// Minimum value function.
  160. /// </summary>
  161. public Func<uint> min;
  162. /// <summary>
  163. /// Maximum value function.
  164. /// </summary>
  165. public Func<uint> max;
  166. // Runtime-only
  167. /// <summary>
  168. /// Step increment.
  169. /// </summary>
  170. public uint incStep = 1u;
  171. /// <summary>
  172. /// Step increment multiplier.
  173. /// </summary>
  174. public uint intStepMult = 10u;
  175. /// <summary>
  176. /// Function used to validate the value when updating the field.
  177. /// </summary>
  178. /// <param name="value">Input value.</param>
  179. /// <returns>Validated value.</returns>
  180. public override uint ValidateValue(uint value)
  181. {
  182. if (min != null) value = (uint)Mathf.Max((int)value, (int)min());
  183. if (max != null) value = (uint)Mathf.Min((int)value, (int)max());
  184. return value;
  185. }
  186. }
  187. /// <summary>
  188. /// A slider for a float.
  189. /// </summary>
  190. public class FloatField : Field<float>
  191. {
  192. /// <summary>
  193. /// Minimum value function.
  194. /// </summary>
  195. public Func<float> min;
  196. /// <summary>
  197. /// Maximum value function.
  198. /// </summary>
  199. public Func<float> max;
  200. // Runtime-only
  201. /// <summary>
  202. /// Step increment.
  203. /// </summary>
  204. public float incStep = 0.1f;
  205. /// <summary>
  206. /// Step increment multiplier.
  207. /// </summary>
  208. public float incStepMult = 10f;
  209. /// <summary>
  210. /// Number of decimals.
  211. /// </summary>
  212. public int decimals = 3;
  213. /// <summary>
  214. /// Function used to validate the value when updating the field.
  215. /// </summary>
  216. /// <param name="value">Input value.</param>
  217. /// <returns>Validated value.</returns>
  218. public override float ValidateValue(float value)
  219. {
  220. if (min != null) value = Mathf.Max(value, min());
  221. if (max != null) value = Mathf.Min(value, max());
  222. return value;
  223. }
  224. }
  225. /// <summary>
  226. /// Generic <see cref="EnumField"/> that stores enumNames and enumValues
  227. /// </summary>
  228. /// <typeparam name="T">The inner type of the field</typeparam>
  229. public abstract class EnumField<T> : Field<T>
  230. {
  231. /// <summary>
  232. /// List of names of the enumerator entries.
  233. /// </summary>
  234. public GUIContent[] enumNames;
  235. private int[] m_EnumValues;
  236. /// <summary>
  237. /// List of values of the enumerator entries.
  238. /// </summary>
  239. public int[] enumValues
  240. {
  241. get => m_EnumValues;
  242. set
  243. {
  244. if (value?.Distinct().Count() != value?.Count())
  245. Debug.LogWarning($"{displayName} - The values of the enum are duplicated, this might lead to a errors displaying the enum");
  246. m_EnumValues = value;
  247. }
  248. }
  249. // Space-delimit PascalCase (https://stackoverflow.com/questions/155303/net-how-can-you-split-a-caps-delimited-string-into-an-array)
  250. static Regex s_NicifyRegEx = new("([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", RegexOptions.Compiled);
  251. /// <summary>
  252. /// Automatically fills the enum names with a given <see cref="Type"/>
  253. /// </summary>
  254. /// <param name="enumType">The enum type</param>
  255. protected void AutoFillFromType(Type enumType)
  256. {
  257. if (enumType == null || !enumType.IsEnum)
  258. throw new ArgumentException($"{nameof(enumType)} must not be null and it must be an Enum type");
  259. using (ListPool<GUIContent>.Get(out var tmpNames))
  260. using (ListPool<int>.Get(out var tmpValues))
  261. {
  262. var enumEntries = enumType.GetFields(BindingFlags.Public | BindingFlags.Static)
  263. .Where(fieldInfo => !fieldInfo.IsDefined(typeof(ObsoleteAttribute)) && !fieldInfo.IsDefined(typeof(HideInInspector)));
  264. foreach (var fieldInfo in enumEntries)
  265. {
  266. var description = fieldInfo.GetCustomAttribute<InspectorNameAttribute>();
  267. var displayName = new GUIContent(description == null ? s_NicifyRegEx.Replace(fieldInfo.Name, "$1 ") : description.displayName);
  268. tmpNames.Add(displayName);
  269. tmpValues.Add((int)Enum.Parse(enumType, fieldInfo.Name));
  270. }
  271. enumNames = tmpNames.ToArray();
  272. enumValues = tmpValues.ToArray();
  273. }
  274. }
  275. }
  276. /// <summary>
  277. /// A dropdown that contains the values from an enum.
  278. /// </summary>
  279. public class EnumField : EnumField<int>
  280. {
  281. internal int[] quickSeparators;
  282. private int[] m_Indexes;
  283. internal int[] indexes => m_Indexes ??= Enumerable.Range(0, enumNames?.Length ?? 0).ToArray();
  284. /// <summary>
  285. /// Get the enumeration value index.
  286. /// </summary>
  287. public Func<int> getIndex { get; set; }
  288. /// <summary>
  289. /// Set the enumeration value index.
  290. /// </summary>
  291. public Action<int> setIndex { get; set; }
  292. /// <summary>
  293. /// Current enumeration value index.
  294. /// </summary>
  295. public int currentIndex
  296. {
  297. get => getIndex();
  298. set => setIndex(value);
  299. }
  300. /// <summary>
  301. /// Generates enumerator values and names automatically based on the provided type.
  302. /// </summary>
  303. public Type autoEnum
  304. {
  305. set
  306. {
  307. AutoFillFromType(value);
  308. InitQuickSeparators();
  309. }
  310. }
  311. internal void InitQuickSeparators()
  312. {
  313. var enumNamesPrefix = enumNames.Select(x =>
  314. {
  315. string[] splitted = x.text.Split('/');
  316. if (splitted.Length == 1)
  317. return "";
  318. else
  319. return splitted[0];
  320. });
  321. quickSeparators = new int[enumNamesPrefix.Distinct().Count()];
  322. string lastPrefix = null;
  323. for (int i = 0, wholeNameIndex = 0; i < quickSeparators.Length; ++i)
  324. {
  325. var currentTestedPrefix = enumNamesPrefix.ElementAt(wholeNameIndex);
  326. while (lastPrefix == currentTestedPrefix)
  327. {
  328. currentTestedPrefix = enumNamesPrefix.ElementAt(++wholeNameIndex);
  329. }
  330. lastPrefix = currentTestedPrefix;
  331. quickSeparators[i] = wholeNameIndex++;
  332. }
  333. }
  334. /// <summary>
  335. /// Set the value of the field.
  336. /// </summary>
  337. /// <param name="value">Input value.</param>
  338. public override void SetValue(int value)
  339. {
  340. Assert.IsNotNull(setter);
  341. var validValue = ValidateValue(value);
  342. // There might be cases that the value does not map the index, look for the correct index
  343. var newCurrentIndex = Array.IndexOf(enumValues, validValue);
  344. if (currentIndex != newCurrentIndex && !validValue.Equals(getter()))
  345. {
  346. setter(validValue);
  347. onValueChanged?.Invoke(this, validValue);
  348. if (newCurrentIndex > -1)
  349. currentIndex = newCurrentIndex;
  350. }
  351. }
  352. }
  353. /// <summary>
  354. /// A dropdown that contains a list of Unity objects.
  355. /// </summary>
  356. public class ObjectPopupField : Field<Object>
  357. {
  358. /// <summary>
  359. /// Callback to obtain the elemtents of the pop up
  360. /// </summary>
  361. public Func<IEnumerable<Object>> getObjects { get; set; }
  362. }
  363. /// <summary>
  364. /// Enumerator field with history.
  365. /// </summary>
  366. public class HistoryEnumField : EnumField
  367. {
  368. /// <summary>
  369. /// History getter for this field.
  370. /// </summary>
  371. public Func<int>[] historyIndexGetter { get; set; }
  372. /// <summary>
  373. /// Depth of the field's history.
  374. /// </summary>
  375. public int historyDepth => historyIndexGetter?.Length ?? 0;
  376. /// <summary>
  377. /// Get the value of the field at a certain history index.
  378. /// </summary>
  379. /// <param name="historyIndex">Index of the history to query.</param>
  380. /// <returns>Value of the field at the provided history index.</returns>
  381. public int GetHistoryValue(int historyIndex)
  382. {
  383. Assert.IsNotNull(historyIndexGetter);
  384. Assert.IsTrue(historyIndex >= 0 && historyIndex < historyIndexGetter.Length, "out of range historyIndex");
  385. Assert.IsNotNull(historyIndexGetter[historyIndex]);
  386. return historyIndexGetter[historyIndex]();
  387. }
  388. }
  389. /// <summary>
  390. /// Bitfield enumeration field.
  391. /// </summary>
  392. public class BitField : EnumField<Enum>
  393. {
  394. Type m_EnumType;
  395. /// <summary>
  396. /// Generates bitfield values and names automatically based on the provided type.
  397. /// </summary>
  398. public Type enumType
  399. {
  400. get => m_EnumType;
  401. set
  402. {
  403. m_EnumType = value;
  404. AutoFillFromType(value);
  405. }
  406. }
  407. }
  408. /// <summary>
  409. /// Color field.
  410. /// </summary>
  411. public class ColorField : Field<Color>
  412. {
  413. /// <summary>
  414. /// HDR color.
  415. /// </summary>
  416. public bool hdr = false;
  417. /// <summary>
  418. /// Show alpha of the color field.
  419. /// </summary>
  420. public bool showAlpha = true;
  421. // Editor-only
  422. /// <summary>
  423. /// Show the color picker.
  424. /// </summary>
  425. public bool showPicker = true;
  426. // Runtime-only
  427. /// <summary>
  428. /// Step increment.
  429. /// </summary>
  430. public float incStep = 0.025f;
  431. /// <summary>
  432. /// Step increment multiplier.
  433. /// </summary>
  434. public float incStepMult = 5f;
  435. /// <summary>
  436. /// Number of decimals.
  437. /// </summary>
  438. public int decimals = 3;
  439. /// <summary>
  440. /// Function used to validate the value when updating the field.
  441. /// </summary>
  442. /// <param name="value">Input value.</param>
  443. /// <returns>Validated value.</returns>
  444. public override Color ValidateValue(Color value)
  445. {
  446. if (!hdr)
  447. {
  448. value.r = Mathf.Clamp01(value.r);
  449. value.g = Mathf.Clamp01(value.g);
  450. value.b = Mathf.Clamp01(value.b);
  451. value.a = Mathf.Clamp01(value.a);
  452. }
  453. return value;
  454. }
  455. }
  456. /// <summary>
  457. /// Vector2 field.
  458. /// </summary>
  459. public class Vector2Field : Field<Vector2>
  460. {
  461. // Runtime-only
  462. /// <summary>
  463. /// Step increment.
  464. /// </summary>
  465. public float incStep = 0.025f;
  466. /// <summary>
  467. /// Step increment multiplier.
  468. /// </summary>
  469. public float incStepMult = 10f;
  470. /// <summary>
  471. /// Number of decimals.
  472. /// </summary>
  473. public int decimals = 3;
  474. }
  475. /// <summary>
  476. /// Vector3 field.
  477. /// </summary>
  478. public class Vector3Field : Field<Vector3>
  479. {
  480. // Runtime-only
  481. /// <summary>
  482. /// Step increment.
  483. /// </summary>
  484. public float incStep = 0.025f;
  485. /// <summary>
  486. /// Step increment multiplier.
  487. /// </summary>
  488. public float incStepMult = 10f;
  489. /// <summary>
  490. /// Number of decimals.
  491. /// </summary>
  492. public int decimals = 3;
  493. }
  494. /// <summary>
  495. /// Vector4 field.
  496. /// </summary>
  497. public class Vector4Field : Field<Vector4>
  498. {
  499. // Runtime-only
  500. /// <summary>
  501. /// Step increment.
  502. /// </summary>
  503. public float incStep = 0.025f;
  504. /// <summary>
  505. /// Step increment multiplier.
  506. /// </summary>
  507. public float incStepMult = 10f;
  508. /// <summary>
  509. /// Number of decimals.
  510. /// </summary>
  511. public int decimals = 3;
  512. }
  513. /// <summary>
  514. /// A field for selecting a Unity object.
  515. /// </summary>
  516. public class ObjectField : Field<Object>
  517. {
  518. /// <summary>
  519. /// Object type.
  520. /// </summary>
  521. public Type type = typeof(Object);
  522. }
  523. /// <summary>
  524. /// A list of fields for selecting Unity objects.
  525. /// </summary>
  526. public class ObjectListField : Field<Object[]>
  527. {
  528. /// <summary>
  529. /// Objects type.
  530. /// </summary>
  531. public Type type = typeof(Object);
  532. }
  533. /// <summary>
  534. /// A read-only message box with an icon.
  535. /// </summary>
  536. public class MessageBox : Widget
  537. {
  538. /// <summary>
  539. /// Label style defines text color and background.
  540. /// </summary>
  541. public enum Style
  542. {
  543. /// <summary>
  544. /// Info category
  545. /// </summary>
  546. Info,
  547. /// <summary>
  548. /// Warning category
  549. /// </summary>
  550. Warning,
  551. /// <summary>
  552. /// Error category
  553. /// </summary>
  554. Error
  555. }
  556. /// <summary>
  557. /// Style used to render displayName.
  558. /// </summary>
  559. public Style style = Style.Info;
  560. /// <summary>
  561. /// Message Callback to feed the new message to the widget
  562. /// </summary>
  563. public Func<string> messageCallback = null;
  564. /// <summary>
  565. /// This obtains the message from the display name or from the message callback if it is not null
  566. /// </summary>
  567. public string message => messageCallback == null ? displayName : messageCallback();
  568. }
  569. /// <summary>
  570. /// Widget that will show into the Runtime UI only
  571. /// Warning the user if the Runtime Debug Shaders variants are being stripped from the build.
  572. /// </summary>
  573. public class RuntimeDebugShadersMessageBox : MessageBox
  574. {
  575. /// <summary>
  576. /// Constructs a <see cref="RuntimeDebugShadersMessageBox"/>
  577. /// </summary>
  578. public RuntimeDebugShadersMessageBox()
  579. {
  580. displayName =
  581. "Warning: the debug shader variants are missing. Ensure that the \"Strip Runtime Debug Shaders\" option is disabled in the SRP Graphics Settings.";
  582. style = DebugUI.MessageBox.Style.Warning;
  583. isHiddenCallback = () =>
  584. {
  585. #if !UNITY_EDITOR
  586. if (GraphicsSettings.TryGetRenderPipelineSettings<ShaderStrippingSetting>(out var shaderStrippingSetting))
  587. return !shaderStrippingSetting.stripRuntimeDebugShaders;
  588. #endif
  589. return true;
  590. };
  591. }
  592. }
  593. }
  594. }