Add CPU load graph to Cocoa, closes #654

This commit is contained in:
Lior Halphon
2025-06-06 23:10:57 +03:00
parent f706988171
commit 6ab1be654b
7 changed files with 141 additions and 7 deletions
+3
View File
@@ -3,6 +3,7 @@
#import "GBImageView.h"
#import "GBSplitView.h"
#import "GBVisualizerView.h"
#import "GBCPUView.h"
#import "GBOSDView.h"
#import "GBDebuggerButton.h"
@@ -84,6 +85,8 @@ enum model {
@property IBOutlet NSScrollView *debuggerScrollView;
@property IBOutlet NSView *debugBar;
@property IBOutlet GBCPUView *cpuView;
@property IBOutlet NSTextField *cpuCounter;
+ (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale;
- (void) performAtomicBlock: (void (^)())block;
+16
View File
@@ -364,6 +364,18 @@ static void debuggerReloadCallback(GB_gameboy_t *gb)
[_gbsVisualizer setNeedsDisplay:true];
});
}
double frameUsage = GB_debugger_get_frame_cpu_usage(&_gb);
[_cpuView addSample:frameUsage];
if (self.consoleWindow.visible) {
double secondUsage = GB_debugger_get_second_cpu_usage(&_gb);
dispatch_async(dispatch_get_main_queue(), ^{
[_cpuView setNeedsDisplay:true];
_cpuCounter.stringValue = [NSString stringWithFormat:@"%.2f%%", secondUsage * 100];
});
}
if (type != GB_VBLANK_TYPE_REPEAT) {
[self.view flip];
if (_borderModeChanged) {
@@ -1325,6 +1337,8 @@ static bool is_path_writeable(const char *path)
GB_debugger_break(&_gb);
[self start];
[self.consoleWindow makeKeyAndOrderFront:nil];
double secondUsage = GB_debugger_get_second_cpu_usage(&_gb);
_cpuCounter.stringValue = [NSString stringWithFormat:@"%.2f%%", secondUsage * 100];
[self.consoleInput becomeFirstResponder];
}
@@ -1606,6 +1620,8 @@ enum GBWindowResizeAction
- (IBAction)showConsoleWindow:(id)sender
{
[self.consoleWindow orderBack:nil];
double secondUsage = GB_debugger_get_second_cpu_usage(&_gb);
_cpuCounter.stringValue = [NSString stringWithFormat:@"%.2f%%", secondUsage * 100];
}
- (void)queueDebuggerCommand:(NSString *)command
+37 -7
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -15,6 +15,8 @@
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
<outlet property="cpuCounter" destination="xdx-GC-Tb8" id="oSn-U3-qvg"/>
<outlet property="cpuView" destination="aBf-yU-8M0" id="RIb-Fj-lvt"/>
<outlet property="debugBar" destination="sah-kv-6KJ" id="24d-aM-rs5"/>
<outlet property="debuggerBackstepButton" destination="E87-Uq-f2l" id="PI7-Wu-f0v"/>
<outlet property="debuggerContinueButton" destination="ybQ-jy-NgI" id="fRF-S3-xSh"/>
@@ -283,18 +285,18 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
</box>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vts-CC-ZjQ">
<rect key="frame" x="0.0" y="0.0" width="329" height="338"/>
<rect key="frame" x="0.0" y="78" width="329" height="260"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
<rect key="frame" x="0.0" y="0.0" width="329" height="338"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="260"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
<rect key="frame" x="0.0" y="0.0" width="329" height="338"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="260"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.14901960780000001" green="0.14901960780000001" blue="0.14901960780000001" alpha="1" colorSpace="calibratedRGB"/>
<size key="minSize" width="329" height="338"/>
<size key="minSize" width="329" height="260"/>
<size key="maxSize" width="1160" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
@@ -309,10 +311,38 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4jm-Gm-D2R">
<rect key="frame" x="313" y="0.0" width="16" height="338"/>
<rect key="frame" x="313" y="0.0" width="16" height="260"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="wC1-WG-WRf">
<rect key="frame" x="0.0" y="75" width="329" height="5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
</box>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aBf-yU-8M0" customClass="GBCPUView">
<rect key="frame" x="0.0" y="0.0" width="329" height="77"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xdx-GC-Tb8">
<rect key="frame" x="225" y="56" width="100" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="100%" id="set-AM-fVX">
<font key="font" metaFont="smallSystemBold"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F9b-AT-CdX">
<rect key="frame" x="4" y="56" width="100" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="CPU Load" id="ai6-8E-Let">
<font key="font" metaFont="smallSystemBold"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</customView>
</subviews>
</customView>
</subviews>
+5
View File
@@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
@interface GBCPUView : NSView
- (void)addSample:(double)sample;
@end
+59
View File
@@ -0,0 +1,59 @@
#import "GBCPUView.h"
#define SAMPLE_COUNT 0x100 // ~4 seconds
@implementation GBCPUView
{
double _samples[SAMPLE_COUNT];
size_t _position;
}
- (void)drawRect:(NSRect)dirtyRect
{
NSSize size = self.bounds.size;
NSBezierPath *line = [NSBezierPath bezierPath];
{
double sample = _samples[_position % SAMPLE_COUNT];
[line moveToPoint:NSMakePoint(size.width * 0.5 / SAMPLE_COUNT,
sample * size.height)];
}
for (unsigned i = 1; i < SAMPLE_COUNT; i++) {
double sample = _samples[(i + _position) % SAMPLE_COUNT];
[line lineToPoint:NSMakePoint(size.width * (i + 0.5) / SAMPLE_COUNT,
sample * size.height)];
}
NSBezierPath *fill = [line copy];
[fill lineToPoint:NSMakePoint(size.width + 0.5, 0)];
[fill lineToPoint:NSMakePoint(0.5, 0)];
NSColor *strokeColor;
if (@available(macOS 10.10, *)) {
strokeColor = [NSColor systemGreenColor];
}
else {
strokeColor = [NSColor colorWithRed:3.0 / 16 green:0.5 blue:5.0 / 16 alpha:1.0];
}
NSColor *fillColor = [strokeColor colorWithAlphaComponent:1 / 3.0];
[fillColor setFill];
[fill fill];
[strokeColor setStroke];
[line stroke];
[super drawRect:dirtyRect];
}
- (void)addSample:(double)sample
{
_samples[_position++] = sample;
if (_position == SAMPLE_COUNT) {
_position = 0;
}
}
@end
+18
View File
@@ -1728,6 +1728,24 @@ static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
return true;
}
double GB_debugger_get_frame_cpu_usage(GB_gameboy_t *gb)
{
if (gb->last_frame_busy_cycles || gb->last_frame_idle_cycles) {
return (double)gb->last_frame_busy_cycles / (gb->last_frame_busy_cycles + gb->last_frame_idle_cycles);
}
return 0;
}
double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb)
{
if (gb->last_second_busy_cycles || gb->last_second_idle_cycles) {
return (double)gb->last_second_busy_cycles / (gb->last_second_busy_cycles + gb->last_second_idle_cycles);
}
return 0;
}
double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb);
static bool usage(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{
NO_MODIFIERS
+3
View File
@@ -25,6 +25,9 @@ void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled);
void GB_debugger_clear_symbols(GB_gameboy_t *gb);
void GB_debugger_set_reload_callback(GB_gameboy_t *gb, GB_debugger_reload_callback_t callback);
double GB_debugger_get_frame_cpu_usage(GB_gameboy_t *gb);
double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb);
#ifdef GB_INTERNAL
internal void GB_debugger_run(GB_gameboy_t *gb);
internal void GB_debugger_handle_async_commands(GB_gameboy_t *gb);