Skip to content

Commit ac894ac

Browse files
committed
VRAM stats
1 parent 7699489 commit ac894ac

File tree

16 files changed

+456
-11
lines changed

16 files changed

+456
-11
lines changed

Intersect.Client.Core/Interface/Debugging/DebugWindow.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Intersect.Client.Interface.Debugging.Providers;
1818
using Intersect.Client.Localization;
1919
using Intersect.Client.Maps;
20+
using Intersect.Client.MonoGame.NativeInterop.OpenGL;
2021
using Intersect.Framework.Reflection;
2122
using static Intersect.Client.Framework.File_Management.GameContentManager;
2223

@@ -405,7 +406,13 @@ private Table CreateInfoTableDebugStats(Base parent)
405406
table.AddRow(Strings.Debug.LightsDrawn, name: "LightsDrawnRow").Listen(1, new DelegateDataProvider<int>(() => Graphics.LightsDrawn), NoValue);
406407
table.AddRow(Strings.Debug.InterfaceObjects, name: "InterfaceObjectsRow").Listen(1, new DelegateDataProvider<int?>(() => Interface.CurrentInterface?.NodeCount, delayMilliseconds: 1000), NoValue);
407408

408-
var titleRow = table.AddRow(Strings.Debug.ControlUnderCursor, columnCount: 2, name: "ControlUnderCursorRow", columnIndex: 1);
409+
_ = table.AddRow(Strings.Debug.SectionGPUStatistics, columnCount: 2, name: "SectionGPU", columnIndex: 1);
410+
411+
table.AddRow(Strings.Debug.RenderBufferVRAMFree, name: "GPUVRAMRenderBuffers").Listen(1, new DelegateDataProvider<string>(() => Strings.FormatBytes(GL.AvailableRenderBufferMemory)), NoValue);
412+
table.AddRow(Strings.Debug.TextureVRAMFree, name: "GPUVRAMTextures").Listen(1, new DelegateDataProvider<string>(() => Strings.FormatBytes(GL.AvailableTextureMemory)), NoValue);
413+
table.AddRow(Strings.Debug.VBOVRAMFree, name: "GPUVRAMVBOs").Listen(1, new DelegateDataProvider<string>(() => Strings.FormatBytes(GL.AvailableVBOMemory)), NoValue);
414+
415+
_ = table.AddRow(Strings.Debug.ControlUnderCursor, columnCount: 2, name: "SectionUI", columnIndex: 1);
409416

410417
table.AddRow(Strings.Internals.Type, name: "TypeRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.GetType().GetName(), Strings.Internals.NotApplicable);
411418
table.AddRow(Strings.Internals.Name, name: "NameRow").Listen(1, _nodeUnderCursorProvider, (node, _) => node?.ParentQualifiedName, NoValue);
@@ -435,7 +442,7 @@ private Table CreateInfoTableDebugStats(Base parent)
435442
var rows = table.Children.OfType<TableRow>().ToArray();
436443
foreach (var row in rows)
437444
{
438-
if (row == titleRow && row.GetCellContents(1) is Label titleLabel)
445+
if (row.Name.StartsWith("Section") && row.GetCellContents(1) is Label titleLabel)
439446
{
440447
titleLabel.Padding = titleLabel.Padding with { Top = 8 };
441448
}

Intersect.Client.Core/Localization/Strings.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,31 @@ public static partial class Strings
2020
private const string StringsFileName = "client_strings.json";
2121
private static char[] mQuantityTrimChars = new char[] { '.', '0' };
2222

23+
private static string[] _unitsBits = [string.Empty, "Ki", "Mi", "Gi", "Ti"];
24+
private static string[] _unitsBytes = [string.Empty, "K", "M", "G", "T"];
25+
26+
public static string FormatBits(long quantity)
27+
{
28+
var log = quantity < 2 ? 0 : Math.Log2(quantity);
29+
var offsetLog = Math.Max(0, Math.Floor(log - 3.3));
30+
var unitIndex = (int)Math.Clamp(Math.Floor(offsetLog / 10), 0, _unitsBits.Length - 1);
31+
var divisor = Math.Pow(2, unitIndex * 10);
32+
var quotient = quantity / divisor;
33+
var unitPrefix = _unitsBits[unitIndex];
34+
return $"{quotient:0.##}{unitPrefix}B";
35+
}
36+
37+
public static string FormatBytes(long quantity)
38+
{
39+
var log = quantity < 10 ? 0 : Math.Floor(Math.Log10(quantity));
40+
var offsetLog = Math.Max(0, log - 1);
41+
var unitIndex = (int)Math.Clamp(Math.Floor(offsetLog / 3), 0, _unitsBytes.Length - 1);
42+
var divisor = Math.Pow(10, unitIndex * 3);
43+
var quotient = quantity / divisor;
44+
var unitPrefix = _unitsBytes[unitIndex];
45+
return $"{quotient:0.##}{unitPrefix}B";
46+
}
47+
2348
public static string FormatQuantityAbbreviated(long value)
2449
{
2550
if (value == 0)
@@ -930,6 +955,18 @@ public partial struct Credits
930955

931956
public partial struct Debug
932957
{
958+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
959+
public static LocalizedString SectionGPUStatistics = @"GPU Statistics";
960+
961+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
962+
public static LocalizedString RenderBufferVRAMFree = @"Render Buffer VRAM Free";
963+
964+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
965+
public static LocalizedString TextureVRAMFree = @"Texture VRAM Free";
966+
967+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
968+
public static LocalizedString VBOVRAMFree = @"VBO VRAM Free";
969+
933970
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
934971
public static LocalizedString ControlUnderCursor = @"Control Under Cursor";
935972

Intersect.Client.Core/MonoGame/Graphics/MonoRenderer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Intersect.Client.Interface.Shared;
1010
using Intersect.Client.Localization;
1111
using Intersect.Client.MonoGame.NativeInterop;
12+
using Intersect.Client.MonoGame.NativeInterop.OpenGL;
1213
using Intersect.Client.ThirdParty;
1314
using Intersect.Configuration;
1415
using Intersect.Extensions;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Runtime.InteropServices;
3+
4+
namespace Intersect.Client.MonoGame.NativeInterop.OpenGL;
5+
6+
[SuppressMessage("ReSharper", "InconsistentNaming")]
7+
public static partial class GL
8+
{
9+
private static bool IsATI_meminfoSupported => IsExtensionSupported("GL_ATI_meminfo");
10+
11+
[StructLayout(LayoutKind.Sequential)]
12+
private struct ATI_meminfo_Tuple
13+
{
14+
public int FreeInPool;
15+
public int LargestFreeBlockInPool;
16+
public int FreeInAuxiliaryPool;
17+
public int LargestFreeBlockInAuxiliaryPool;
18+
}
19+
20+
private static unsafe ATI_meminfo_Tuple glGetATIMemInfo(GLenum name)
21+
{
22+
ATI_meminfo_Tuple data = default;
23+
int* p_data = &data.FreeInPool;
24+
glGetIntegerv_f(name, p_data);
25+
return data;
26+
}
27+
}
28+
29+
/*
30+
VBO_FREE_MEMORY_ATI 0x87FB
31+
TEXTURE_FREE_MEMORY_ATI 0x87FC
32+
RENDERBUFFER_FREE_MEMORY_ATI 0x87FD*/
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Intersect.Client.MonoGame.NativeInterop.OpenGL;
2+
3+
public static partial class GL
4+
{
5+
private static HashSet<string>? _cachedExtensions;
6+
7+
public static HashSet<string> glGetExtensions()
8+
{
9+
// ReSharper disable once InvertIf
10+
if (_cachedExtensions is not { Count: > 0 })
11+
{
12+
var numExtensions = glGetIntegerv(GLenum.GL_NUM_EXTENSIONS);
13+
var extensions = glGetStrings(GLenum.GL_EXTENSIONS, numExtensions);
14+
_cachedExtensions = extensions.OfType<string>().ToHashSet();
15+
}
16+
17+
return _cachedExtensions;
18+
}
19+
20+
public static bool IsExtensionSupported(string extensionName) => glGetExtensions().Contains(extensionName);
21+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
namespace Intersect.Client.MonoGame.NativeInterop.OpenGL;
2+
3+
public static partial class GL
4+
{
5+
/// <summary>
6+
/// Available amount of memory for render buffers in bytes
7+
/// </summary>
8+
public static long AvailableRenderBufferMemory
9+
{
10+
get
11+
{
12+
if (IsATI_meminfoSupported)
13+
{
14+
var info = glGetATIMemInfo(GLenum.RENDERBUFFER_FREE_MEMORY_ATI);
15+
return info.FreeInPool * 1000L;
16+
}
17+
18+
if (IsNVX_gpu_memory_infoSupported)
19+
{
20+
return glGetNVXGPUMemoryInfo(GLenum.GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX);
21+
}
22+
23+
return -1;
24+
}
25+
}
26+
27+
/// <summary>
28+
/// Available amount of memory for textures in bytes
29+
/// </summary>
30+
public static long AvailableTextureMemory
31+
{
32+
get
33+
{
34+
if (IsATI_meminfoSupported)
35+
{
36+
var info = glGetATIMemInfo(GLenum.TEXTURE_FREE_MEMORY_ATI);
37+
return info.FreeInPool * 1000L;
38+
}
39+
40+
if (IsNVX_gpu_memory_infoSupported)
41+
{
42+
return glGetNVXGPUMemoryInfo(GLenum.GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX);
43+
}
44+
45+
return -1;
46+
}
47+
}
48+
49+
/// <summary>
50+
/// Available amount of memory for VBOs in bytes
51+
/// </summary>
52+
public static long AvailableVBOMemory
53+
{
54+
get
55+
{
56+
if (IsATI_meminfoSupported)
57+
{
58+
var info = glGetATIMemInfo(GLenum.VBO_FREE_MEMORY_ATI);
59+
return info.FreeInPool * 1000L;
60+
}
61+
62+
if (IsNVX_gpu_memory_infoSupported)
63+
{
64+
return glGetNVXGPUMemoryInfo(GLenum.GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX);
65+
}
66+
67+
return -1;
68+
}
69+
}
70+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Intersect.Client.MonoGame.NativeInterop.OpenGL;
2+
3+
public static partial class GL
4+
{
5+
public static bool IsNVX_gpu_memory_infoSupported => IsExtensionSupported("GL_NVX_gpu_memory_info");
6+
7+
private static long glGetNVXGPUMemoryInfo(GLenum name)
8+
{
9+
return glGetIntegerv(name);
10+
}
11+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace Intersect.Client.MonoGame.NativeInterop.OpenGL;
4+
5+
public static partial class GL
6+
{
7+
private static TDelegate LoadFunction<TDelegate>(string function)
8+
{
9+
return LoadFunction<TDelegate>(function, false)!;
10+
}
11+
12+
private static T? LoadFunction<T>(string function, bool throwIfNotFound)
13+
{
14+
var ret = Sdl2.SDL_GL_GetProcAddress(function);
15+
16+
if (ret != IntPtr.Zero)
17+
{
18+
return Marshal.GetDelegateForFunctionPointer<T>(ret);
19+
}
20+
21+
if (throwIfNotFound)
22+
{
23+
throw new EntryPointNotFoundException(function);
24+
}
25+
26+
return default;
27+
}
28+
29+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
30+
private delegate GLenum glGetError_d();
31+
32+
private static readonly glGetError_d glGetError_f = LoadFunction<glGetError_d>(nameof(glGetError));
33+
34+
public static GLenum glGetError()
35+
{
36+
return glGetError_f();
37+
}
38+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace Intersect.Client.MonoGame.NativeInterop.OpenGL;
2+
3+
public enum GLenum
4+
{
5+
#region Utility
6+
7+
GL_VENDOR = 0x1F00,
8+
GL_RENDERER = 0x1F01,
9+
GL_VERSION = 0x1F02,
10+
GL_EXTENSIONS = 0x1F03,
11+
12+
#endregion Utility
13+
14+
#region Errors
15+
16+
GL_NO_ERROR = 0,
17+
GL_INVALID_ENUM = 0x0500,
18+
GL_INVALID_VALUE = 0x0501,
19+
GL_INVALID_OPERATION = 0x0502,
20+
GL_STACK_OVERFLOW = 0x0503,
21+
GL_STACK_UNDERFLOW = 0x0504,
22+
GL_OUT_OF_MEMORY = 0x0505,
23+
24+
#endregion Errors
25+
26+
GL_NUM_EXTENSIONS = 0x821D,
27+
28+
#region ATI_meminfo
29+
30+
VBO_FREE_MEMORY_ATI = 0x87FB,
31+
TEXTURE_FREE_MEMORY_ATI = 0x87FC,
32+
RENDERBUFFER_FREE_MEMORY_ATI = 0x87FD,
33+
34+
#endregion ATI_meminfo
35+
36+
#region NVX_gpu_memory_info
37+
38+
GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX = 0x9047,
39+
GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX = 0x9048,
40+
GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX = 0x9049,
41+
GPU_MEMORY_INFO_EVICTION_COUNT_NVX = 0x904A,
42+
GPU_MEMORY_INFO_EVICTED_MEMORY_NVX = 0x904B,
43+
44+
#endregion NVX_gpu_memory_info
45+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Text;
2+
3+
namespace Intersect.Client.MonoGame.NativeInterop.OpenGL;
4+
5+
public static partial class GL
6+
{
7+
private static unsafe string? PointerToUTF8(byte* ptr)
8+
{
9+
if (ptr == default)
10+
{
11+
return null;
12+
}
13+
14+
var end = ptr;
15+
while (*end != 0)
16+
{
17+
++end;
18+
}
19+
20+
var length = (int)(end - ptr);
21+
var str = Encoding.UTF8.GetString(ptr, length);
22+
return str;
23+
}
24+
}

0 commit comments

Comments
 (0)