mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
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.
This commit is contained in:
@@ -8,3 +8,18 @@ dev-window-tab-textures-info = Width: { $width } Height: { $height }
|
||||
PixelType: { $pixelType } sRGB: { $srgb }
|
||||
Name: { $name }
|
||||
Est. memory usage: { $bytes }
|
||||
|
||||
## "Render Targets" dev window tab
|
||||
dev-window-tab-render-targets-title = Render Targets
|
||||
dev-window-tab-render-targets-reload = Reload
|
||||
dev-window-tab-render-targets-filter = Filter
|
||||
dev-window-tab-render-targets-column-id = ID
|
||||
dev-window-tab-render-targets-column-name = Name
|
||||
dev-window-tab-render-targets-column-size = Size
|
||||
dev-window-tab-render-targets-column-type = Type
|
||||
dev-window-tab-render-targets-column-vram = VRAM
|
||||
dev-window-tab-render-targets-column-thumbnail = Thumbnail
|
||||
|
||||
dev-window-tab-render-targets-value-null = null
|
||||
dev-window-tab-render-targets-value-not-available = Not available
|
||||
dev-window-tab-render-targets-summary = Total VRAM: { $vram }
|
||||
|
||||
@@ -121,6 +121,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderNow(IRenderTarget renderTarget, Action<IRenderHandle> callback)
|
||||
{
|
||||
ClearRenderState();
|
||||
|
||||
_renderHandle.RenderInRenderTarget(
|
||||
renderTarget,
|
||||
() =>
|
||||
{
|
||||
callback(_renderHandle);
|
||||
},
|
||||
null);
|
||||
}
|
||||
|
||||
private void RenderSingleWorldOverlay(Overlay overlay, Viewport vp, OverlaySpace space, in Box2 worldBox, in Box2Rotated worldBounds)
|
||||
{
|
||||
// Check that entity manager has started.
|
||||
|
||||
@@ -209,6 +209,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var pressure = estPixSize * size.X * size.Y;
|
||||
|
||||
var handle = AllocRid();
|
||||
var renderTarget = new RenderTexture(size, textureObject, this, handle);
|
||||
var data = new LoadedRenderTarget
|
||||
{
|
||||
IsWindow = false,
|
||||
@@ -220,10 +221,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
MemoryPressure = pressure,
|
||||
ColorFormat = format.ColorFormat,
|
||||
SampleParameters = sampleParameters,
|
||||
Instance = new WeakReference<RenderTargetBase>(renderTarget),
|
||||
Name = name,
|
||||
};
|
||||
|
||||
//GC.AddMemoryPressure(pressure);
|
||||
var renderTarget = new RenderTexture(size, textureObject, this, handle);
|
||||
_renderTargets.Add(handle, data);
|
||||
return renderTarget;
|
||||
}
|
||||
@@ -301,10 +303,22 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LoadedRenderTarget
|
||||
public IEnumerable<(RenderTargetBase, LoadedRenderTarget)> GetLoadedRenderTextures()
|
||||
{
|
||||
foreach (var loaded in _renderTargets.Values)
|
||||
{
|
||||
if (!loaded.Instance.TryGetTarget(out var instance))
|
||||
continue;
|
||||
|
||||
yield return (instance, loaded);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LoadedRenderTarget
|
||||
{
|
||||
public bool IsWindow;
|
||||
public WindowId WindowId;
|
||||
public string? Name;
|
||||
|
||||
public Vector2i Size;
|
||||
public bool IsSrgb;
|
||||
@@ -325,9 +339,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public long MemoryPressure;
|
||||
|
||||
public TextureSampleParameters? SampleParameters;
|
||||
|
||||
public required WeakReference<RenderTargetBase> Instance;
|
||||
}
|
||||
|
||||
private abstract class RenderTargetBase : IRenderTarget
|
||||
internal abstract class RenderTargetBase : IRenderTarget
|
||||
{
|
||||
protected readonly Clyde Clyde;
|
||||
private bool _disposed;
|
||||
@@ -389,7 +405,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RenderTexture : RenderTargetBase, IRenderTexture
|
||||
internal sealed class RenderTexture : RenderTargetBase, IRenderTexture
|
||||
{
|
||||
public RenderTexture(Vector2i size, ClydeTexture texture, Clyde clyde, ClydeHandle handle)
|
||||
: base(clyde, handle)
|
||||
|
||||
@@ -354,15 +354,17 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_windowHandles.Add(reg.Handle);
|
||||
|
||||
var rtId = AllocRid();
|
||||
var renderTarget = new RenderWindow(this, rtId);
|
||||
_renderTargets.Add(rtId, new LoadedRenderTarget
|
||||
{
|
||||
Size = reg.FramebufferSize,
|
||||
IsWindow = true,
|
||||
WindowId = reg.Id,
|
||||
IsSrgb = true
|
||||
IsSrgb = true,
|
||||
Instance = new WeakReference<RenderTargetBase>(renderTarget),
|
||||
});
|
||||
|
||||
reg.RenderTarget = new RenderWindow(this, rtId);
|
||||
reg.RenderTarget = renderTarget;
|
||||
|
||||
_glContext!.WindowCreated(glSpec, reg);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return [];
|
||||
}
|
||||
|
||||
public IEnumerable<(Clyde.RenderTargetBase, Clyde.LoadedRenderTarget)> GetLoadedRenderTextures()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public ClydeDebugLayers DebugLayers { get; set; }
|
||||
|
||||
public string GetKeyName(Keyboard.Key key) => string.Empty;
|
||||
@@ -317,6 +322,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
#endif // TOOLS
|
||||
|
||||
public void RenderNow(IRenderTarget renderTarget, Action<IRenderHandle> callback)
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class DummyCursor : ICursor
|
||||
{
|
||||
public void Dispose()
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace Robust.Client.Graphics
|
||||
|
||||
Texture GetStockTexture(ClydeStockTexture stockTexture);
|
||||
IEnumerable<(Clyde.Clyde.ClydeTexture, Clyde.Clyde.LoadedTexture)> GetLoadedTextures();
|
||||
IEnumerable<(Clyde.Clyde.RenderTargetBase, Clyde.Clyde.LoadedRenderTarget)> GetLoadedRenderTextures();
|
||||
|
||||
ClydeDebugLayers DebugLayers { get; set; }
|
||||
|
||||
@@ -85,5 +86,7 @@ namespace Robust.Client.Graphics
|
||||
void ViewportsClearAllCached();
|
||||
|
||||
#endif // TOOLS
|
||||
|
||||
void RenderNow(IRenderTarget renderTarget, Action<IRenderHandle> callback);
|
||||
}
|
||||
}
|
||||
|
||||
270
Robust.Client/UserInterface/Controls/TableContainer.cs
Normal file
270
Robust.Client/UserInterface/Controls/TableContainer.cs
Normal file
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
internal sealed class TableContainer : Container
|
||||
{
|
||||
private int _columns = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The absolute minimum width a column can be forced to.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a column *asks* for less width than this (small contents), it can still be smaller.
|
||||
/// But if it asks for more it cannot go below this width.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public float MinForcedColumnWidth { get; set; } = 50;
|
||||
|
||||
// Scratch space used while calculating layout, cached to avoid regular allocations during layout pass.
|
||||
private ColumnData[] _columnDataCache = [];
|
||||
private RowData[] _rowDataCache = [];
|
||||
|
||||
/// <summary>
|
||||
/// How many columns should be displayed.
|
||||
/// </summary>
|
||||
public int Columns
|
||||
{
|
||||
get => _columns;
|
||||
set
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1, nameof(value));
|
||||
|
||||
_columns = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
ResetCachedArrays();
|
||||
|
||||
// Do a first pass measuring all child controls as if they're given infinite space.
|
||||
// This gives us a maximum width the columns want, which we use to proportion them later.
|
||||
var columnIdx = 0;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
ref var column = ref _columnDataCache[columnIdx];
|
||||
|
||||
child.Measure(new Vector2(float.PositiveInfinity, float.PositiveInfinity));
|
||||
column.MaxWidth = Math.Max(column.MaxWidth, child.DesiredSize.X);
|
||||
|
||||
columnIdx += 1;
|
||||
if (columnIdx == _columns)
|
||||
columnIdx = 0;
|
||||
}
|
||||
|
||||
// Calculate Slack and MinWidth for all columns. Also calculate sums for all columns.
|
||||
var totalMinWidth = 0f;
|
||||
var totalMaxWidth = 0f;
|
||||
var totalSlack = 0f;
|
||||
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
column.MinWidth = Math.Min(column.MaxWidth, MinForcedColumnWidth);
|
||||
column.Slack = column.MaxWidth - column.MinWidth;
|
||||
|
||||
totalMinWidth += column.MinWidth;
|
||||
totalMaxWidth += column.MaxWidth;
|
||||
totalSlack += column.Slack;
|
||||
}
|
||||
|
||||
if (totalMaxWidth <= availableSize.X)
|
||||
{
|
||||
// We want less horizontal space than we're given. Huh, that's convenient.
|
||||
// Just set assigned width to be however much they asked for.
|
||||
// We could probably skip the second measure pass in this scenario,
|
||||
// but that's just an optimization, so I don't care right now.
|
||||
//
|
||||
// There's probably a very clever way to make this behavior work with the else block of logic,
|
||||
// just by fiddling with the math.
|
||||
// I'm dumb, it's 4:30 AM. Yeah, I *started* at 2 AM.
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
|
||||
column.AssignedWidth = column.MaxWidth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't have enough horizontal space,
|
||||
// at least without causing *some* sort of word wrapping (assuming text contents).
|
||||
//
|
||||
// Assign horizontal space proportional to the wanted maximum size of the columns.
|
||||
var assignableWidth = Math.Max(0, availableSize.X - totalMinWidth);
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
|
||||
var slackRatio = column.Slack / totalSlack;
|
||||
column.AssignedWidth = column.MinWidth + slackRatio * assignableWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Go over controls for a second measuring pass, this time giving them their assigned measure width.
|
||||
// This will give us a height to slot into per-row data.
|
||||
// We still measure assuming infinite vertical space.
|
||||
// This control can't properly handle being constrained on the Y axis.
|
||||
columnIdx = 0;
|
||||
var rowIdx = 0;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
ref var column = ref _columnDataCache[columnIdx];
|
||||
ref var row = ref _rowDataCache[rowIdx];
|
||||
|
||||
child.Measure(new Vector2(column.AssignedWidth, float.PositiveInfinity));
|
||||
row.MeasuredHeight = Math.Max(row.MeasuredHeight, child.DesiredSize.Y);
|
||||
|
||||
columnIdx += 1;
|
||||
if (columnIdx == _columns)
|
||||
{
|
||||
columnIdx = 0;
|
||||
rowIdx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Sum up height of all rows to get final measured table height.
|
||||
var totalHeight = 0f;
|
||||
for (var r = 0; r < _rowDataCache.Length; r++)
|
||||
{
|
||||
ref var row = ref _rowDataCache[r];
|
||||
totalHeight += row.MeasuredHeight;
|
||||
}
|
||||
|
||||
return new Vector2(Math.Min(availableSize.X, totalMaxWidth), totalHeight);
|
||||
}
|
||||
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
// TODO: Expand to fit given vertical space.
|
||||
|
||||
// Calculate MinWidth and Slack sums again from column data.
|
||||
// We could've cached these from measure but whatever.
|
||||
var totalMinWidth = 0f;
|
||||
var totalSlack = 0f;
|
||||
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
totalMinWidth += column.MinWidth;
|
||||
totalSlack += column.Slack;
|
||||
}
|
||||
|
||||
// Calculate new width based on final given size, also assign horizontal positions of all columns.
|
||||
var assignableWidth = Math.Max(0, finalSize.X - totalMinWidth);
|
||||
var xPos = 0f;
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
|
||||
var slackRatio = column.Slack / totalSlack;
|
||||
column.ArrangedWidth = column.MinWidth + slackRatio * assignableWidth;
|
||||
column.ArrangedX = xPos;
|
||||
|
||||
xPos += column.ArrangedWidth;
|
||||
}
|
||||
|
||||
// Do actual arrangement row-by-row.
|
||||
var arrangeY = 0f;
|
||||
for (var r = 0; r < _rowDataCache.Length; r++)
|
||||
{
|
||||
ref var row = ref _rowDataCache[r];
|
||||
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
var index = c + r * _columns;
|
||||
|
||||
if (index >= ChildCount) // Quit early if we don't actually fill out the row.
|
||||
break;
|
||||
var child = GetChild(c + r * _columns);
|
||||
|
||||
child.Arrange(UIBox2.FromDimensions(column.ArrangedX, arrangeY, column.ArrangedWidth, row.MeasuredHeight));
|
||||
}
|
||||
|
||||
arrangeY += row.MeasuredHeight;
|
||||
}
|
||||
|
||||
return finalSize with { Y = arrangeY };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure cached array space is allocated to correct size and is reset to a clean slate.
|
||||
/// </summary>
|
||||
private void ResetCachedArrays()
|
||||
{
|
||||
// 1-argument Array.Clear() is not currently available in sandbox (added in .NET 6).
|
||||
|
||||
if (_columnDataCache.Length != _columns)
|
||||
_columnDataCache = new ColumnData[_columns];
|
||||
|
||||
Array.Clear(_columnDataCache, 0, _columnDataCache.Length);
|
||||
|
||||
var rowCount = ChildCount / _columns;
|
||||
if (ChildCount % _columns != 0)
|
||||
rowCount += 1;
|
||||
|
||||
if (rowCount != _rowDataCache.Length)
|
||||
_rowDataCache = new RowData[rowCount];
|
||||
|
||||
Array.Clear(_rowDataCache, 0, _rowDataCache.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-column data used during layout.
|
||||
/// </summary>
|
||||
private struct ColumnData
|
||||
{
|
||||
// Measure data.
|
||||
|
||||
/// <summary>
|
||||
/// The maximum width any control in this column wants, if given infinite space.
|
||||
/// Maximum of all controls on the column.
|
||||
/// </summary>
|
||||
public float MaxWidth;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum width this column may be given.
|
||||
/// This is either <see cref="MaxWidth"/> or <see cref="TableContainer.MinForcedColumnWidth"/>.
|
||||
/// </summary>
|
||||
public float MinWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Difference between max and min width; how much this column can expand from its minimum.
|
||||
/// </summary>
|
||||
public float Slack;
|
||||
|
||||
/// <summary>
|
||||
/// How much horizontal space this column was assigned at measure time.
|
||||
/// </summary>
|
||||
public float AssignedWidth;
|
||||
|
||||
// Arrange data.
|
||||
|
||||
/// <summary>
|
||||
/// How much horizontal space this column was assigned at arrange time.
|
||||
/// </summary>
|
||||
public float ArrangedWidth;
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal position this column was assigned at arrange time.
|
||||
/// </summary>
|
||||
public float ArrangedX;
|
||||
}
|
||||
|
||||
private struct RowData
|
||||
{
|
||||
// Measure data.
|
||||
|
||||
/// <summary>
|
||||
/// How much height the tallest control on this row was measured at,
|
||||
/// measuring for infinite vertical space but assigned column width.
|
||||
/// </summary>
|
||||
public float MeasuredHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
<DevWindowTabUI Name="UI" />
|
||||
<DevWindowTabPerf Name="Perf" />
|
||||
<DevWindowTabTextures Name="Textures" />
|
||||
<DevWindowTabRenderTargets Name="RenderTargets" />
|
||||
</TabContainer>
|
||||
</Control>
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Robust.Client.UserInterface
|
||||
TabContainer.SetTabTitle(UI, "User Interface");
|
||||
TabContainer.SetTabTitle(Perf, "Profiling");
|
||||
TabContainer.SetTabTitle(Textures, Loc.GetString("dev-window-tab-textures-title"));
|
||||
TabContainer.SetTabTitle(RenderTargets, Loc.GetString("dev-window-tab-render-targets-title"));
|
||||
|
||||
Stylesheet =
|
||||
new DefaultStylesheet(IoCManager.Resolve<IResourceCache>(), IoCManager.Resolve<IUserInterfaceManager>()).Stylesheet;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="Robust.Client.UserInterface.DevWindowTabRenderTargets">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Text="{Loc 'dev-window-tab-render-targets-reload'}" Name="ReloadButton" />
|
||||
</BoxContainer>
|
||||
<LineEdit Name="FilterEdit" PlaceHolder="{Loc 'dev-window-tab-render-targets-filter'}" />
|
||||
<ScrollContainer VerticalExpand="True" VScrollEnabled="True" HScrollEnabled="False">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<TableContainer Name="Table" Margin="2" Columns="6">
|
||||
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-id'}" />
|
||||
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-name'}" />
|
||||
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-size'}" />
|
||||
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-type'}" />
|
||||
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-vram'}" />
|
||||
<Label Margin="4" Text="{Loc 'dev-window-tab-render-targets-column-thumbnail'}" />
|
||||
</TableContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="TotalLabel" Margin="4" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
</Control>
|
||||
@@ -0,0 +1,242 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user