all repos — mgba @ 3ee3f7625f5f60248ff2a05853bf93e8a00d9171

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