all repos — mgba @ a701a6d9dd272be961149477170384c7c591eb2d

mGBA Game Boy Advance Emulator

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(&currentTime, 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}