memory reports

This commit is contained in:
Jan Racek
2026-03-27 19:29:41 +01:00
parent 24d0c1fa07
commit 45a552be5a
9 changed files with 823 additions and 2 deletions

View File

@@ -42,6 +42,10 @@ namespace SplashEdit.EditorCode
// ───── Scene List ─────
private List<SceneEntry> _sceneList = new List<SceneEntry>();
// ───── Memory Reports ─────
private List<SceneMemoryReport> _memoryReports = new List<SceneMemoryReport>();
private bool _showMemoryReport = true;
// ───── Toolchain Cache ─────
private bool _hasMIPS;
private bool _hasMake;
@@ -585,6 +589,11 @@ namespace SplashEdit.EditorCode
// Clean Build toggle
SplashSettings.CleanBuild = EditorGUILayout.Toggle("Clean Build", SplashSettings.CleanBuild);
// Memory Overlay toggle
SplashSettings.MemoryOverlay = EditorGUILayout.Toggle(
new GUIContent("Memory Overlay", "Show heap/RAM usage bar at top-right during gameplay"),
SplashSettings.MemoryOverlay);
// Serial port (only for Real Hardware)
if (SplashSettings.Target == BuildTarget.RealHardware)
{
@@ -680,6 +689,145 @@ namespace SplashEdit.EditorCode
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
// Memory report (shown after export)
if (_memoryReports.Count > 0)
{
EditorGUILayout.Space(8);
DrawMemoryReports();
}
}
// ═══════════════════════════════════════════════════════════════
// Memory Reports
// ═══════════════════════════════════════════════════════════════
private void DrawMemoryReports()
{
_showMemoryReport = DrawSectionFoldout("Memory Report", _showMemoryReport);
if (!_showMemoryReport) return;
foreach (var report in _memoryReports)
{
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
GUILayout.Label($"Scene: {report.sceneName}", PSXEditorStyles.SectionHeader);
EditorGUILayout.Space(4);
// Main RAM bar
DrawMemoryBar("Main RAM",
report.TotalRamUsage, SceneMemoryReport.USABLE_RAM,
report.RamPercent,
new Color(0.3f, 0.6f, 1f),
$"Scene: {FormatBytes(report.SceneRamUsage)} | " +
$"Fixed: {FormatBytes(report.FixedOverhead)} | " +
$"Free: {FormatBytes(report.RamFree)}");
EditorGUILayout.Space(4);
// VRAM bar
DrawMemoryBar("VRAM",
report.TotalVramUsed, SceneMemoryReport.TOTAL_VRAM,
report.VramPercent,
new Color(0.9f, 0.5f, 0.2f),
$"FB: {FormatBytes(report.framebufferSize)} | " +
$"Tex: {FormatBytes(report.textureAtlasSize)} | " +
$"CLUT: {FormatBytes(report.clutSize)} | " +
$"Free: {FormatBytes(report.VramFree)}");
EditorGUILayout.Space(4);
// SPU RAM bar
DrawMemoryBar("SPU RAM",
report.TotalSpuUsed, SceneMemoryReport.USABLE_SPU,
report.SpuPercent,
new Color(0.6f, 0.3f, 0.9f),
report.audioClipCount > 0
? $"{report.audioClipCount} clips | {FormatBytes(report.audioDataSize)} | Free: {FormatBytes(report.SpuFree)}"
: "No audio clips");
EditorGUILayout.Space(4);
// CD Storage (no bar, just info)
EditorGUILayout.BeginHorizontal();
GUILayout.Label("CD Storage:", EditorStyles.miniLabel, GUILayout.Width(70));
GUILayout.Label(
$"Scene: {FormatBytes(report.splashpackFileSize)}" +
(report.loaderPackSize > 0 ? $" | Loader: {FormatBytes(report.loaderPackSize)}" : "") +
$" | Total: {FormatBytes(report.TotalDiscSize)}",
EditorStyles.miniLabel);
EditorGUILayout.EndHorizontal();
// Summary stats
PSXEditorStyles.DrawSeparator(4, 4);
EditorGUILayout.LabelField(
$"<b>{report.gameObjectCount}</b> objects | " +
$"<b>{report.triangleCount}</b> tris | " +
$"<b>{report.atlasCount}</b> atlases | " +
$"<b>{report.clutCount}</b> CLUTs",
PSXEditorStyles.RichLabel);
EditorGUILayout.EndVertical();
EditorGUILayout.Space(4);
}
}
private void DrawMemoryBar(string label, long used, long total, float percent, Color barColor, string details)
{
// Label row
EditorGUILayout.BeginHorizontal();
GUILayout.Label(label, EditorStyles.boldLabel, GUILayout.Width(70));
GUILayout.Label($"{FormatBytes(used)} / {FormatBytes(total)} ({percent:F1}%)", EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
// Progress bar
Rect barRect = GUILayoutUtility.GetRect(0, 16, GUILayout.ExpandWidth(true));
// Background
EditorGUI.DrawRect(barRect, new Color(0.15f, 0.15f, 0.17f));
// Fill
float fillFraction = Mathf.Clamp01((float)used / total);
Rect fillRect = new Rect(barRect.x, barRect.y, barRect.width * fillFraction, barRect.height);
// Color shifts toward red when over 80%
Color fillColor = barColor;
if (percent > 90f)
fillColor = Color.Lerp(PSXEditorStyles.Warning, PSXEditorStyles.Error, (percent - 90f) / 10f);
else if (percent > 80f)
fillColor = Color.Lerp(barColor, PSXEditorStyles.Warning, (percent - 80f) / 10f);
EditorGUI.DrawRect(fillRect, fillColor);
// Border
DrawRectOutline(barRect, new Color(0.3f, 0.3f, 0.35f));
// Percent text overlay
var style = new GUIStyle(EditorStyles.miniLabel)
{
alignment = TextAnchor.MiddleCenter,
normal = { textColor = Color.white }
};
GUI.Label(barRect, $"{percent:F1}%", style);
// Details row
GUILayout.Label(details, EditorStyles.miniLabel);
}
private static void DrawRectOutline(Rect rect, Color color)
{
EditorGUI.DrawRect(new Rect(rect.x, rect.y, rect.width, 1), color);
EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - 1, rect.width, 1), color);
EditorGUI.DrawRect(new Rect(rect.x, rect.y, 1, rect.height), color);
EditorGUI.DrawRect(new Rect(rect.xMax - 1, rect.y, 1, rect.height), color);
}
private static string FormatBytes(long bytes)
{
if (bytes < 0) return "N/A";
if (bytes < 1024) return $"{bytes} B";
if (bytes < 1024 * 1024) return $"{bytes / 1024f:F1} KB";
return $"{bytes / (1024f * 1024f):F2} MB";
}
// ═══════════════════════════════════════════════════════════════
@@ -808,6 +956,7 @@ namespace SplashEdit.EditorCode
{
SplashBuildPaths.EnsureDirectories();
_loaderPackCache = new Dictionary<string, string>();
_memoryReports.Clear();
// Save current scene
string currentScenePath = SceneManager.GetActiveScene().path;
@@ -841,15 +990,34 @@ namespace SplashEdit.EditorCode
// Export to the build directory
string outputPath = SplashBuildPaths.GetSceneSplashpackPath(i, scene.name);
string loaderPath = null;
exporter.ExportToPath(outputPath);
Log($"Exported '{scene.name}' → {Path.GetFileName(outputPath)}", LogType.Log);
// Export loading screen if assigned
if (exporter.LoadingScreenPrefab != null)
{
string loaderPath = SplashBuildPaths.GetSceneLoaderPackPath(i, scene.name);
loaderPath = SplashBuildPaths.GetSceneLoaderPackPath(i, scene.name);
ExportLoaderPack(exporter.LoadingScreenPrefab, loaderPath, i, scene.name);
}
// Generate memory report for this scene
try
{
var report = SceneMemoryAnalyzer.Analyze(
scene.name,
outputPath,
loaderPath,
exporter.LastExportAtlases,
exporter.LastExportAudioSizes,
exporter.LastExportFonts,
exporter.LastExportTriangleCount);
_memoryReports.Add(report);
}
catch (Exception reportEx)
{
Log($"Memory report for '{scene.name}' failed: {reportEx.Message}", LogType.Warning);
}
}
catch (Exception ex)
{
@@ -1017,6 +1185,9 @@ namespace SplashEdit.EditorCode
if (SplashSettings.Target == BuildTarget.ISO)
buildArg += " LOADER=cdrom";
if (SplashSettings.MemoryOverlay)
buildArg += " MEMOVERLAY=1";
int jobCount = Math.Max(1, SystemInfo.processorCount - 1);
string cleanPrefix = SplashSettings.CleanBuild ? "make clean && " : "";
string makeCmd = $"{cleanPrefix}make all -j{jobCount} {buildArg}".Trim();