Files
RobustToolbox/Robust.Client/UserInterface/DevWindow/DevWindowTabRenderTargets.xaml.cs
PJB3005 60d26be139 Add devwindow tab listing render targets
Copy TableContainer from content into engine. It's internal though.

Add various stuff to Clyde to allow the UI to access it. Includes a new IClydeInternal.RenderNow() which seems to not completely explode in my face.
2025-09-13 01:21:22 +02:00

243 lines
6.3 KiB
C#

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 readonly IClydeInternal _clyde = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
private readonly Control[] _gridHeader;
private readonly StyleBoxFlat _styleAltRow = new() { BackgroundColor = Color.FromHex("#222") };
#if TOOLS
private readonly HashSet<IRenderTarget> _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
}