git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

commit 6ab1be654b4e56db1028b6f5faa1d67543623d2d
parent f706988171a83402a7e982115abe8359c3ae4cc3
Author: Lior Halphon <LIJI32@gmail.com>
Date:   Fri,  6 Jun 2025 23:10:57 +0300

Add CPU load graph to Cocoa, closes #654

Diffstat:
MCocoa/Document.h3+++
MCocoa/Document.m16++++++++++++++++
MCocoa/Document.xib44+++++++++++++++++++++++++++++++++++++-------
ACocoa/GBCPUView.h5+++++
ACocoa/GBCPUView.m59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MCore/debugger.c18++++++++++++++++++
MCore/debugger.h3+++
7 files changed, 141 insertions(+), 7 deletions(-)

diff --git a/Cocoa/Document.h b/Cocoa/Document.h @@ -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; diff --git a/Cocoa/Document.m b/Cocoa/Document.m @@ -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 diff --git a/Cocoa/Document.xib b/Cocoa/Document.xib @@ -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> diff --git a/Cocoa/GBCPUView.h b/Cocoa/GBCPUView.h @@ -0,0 +1,5 @@ +#import <Cocoa/Cocoa.h> + +@interface GBCPUView : NSView +- (void)addSample:(double)sample; +@end diff --git a/Cocoa/GBCPUView.m b/Cocoa/GBCPUView.m @@ -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 diff --git a/Core/debugger.c b/Core/debugger.c @@ -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 diff --git a/Core/debugger.h b/Core/debugger.h @@ -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);

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.