Perf: De-threadify and make compatible with 3DS

This commit is contained in:
Jeffrey Pfau 2016-02-25 23:54:14 -08:00
parent 9919fffcb7
commit 9f6837da42
4 changed files with 96 additions and 77 deletions

View File

@ -45,7 +45,7 @@ file(GLOB GBA_RENDERER_SRC ${CMAKE_SOURCE_DIR}/src/gba/renderers/*.c)
file(GLOB SIO_SRC ${CMAKE_SOURCE_DIR}/src/gba/sio/lockstep.c)
file(GLOB GB_RENDERER_SRC ${CMAKE_SOURCE_DIR}/src/gb/renderers/*.c)
file(GLOB THIRD_PARTY_SRC ${CMAKE_SOURCE_DIR}/src/third-party/inih/*.c)
list(APPEND GBA_SV_SRC ${CMAKE_SOURCE_DIR}/src/platform/commandline.c)
set(CLI_SRC ${CMAKE_SOURCE_DIR}/src/platform/commandline.c)
set(CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-mem.c)
set(VFS_SRC)
source_group("ARM core" FILES ${ARM_SRC})
@ -529,7 +529,8 @@ if(NOT MINIMAL_CORE)
${SIO_SRC})
endif()
list(APPEND SRC
${FEATURE_SRC})
${FEATURE_SRC}
${CLI_SRC})
endif()
if(NOT SKIP_LIBRARY)
@ -622,13 +623,13 @@ if(BUILD_QT)
endif()
if(BUILD_PERF)
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c)
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c ${CLI_SRC})
if(UNIX AND NOT APPLE)
list(APPEND PERF_LIB rt)
endif()
add_executable(${BINARY_NAME}-perf ${PERF_SRC})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB} ${OS_LIB})
set_target_properties(${BINARY_NAME}-perf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
install(TARGETS ${BINARY_NAME}-perf DESTINATION bin COMPONENT ${BINARY_NAME}-perf)
install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf)

View File

@ -14,8 +14,9 @@ set(OS_DEFINES COLOR_16_BIT COLOR_5_6_5 IOAPI_NO_64)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
list(APPEND OS_LIB ctru)
file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/3ds-*.c)
file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/3ds-*.c ${CMAKE_CURRENT_SOURCE_DIR}/ctru-heap.c)
set(OS_SRC ${OS_SRC} PARENT_SCOPE)
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
source_group("3DS-specific code" FILES ${OS_SRC})
if(USE_VFS_3DS)
@ -45,7 +46,7 @@ set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/uishader.h
${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
PROPERTIES GENERATED ON)
add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c ctru-heap.c)
add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c)
set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${M_LIBRARY} ${OS_LIB})
@ -93,6 +94,12 @@ add_custom_target(${BINARY_NAME}.cia ALL
add_custom_target(run ${3DSLINK} ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.3dsx
DEPENDS ${BINARY_NAME}.3dsx)
if(BUILD_PERF)
add_custom_target(${BINARY_NAME}-perf.3dsx ALL
${3DSXTOOL} ../${BINARY_NAME}-perf ${BINARY_NAME}-perf.3dsx --smdh=${BINARY_NAME}.smdh
DEPENDS ${BINARY_NAME}-perf ${BINARY_NAME}.smdh)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cia.rsf.in ${CMAKE_CURRENT_BINARY_DIR}/cia.rsf)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.3dsx

View File

@ -25,7 +25,7 @@ set(CMAKE_PROGRAM_PATH ${DEVKITARM}/bin)
set(cross_prefix ${CMAKE_PROGRAM_PATH}/arm-none-eabi-)
set(arch_flags "-march=armv6k -mtune=mpcore -mfpu=vfp -mfloat-abi=hard")
set(inc_flags "-I${CTRULIB}/include ${arch_flags} -mword-relocations")
set(link_flags "-L${CTRULIB}/lib -lctru -specs=3dsx.specs ${arch_flags}")
set(link_flags "-L${CTRULIB}/lib -specs=3dsx.specs ${arch_flags}")
set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name")
set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor")

View File

@ -4,7 +4,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "core/config.h"
#include "core/thread.h"
#include "gba/core.h"
#include "gba/gba.h"
#include "gba/renderers/video-software.h"
@ -14,6 +13,10 @@
#include "util/string.h"
#include "util/vfs.h"
#ifdef _3DS
#include <3ds.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
@ -37,17 +40,40 @@ struct PerfOpts {
char* savestate;
};
static void _GBAPerfRunloop(struct mCoreThread* context, int* frames, bool quiet);
static void _GBAPerfShutdown(int signal);
static bool _parsePerfOpts(struct mSubParser* parser, int option, const char* arg);
static void _loadSavestate(struct mCoreThread* context);
#ifdef _3DS
extern bool allocateRomBuffer(void);
FS_Archive sdmcArchive;
#endif
static void _mPerfRunloop(struct mCore* context, int* frames, bool quiet);
static void _mPerfShutdown(int signal);
static bool _parsePerfOpts(struct mSubParser* parser, int option, const char* arg);
static void _log(struct mLogger*, int, enum mLogLevel, const char*, va_list);
static struct mCoreThread* _thread;
static bool _dispatchExiting = false;
static struct VFile* _savestate = 0;
int main(int argc, char** argv) {
signal(SIGINT, _GBAPerfShutdown);
#ifdef _3DS
gfxInitDefault();
osSetSpeedupEnable(true);
consoleInit(GFX_BOTTOM, NULL);
if (!allocateRomBuffer()) {
return 1;
}
sdmcArchive = (FS_Archive) {
ARCHIVE_SDMC,
(FS_Path) { PATH_EMPTY, 1, "" },
0
};
FSUSER_OpenArchive(&sdmcArchive);
#else
signal(SIGINT, _mPerfShutdown);
#endif
int didFail = 0;
struct mLogger logger = { .log = _log };
mLogSetDefaultLogger(&logger);
struct PerfOpts perfOpts = { false, false, 0, 0, 0 };
struct mSubParser subparser = {
@ -57,30 +83,26 @@ int main(int argc, char** argv) {
.opts = &perfOpts
};
struct mArguments args;
struct mArguments args = {};
bool parsed = parseArguments(&args, argc, argv, &subparser);
if (!parsed || args.showHelp) {
usage(argv[0], PERF_USAGE);
freeArguments(&args);
return !parsed;
didFail = !parsed;
goto cleanup;
}
if (args.showVersion) {
version(argv[0]);
freeArguments(&args);
return 0;
goto cleanup;
}
void* outputBuffer = malloc(256 * 256 * 4);
struct mCore* core = mCoreFind(args.fname);
if (!core) {
freeArguments(&args);
return 1;
didFail = 1;
goto cleanup;
}
struct mCoreThread context = {
.core = core
};
_thread = &context;
if (!perfOpts.noVideo) {
core->setVideoBuffer(core, outputBuffer, 256);
@ -89,9 +111,6 @@ int main(int argc, char** argv) {
_savestate = VFileOpen(perfOpts.savestate, O_RDONLY);
free(perfOpts.savestate);
}
if (_savestate) {
context.startCallback = _loadSavestate;
}
// TODO: Put back debugger
char gameCode[5] = { 0 };
@ -110,19 +129,14 @@ int main(int argc, char** argv) {
mCoreConfigLoadDefaults(&core->config, &opts);
mCoreLoadConfig(core);
int didStart = mCoreThreadStart(&context);
if (!didStart) {
goto cleanup;
}
mCoreThreadInterrupt(&context);
if (mCoreThreadHasCrashed(&context)) {
mCoreThreadJoin(&context);
goto cleanup;
core->reset(core);
if (_savestate) {
core->loadState(core, _savestate, 0);
_savestate->close(_savestate);
_savestate = NULL;
}
GBAGetGameCode(core->board, gameCode);
mCoreThreadContinue(&context);
core->getGameCode(core, gameCode);
int frames = perfOpts.frames;
if (!frames) {
@ -131,12 +145,12 @@ int main(int argc, char** argv) {
struct timeval tv;
gettimeofday(&tv, 0);
uint64_t start = 1000000LL * tv.tv_sec + tv.tv_usec;
_GBAPerfRunloop(&context, &frames, perfOpts.csv);
_mPerfRunloop(core, &frames, perfOpts.csv);
gettimeofday(&tv, 0);
uint64_t end = 1000000LL * tv.tv_sec + tv.tv_usec;
uint64_t duration = end - start;
mCoreThreadJoin(&context);
core->deinit(core);
float scaledFrames = frames * 1000000.f;
if (perfOpts.csv) {
@ -152,49 +166,46 @@ int main(int argc, char** argv) {
printf("%u frames in %" PRIu64 " microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
}
cleanup:
if (_savestate) {
_savestate->close(_savestate);
}
mCoreConfigFreeOpts(&opts);
freeArguments(&args);
mCoreConfigDeinit(&core->config);
free(outputBuffer);
return !didStart || mCoreThreadHasCrashed(&context);
cleanup:
freeArguments(&args);
#ifdef _3DS
gfxExit();
#endif
return didFail;
}
static void _GBAPerfRunloop(struct mCoreThread* context, int* frames, bool quiet) {
static void _mPerfRunloop(struct mCore* core, int* frames, bool quiet) {
struct timeval lastEcho;
gettimeofday(&lastEcho, 0);
int duration = *frames;
*frames = 0;
int lastFrames = 0;
while (context->state < THREAD_EXITING) {
if (mCoreSyncWaitFrameStart(&context->sync)) {
++*frames;
++lastFrames;
if (!quiet) {
struct timeval currentTime;
long timeDiff;
gettimeofday(&currentTime, 0);
timeDiff = currentTime.tv_sec - lastEcho.tv_sec;
timeDiff *= 1000;
timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000;
if (timeDiff >= 1000) {
printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f)));
fflush(stdout);
lastEcho = currentTime;
lastFrames = 0;
}
while (!_dispatchExiting) {
core->runFrame(core);
++*frames;
++lastFrames;
if (!quiet) {
struct timeval currentTime;
long timeDiff;
gettimeofday(&currentTime, 0);
timeDiff = currentTime.tv_sec - lastEcho.tv_sec;
timeDiff *= 1000;
timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000;
if (timeDiff >= 1000) {
printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f)));
fflush(stdout);
lastEcho = currentTime;
lastFrames = 0;
}
}
mCoreSyncWaitFrameEnd(&context->sync);
if (duration > 0 && *frames == duration) {
_GBAPerfShutdown(0);
}
if (_dispatchExiting) {
mCoreThreadEnd(context);
break;
}
}
if (!quiet) {
@ -202,11 +213,9 @@ static void _GBAPerfRunloop(struct mCoreThread* context, int* frames, bool quiet
}
}
static void _GBAPerfShutdown(int signal) {
static void _mPerfShutdown(int signal) {
UNUSED(signal);
// This will come in ON the GBA thread, so we have to handle it carefully
_dispatchExiting = true;
ConditionWake(&_thread->sync.videoFrameAvailableCond);
}
static bool _parsePerfOpts(struct mSubParser* parser, int option, const char* arg) {
@ -233,8 +242,10 @@ static bool _parsePerfOpts(struct mSubParser* parser, int option, const char* ar
}
}
static void _loadSavestate(struct mCoreThread* context) {
context->core->loadState(context->core, _savestate, 0);
_savestate->close(_savestate);
_savestate = 0;
static void _log(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) {
UNUSED(log);
UNUSED(category);
UNUSED(level);
UNUSED(format);
UNUSED(args);
}