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