using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Graphics; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Utility; using RTCF = Robust.Client.Graphics.RenderTargetColorFormat; namespace Robust.Client.UserInterface; [GenerateTypedNameReferences] internal sealed partial class DevWindowTabRenderTargets : Control { [Dependency] private IClydeInternal _clyde = default!; [Dependency] private ILocalizationManager _loc = default!; private readonly Control[] _gridHeader; private readonly StyleBoxFlat _styleAltRow = new() { BackgroundColor = Color.FromHex("#222") }; #if TOOLS private readonly HashSet _copyTextures = new(); #endif // TOOLS public DevWindowTabRenderTargets() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _gridHeader = Table.Children.ToArray(); ReloadButton.OnPressed += _ => Reload(); FilterEdit.OnTextChanged += _ => Reload(); } protected override void VisibilityChanged(bool newVisible) { base.VisibilityChanged(newVisible); if (newVisible) { Reload(); } else { // Clear to release memory when tab not visible. Clear(); } } protected override void ExitedTree() { base.ExitedTree(); Clear(); } private void Clear() { Table.RemoveAllChildren(); #if TOOLS foreach (var copiedTexture in _copyTextures) { copiedTexture.Dispose(); } _copyTextures.Clear(); #endif } private void Reload() { Table.RemoveAllChildren(); foreach (var header in _gridHeader) { Table.AddChild(header); } var totalVram = 0L; var even = true; var rts = _clyde.GetLoadedRenderTextures().OrderBy(x => x.Item1.Handle.Value).ToArray(); foreach (var (instance, loaded) in rts) { #if TOOLS if (_copyTextures.Contains(instance)) continue; #endif // TOOLS if (!string.IsNullOrWhiteSpace(FilterEdit.Text)) { if (loaded.Name is not { } name) continue; if (!name.Contains(FilterEdit.Text, StringComparison.CurrentCultureIgnoreCase)) continue; } AddColumnText(instance.Handle.Value.ToString()); if (loaded.Name != null) AddColumnText(loaded.Name); else if (loaded.WindowId != WindowId.Invalid) AddColumnText(loaded.WindowId.ToString()); else AddColumnText(_loc.GetString("dev-window-tab-render-targets-value-null")); AddColumnText(loaded.Size.ToString()); var type = loaded.ColorFormat.ToString(); if (loaded.DepthStencilHandle != default) type += "+DS"; AddColumnText(type); AddColumnText(ByteHelpers.FormatBytes(loaded.MemoryPressure)); // Disable texture thumbnails outside TOOLS. // Avoid people cheating by using devwindow to see through walls. Barely. #if TOOLS if (instance is IRenderTexture renderTexture) { var clone = CloneTexture(renderTexture.Texture, loaded.ColorFormat); _copyTextures.Add(clone); AddColumn(new TextureRect { Texture = clone.Texture, Stretch = TextureRect.StretchMode.KeepAspect }); } else #endif // TOOLS { AddColumnText(_loc.GetString("dev-window-tab-render-targets-value-not-available")); } totalVram += loaded.MemoryPressure; even = !even; } TotalLabel.Text = Loc.GetString( "dev-window-tab-render-targets-summary", ("vram", ByteHelpers.FormatBytes(totalVram))); return; void AddColumnText(string text) { var richTextLabel = new RichTextLabel { Margin = new Thickness(4) }; richTextLabel.SetMessage(text, defaultColor: Color.White); AddColumn(richTextLabel); } void AddColumn(Control control) { control.VerticalAlignment = VAlignment.Center; if (even) { control = new PanelContainer { PanelOverride = _styleAltRow, Children = { control }, }; } else { // Wrapping control so we can use SetHeight. control = new Control { Children = { control }, }; } control.SetHeight = 50; Table.AddChild(control); } } #if TOOLS private IRenderTexture CloneTexture(Texture texture, RTCF colorFormat) { var thumbnailSize = GetThumbnailSize(texture.Size); var rt = _clyde.CreateRenderTarget( thumbnailSize, new RenderTargetFormatParameters { ColorFormat = colorFormat, HasDepthStencil = false, }, new TextureSampleParameters { Filter = true, }, name: $"{nameof(DevWindowTabRenderTargets)}-clone"); _clyde.RenderNow(rt, handle => { handle.DrawingHandleScreen.DrawTextureRect(texture, UIBox2.FromDimensions(Vector2.Zero, thumbnailSize)); }); return rt; } private static Vector2i GetThumbnailSize(Vector2i textureSize) { const int maxHeight = 50; const int maxWidth = 100; var (w, h) = (Vector2)textureSize; if (h > maxHeight) { w /= h / maxHeight; h = maxHeight; } if (w > maxWidth) { h /= w / maxWidth; w = maxWidth; } return new Vector2i((int)w, (int)h); } #endif // TOOLS }