diff --git a/src/main/java/rs117/hd/overlays/FrameTimerOverlay.java b/src/main/java/rs117/hd/overlays/FrameTimerOverlay.java index 810f07de00..4adaaa76e5 100644 --- a/src/main/java/rs117/hd/overlays/FrameTimerOverlay.java +++ b/src/main/java/rs117/hd/overlays/FrameTimerOverlay.java @@ -16,14 +16,7 @@ import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.TitleComponent; import rs117.hd.HdPlugin; -import rs117.hd.renderer.zone.SceneManager; -import rs117.hd.renderer.zone.WorldViewContext; -import rs117.hd.renderer.zone.ZoneRenderer; -import rs117.hd.utils.FrameTimingsRecorder; -import rs117.hd.utils.NpcDisplacementCache; -import rs117.hd.utils.jobs.JobSystem; -import static rs117.hd.renderer.zone.SceneManager.MAX_WORLDVIEWS; import static rs117.hd.utils.MathUtils.*; @Singleton @@ -37,18 +30,6 @@ public class FrameTimerOverlay extends OverlayPanel implements FrameTimer.Listen @Inject private FrameTimer frameTimer; - @Inject - private FrameTimingsRecorder frameTimingsRecorder; - - @Inject - private NpcDisplacementCache npcDisplacementCache; - - @Inject - private JobSystem jobSystem; - - @Inject - private SceneManager sceneManager; - private final ArrayDeque frames = new ArrayDeque<>(); private final long[] timings = new long[Timer.TIMERS.length]; private float cpuLoad; @@ -142,112 +123,6 @@ public Dimension render(Graphics2D g) { .left("Error compensation:") .right(String.format("%d ns", frameTimer.errorCompensation)) .build()); - - children.add(LineComponent.builder() - .left("Garbage collection count:") - .right(String.valueOf(plugin.getGarbageCollectionCount())) - .build()); - - children.add(LineComponent.builder() - .left("Power saving mode:") - .right(plugin.isPowerSaving ? "ON" : "OFF") - .build()); - - children.add(LineComponent.builder() - .leftFont(boldFont) - .left("Scene stats:") - .build()); - - if (plugin.getSceneContext() != null) { - var sceneContext = plugin.getSceneContext(); - children.add(LineComponent.builder() - .left("Lights:") - .right(String.format("%d/%d", sceneContext.numVisibleLights, sceneContext.lights.size())) - .build()); - } - - if (plugin.renderer instanceof ZoneRenderer) { - children.add(LineComponent.builder() - .left("Dynamic renderables:") - .right(String.valueOf(plugin.getDrawnDynamicRenderableCount())) - .build()); - - children.add(LineComponent.builder() - .left("Temp renderables:") - .right(String.valueOf(plugin.getDrawnTempRenderableCount())) - .build()); - } else { - children.add(LineComponent.builder() - .left("Tiles:") - .right(String.valueOf(plugin.getDrawnTileCount())) - .build()); - - children.add(LineComponent.builder() - .left("Static renderables:") - .right(String.valueOf(plugin.getDrawnStaticRenderableCount())) - .build()); - - children.add(LineComponent.builder() - .left("Dynamic renderables:") - .right(String.valueOf(plugin.getDrawnDynamicRenderableCount())) - .build()); - - children.add(LineComponent.builder() - .left("NPC displacement cache size:") - .right(String.valueOf(npcDisplacementCache.size())) - .build()); - } - - children.add(LineComponent.builder() - .leftFont(boldFont) - .left("Streaming Stats:") - .build()); - - WorldViewContext root = sceneManager.getRoot(); - addTiming("Root Scene Load", root.loadTime, false); - addTiming("Root Scene Upload", root.uploadTime, false); - addTiming("Root Scene Swap", root.sceneSwapTime, false); - - // TODO: Maybe this should be calculated somewhere else - int subSceneCount = 0; - long subSceneLoadTime = 0; - long subSceneUploadTime = 0; - long subSceneSwapTime = 0; - - for (int worldViewId = 0; worldViewId < MAX_WORLDVIEWS; worldViewId++) { - WorldViewContext subscene = sceneManager.getContext(worldViewId); - if (subscene != null) { - subSceneCount++; - subSceneLoadTime += subscene.loadTime; - subSceneUploadTime += subscene.uploadTime; - subSceneSwapTime += subscene.sceneSwapTime; - } - } - - if (subSceneCount > 0) { - addTiming("Avg SubScene Load", subSceneLoadTime / subSceneCount, false); - addTiming("Avg SubScene Upload", subSceneUploadTime / subSceneCount, false); - addTiming("Avg SubScene Swap", subSceneSwapTime / subSceneCount, false); - } - - children.add(LineComponent.builder() - .left("Sub Scene Count:") - .right(String.valueOf(subSceneCount)) - .build()); - - - children.add(LineComponent.builder() - .left("Streaming Zones:") - .right(String.valueOf(jobSystem.getWorkQueueSize())) - .build()); - - if (frameTimingsRecorder.isCapturingSnapshot()) - children.add(LineComponent.builder() - .leftFont(boldFont) - .left("Capturing Snapshot...") - .rightFont(boldFont) - .right(String.format("%d%%", frameTimingsRecorder.getProgressPercentage())) - .build()); } var result = super.render(g); diff --git a/src/main/java/rs117/hd/overlays/StatsOverlay.java b/src/main/java/rs117/hd/overlays/StatsOverlay.java new file mode 100644 index 0000000000..d85e2287d4 --- /dev/null +++ b/src/main/java/rs117/hd/overlays/StatsOverlay.java @@ -0,0 +1,209 @@ +package rs117.hd.overlays; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.HashMap; +import java.util.Map; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.OverlayPanel; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; +import rs117.hd.HdPlugin; +import rs117.hd.renderer.zone.SceneManager; +import rs117.hd.renderer.zone.WorldViewContext; +import rs117.hd.renderer.zone.ZoneRenderer; +import rs117.hd.utils.FrameTimingsRecorder; +import rs117.hd.utils.NpcDisplacementCache; +import rs117.hd.utils.jobs.JobSystem; + +import static rs117.hd.renderer.zone.SceneManager.MAX_WORLDVIEWS; +import static rs117.hd.utils.MathUtils.*; + +@Singleton +public class StatsOverlay extends OverlayPanel { + @Inject + private OverlayManager overlayManager; + + @Inject + private HdPlugin plugin; + + @Inject + private SceneManager sceneManager; + + @Inject + private JobSystem jobSystem; + + @Inject + private FrameTimingsRecorder frameTimingsRecorder; + + @Inject + private NpcDisplacementCache npcDisplacementCache; + + private final Map componentMap = new HashMap<>(); + private final StringBuilder sb = new StringBuilder(); + + @Inject + public StatsOverlay(HdPlugin plugin) { + super(plugin); + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.TOP_RIGHT); + panelComponent.setPreferredSize(new Dimension(215, 200)); + } + + public void setActive(boolean activate) { + if (activate) { + overlayManager.add(this); + } else { + overlayManager.remove(this); + } + } + + @Override + public Dimension render(Graphics2D g) { + var boldFont = FontManager.getRunescapeBoldFont(); + var children = panelComponent.getChildren(); + + children.add(TitleComponent.builder() + .text("Stats (non-timers)") + .build()); + + children.add(LineComponent.builder() + .left("Garbage collection count:") + .right(String.valueOf(plugin.getGarbageCollectionCount())) + .build()); + + children.add(LineComponent.builder() + .left("Power saving mode:") + .right(plugin.isPowerSaving ? "ON" : "OFF") + .build()); + + children.add(LineComponent.builder() + .leftFont(boldFont) + .left("Scene stats:") + .build()); + + if (plugin.getSceneContext() != null) { + var sceneContext = plugin.getSceneContext(); + children.add(LineComponent.builder() + .left("Lights:") + .right(String.format("%d/%d", sceneContext.numVisibleLights, sceneContext.lights.size())) + .build()); + } + + if (plugin.renderer instanceof ZoneRenderer) { + children.add(LineComponent.builder() + .left("Dynamic renderables:") + .right(String.valueOf(plugin.getDrawnDynamicRenderableCount())) + .build()); + + children.add(LineComponent.builder() + .left("Temp renderables:") + .right(String.valueOf(plugin.getDrawnTempRenderableCount())) + .build()); + } else { + children.add(LineComponent.builder() + .left("Tiles:") + .right(String.valueOf(plugin.getDrawnTileCount())) + .build()); + + children.add(LineComponent.builder() + .left("Static renderables:") + .right(String.valueOf(plugin.getDrawnStaticRenderableCount())) + .build()); + + children.add(LineComponent.builder() + .left("Dynamic renderables:") + .right(String.valueOf(plugin.getDrawnDynamicRenderableCount())) + .build()); + + children.add(LineComponent.builder() + .left("NPC displacement cache size:") + .right(String.valueOf(npcDisplacementCache.size())) + .build()); + } + + children.add(LineComponent.builder() + .leftFont(boldFont) + .left("Streaming Stats:") + .build()); + + WorldViewContext root = sceneManager.getRoot(); + addTiming("Root Scene Load", root.loadTime, false); + addTiming("Root Scene Upload", root.uploadTime, false); + addTiming("Root Scene Swap", root.sceneSwapTime, false); + + int subSceneCount = 0; + long subSceneLoadTime = 0; + long subSceneUploadTime = 0; + long subSceneSwapTime = 0; + + for (int worldViewId = 0; worldViewId < MAX_WORLDVIEWS; worldViewId++) { + WorldViewContext subscene = sceneManager.getContext(worldViewId); + if (subscene != null) { + subSceneCount++; + subSceneLoadTime += subscene.loadTime; + subSceneUploadTime += subscene.uploadTime; + subSceneSwapTime += subscene.sceneSwapTime; + } + } + + if (subSceneCount > 0) { + addTiming("Avg SubScene Load", subSceneLoadTime / subSceneCount, false); + addTiming("Avg SubScene Upload", subSceneUploadTime / subSceneCount, false); + addTiming("Avg SubScene Swap", subSceneSwapTime / subSceneCount, false); + } + + children.add(LineComponent.builder() + .left("Sub Scene Count:") + .right(String.valueOf(subSceneCount)) + .build()); + + children.add(LineComponent.builder() + .left("Streaming Zones:") + .right(String.valueOf(jobSystem.getWorkQueueSize())) + .build()); + + if (frameTimingsRecorder.isCapturingSnapshot()) { + children.add(LineComponent.builder() + .leftFont(boldFont) + .left("Capturing Snapshot...") + .rightFont(boldFont) + .right(String.format("%d%%", frameTimingsRecorder.getProgressPercentage())) + .build()); + } + + return super.render(g); + } + + private void addTiming(String name, long nanos, boolean bold) { + if (nanos == 0) + return; + + String result = "~0 ms"; + if (abs(nanos) > 1e3) { + result = sb.append(round(nanos / 1e3) / 1e3).append(" ms").toString(); + sb.setLength(0); + } + + LineComponent component = componentMap.get(name); + if (component == null) { + var font = bold ? FontManager.getRunescapeBoldFont() : FontManager.getRunescapeFont(); + component = LineComponent.builder() + .left(name + ":") + .leftFont(font) + .right(result) + .rightFont(font) + .build(); + componentMap.put(name, component); + } else { + component.setRight(result); + } + + panelComponent.getChildren().add(component); + } +} diff --git a/src/main/java/rs117/hd/utils/DeveloperTools.java b/src/main/java/rs117/hd/utils/DeveloperTools.java index 36d42b6171..cd535d5681 100644 --- a/src/main/java/rs117/hd/utils/DeveloperTools.java +++ b/src/main/java/rs117/hd/utils/DeveloperTools.java @@ -14,6 +14,7 @@ import rs117.hd.HdPlugin; import rs117.hd.overlays.FrameTimerOverlay; import rs117.hd.overlays.LightGizmoOverlay; +import rs117.hd.overlays.StatsOverlay; import rs117.hd.overlays.ShadowMapOverlay; import rs117.hd.overlays.TileInfoOverlay; import rs117.hd.overlays.TiledLightingOverlay; @@ -33,6 +34,7 @@ public class DeveloperTools implements KeyListener { private static final Keybind KEY_TOGGLE_SHADOW_MAP_OVERLAY = new Keybind(KeyEvent.VK_F5, CTRL_DOWN_MASK); private static final Keybind KEY_TOGGLE_LIGHT_GIZMO_OVERLAY = new Keybind(KeyEvent.VK_F6, CTRL_DOWN_MASK); private static final Keybind KEY_TOGGLE_TILED_LIGHTING_OVERLAY = new Keybind(KeyEvent.VK_F7, CTRL_DOWN_MASK); + private static final Keybind KEY_TOGGLE_STREAMING_STATS = new Keybind(KeyEvent.VK_F8, CTRL_DOWN_MASK); private static final Keybind KEY_TOGGLE_FREEZE_FRAME = new Keybind(KeyEvent.VK_ESCAPE, SHIFT_DOWN_MASK); private static final Keybind KEY_TOGGLE_ORTHOGRAPHIC = new Keybind(KeyEvent.VK_TAB, SHIFT_DOWN_MASK); private static final Keybind KEY_TOGGLE_HIDE_UI = new Keybind(KeyEvent.VK_H, CTRL_DOWN_MASK); @@ -56,6 +58,9 @@ public class DeveloperTools implements KeyListener { @Inject private FrameTimerOverlay frameTimerOverlay; + @Inject + private StatsOverlay statsOverlay; + @Inject private FrameTimingsRecorder frameTimingsRecorder; @@ -72,6 +77,7 @@ public class DeveloperTools implements KeyListener { private boolean tileInfoOverlayEnabled; @Getter private boolean frameTimingsOverlayEnabled; + private boolean statsOverlayEnabled; private boolean shadowMapOverlayEnabled; private boolean lightGizmoOverlayEnabled; @Getter @@ -93,6 +99,7 @@ public void activate() { clientThread.invokeLater(() -> { tileInfoOverlay.setActive(tileInfoOverlayEnabled); frameTimerOverlay.setActive(frameTimingsOverlayEnabled); + statsOverlay.setActive(statsOverlayEnabled); shadowMapOverlay.setActive(shadowMapOverlayEnabled); lightGizmoOverlay.setActive(lightGizmoOverlayEnabled); tiledLightingOverlay.setActive(tiledLightingOverlayEnabled); @@ -117,6 +124,7 @@ public void deactivate() { keyManager.unregisterKeyListener(this); tileInfoOverlay.setActive(false); frameTimerOverlay.setActive(false); + statsOverlay.setActive(false); shadowMapOverlay.setActive(false); lightGizmoOverlay.setActive(false); tiledLightingOverlay.setActive(false); @@ -141,6 +149,9 @@ public void onCommandExecuted(CommandExecuted commandExecuted) { case "timings": frameTimerOverlay.setActive(frameTimingsOverlayEnabled = !frameTimingsOverlayEnabled); break; + case "stats": + statsOverlay.setActive(statsOverlayEnabled = !statsOverlayEnabled); + break; case "snapshot": frameTimingsRecorder.recordSnapshot(); break; @@ -178,6 +189,8 @@ public void keyPressed(KeyEvent e) { tileInfoOverlay.setActive(tileInfoOverlayEnabled = !tileInfoOverlayEnabled); } else if (KEY_TOGGLE_FRAME_TIMINGS.matches(e)) { frameTimerOverlay.setActive(frameTimingsOverlayEnabled = !frameTimingsOverlayEnabled); + } else if (KEY_TOGGLE_STREAMING_STATS.matches(e)) { + statsOverlay.setActive(statsOverlayEnabled = !statsOverlayEnabled); } else if (KEY_RECORD_TIMINGS_SNAPSHOT.matches(e)) { frameTimingsRecorder.recordSnapshot(); } else if (KEY_TOGGLE_SHADOW_MAP_OVERLAY.matches(e)) {