diff --git a/Cocoa/Document.h b/Cocoa/Document.h index 6f19877..30966ce 100644 --- 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 index ae0e013..ad723ad 100644 --- 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 index 8192430..49b14d6 100644 --- a/Cocoa/Document.xib +++ b/Cocoa/Document.xib @@ -1,8 +1,8 @@ - + - + @@ -15,6 +15,8 @@ + + @@ -283,18 +285,18 @@ - + - + - + - + @@ -309,10 +311,38 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cocoa/GBCPUView.h b/Cocoa/GBCPUView.h new file mode 100644 index 0000000..97e916d --- /dev/null +++ b/Cocoa/GBCPUView.h @@ -0,0 +1,5 @@ +#import + +@interface GBCPUView : NSView +- (void)addSample:(double)sample; +@end diff --git a/Cocoa/GBCPUView.m b/Cocoa/GBCPUView.m new file mode 100644 index 0000000..f927fc9 --- /dev/null +++ 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 index af1649c..3f810d5 100644 --- 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 index 0b853f4..16ab423 100644 --- 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);