src/platform/perf-main.c (view raw)
1#include "gba-thread.h"
2#include "gba.h"
3#include "renderers/video-software.h"
4
5#include <errno.h>
6#include <fcntl.h>
7#include <signal.h>
8#include <sys/time.h>
9
10#define PERF_OPTIONS "S:"
11#define PERF_USAGE \
12 "\nBenchmark options:\n" \
13 " -S SEC Run for SEC in-game seconds before exiting"
14
15struct PerfOpts {
16 int duration;
17};
18
19static void _GBAPerfRunloop(struct GBAThread* context, int* frames);
20static void _GBAPerfShutdown(int signal);
21static int _parsePerfOpts(struct SubParser* parser, int option, const char* arg);
22
23static struct GBAThread* _thread;
24
25int main(int argc, char** argv) {
26 signal(SIGINT, _GBAPerfShutdown);
27
28 struct GBAVideoSoftwareRenderer renderer;
29 GBAVideoSoftwareRendererCreate(&renderer);
30
31 struct PerfOpts perfOpts = { 0 };
32 struct SubParser subparser = {
33 .usage = PERF_USAGE,
34 .parse = _parsePerfOpts,
35 .extraOptions = PERF_OPTIONS,
36 .opts = &perfOpts
37 };
38
39 struct StartupOptions opts;
40 if (!parseCommandArgs(&opts, argc, argv, &subparser)) {
41 usage(argv[0], PERF_USAGE);
42 return 1;
43 }
44
45 renderer.outputBuffer = malloc(256 * 256 * 4);
46 renderer.outputBufferStride = 256;
47
48 struct GBAThread context = {
49 .renderer = &renderer.d,
50 .sync.videoFrameWait = 0,
51 .sync.audioWait = 0
52 };
53 _thread = &context;
54
55 context.debugger = createDebugger(&opts);
56
57 GBAMapOptionsToContext(&opts, &context);
58
59 GBAThreadStart(&context);
60
61 int frames = perfOpts.duration;
62 time_t start = time(0);
63 _GBAPerfRunloop(&context, &frames);
64 time_t end = time(0);
65 int duration = end - start;
66
67 GBAThreadJoin(&context);
68 close(opts.fd);
69 if (opts.biosFd >= 0) {
70 close(opts.biosFd);
71 }
72 free(context.debugger);
73
74 free(renderer.outputBuffer);
75
76 printf("%u frames in %i seconds: %g fps (%gx)\n", frames, duration, frames / (float) duration, frames / (duration * 60.f));
77
78 return 0;
79}
80
81static void _GBAPerfRunloop(struct GBAThread* context, int* frames) {
82 struct timeval lastEcho;
83 gettimeofday(&lastEcho, 0);
84 int duration = *frames;
85 *frames = 0;
86 int lastFrames = 0;
87 while (context->state < THREAD_EXITING) {
88 if (GBASyncWaitFrameStart(&context->sync, 0)) {
89 ++*frames;
90 ++lastFrames;
91 struct timeval currentTime;
92 long timeDiff;
93 gettimeofday(¤tTime, 0);
94 timeDiff = currentTime.tv_sec - lastEcho.tv_sec;
95 timeDiff *= 1000;
96 timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000;
97 if (timeDiff >= 1000) {
98 printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f)));
99 fflush(stdout);
100 lastEcho = currentTime;
101 lastFrames = 0;
102 }
103 }
104 GBASyncWaitFrameEnd(&context->sync);
105 if (*frames == duration * 60) {
106 _GBAPerfShutdown(0);
107 }
108 }
109 printf("\033[2K\r");
110}
111
112static void _GBAPerfShutdown(int signal) {
113 (void) (signal);
114 pthread_mutex_lock(&_thread->stateMutex);
115 _thread->state = THREAD_EXITING;
116 pthread_mutex_unlock(&_thread->stateMutex);
117}
118
119static int _parsePerfOpts(struct SubParser* parser, int option, const char* arg) {
120 struct PerfOpts* opts = parser->opts;
121 switch (option) {
122 case 'S':
123 opts->duration = strtol(arg, 0, 10);
124 return !errno;
125 default:
126 return 0;
127 }
128}