diff --git a/Makefile b/Makefile index 9956d621..fc6458c4 100644 --- a/Makefile +++ b/Makefile @@ -5,14 +5,43 @@ include util.mk # Default target default: all +# Preprocessor definitions +DEFINES := + # Use Libdragon IPL3 # WARNING: This CAN and WILL break certain (most) emulators. # Use if you care about console or ares boot times. -LIBDRAGON_IPL3 := 0 +LIBDRAGON_IPL3 ?= 0 -# Preprocessor definitions -DEFINES := +# Build types +# debug - Debug build +# general - General release build +# final - Final release build with no crash screen, game will reset upon exception. + +RELEASE ?= debug + +# Benchmark build +# Builds a benchmark build that runs a 120 star TAS for automated verification. +# This setting is also in include/cfg/benchmark.h, but provided here as well for automation purposes. + +BENCHMARK ?= 0 + +ifeq ($(BENCHMARK), 1) + DEFINES += CFG_BENCHMARK=1 + RELEASE = general +endif + +ifeq ($(RELEASE), debug) + OPT_FLAGS := -Og -ggdb3 + DEFINES += _DEBUG=1 +else ifeq ($(RELEASE), general) + OPT_FLAGS := -Os -ggdb3 +else ifeq ($(RELEASE), final) + OPT_FLAGS := -Os +else + $(error Invalid build release setting.) +endif ifeq ($(LIBDRAGON_IPL3), 1) DEFINES += LIBDRAGON_IPL3=1 @@ -87,17 +116,9 @@ else ifeq ($(GRUCODE),f3dzex) # Fast3DZEX (2.0J / Animal Forest - Dōbutsu no Mo DEFINES += F3DZEX_GBI_2=1 F3DEX_GBI_2=1 F3DEX_GBI_SHARED=1 endif -NON_MATCHING := 1 MIPSISET := -mips3 -OPT_FLAGS := -Os - - -# NON_MATCHING - whether to build a matching, identical copy of the ROM -# 1 - enable some alternate, more portable code that does not produce a matching ROM -# 0 - build a matching ROM -NON_MATCHING ?= 0 -$(eval $(call validate-option,NON_MATCHING,0 1)) +NON_MATCHING := 1 ifeq ($(TARGET_N64),0) NON_MATCHING := 1 endif @@ -140,6 +161,7 @@ ifeq ($(filter clean distclean,$(MAKECMDGOALS)),) else $(info IPL: Nintendo IPL3) endif + $(info Build type: $(RELEASE)) $(info =======================) endif @@ -306,7 +328,7 @@ endif # C compiler options CFLAGS = -G 0 $(TARGET_CFLAGS) $(DEF_INC_CFLAGS) $(foreach i,$(INCLUDE_DIRS),--embed-dir=$(i)) -CFLAGS += -std=gnu23 -fno-inline-functions -Wno-unused-variable -mno-shared -march=vr4300 -mfix4300 -mabi=32 -mhard-float -mdivide-breaks -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -fno-PIC -mno-abicalls -fno-strict-aliasing -ffreestanding -fwrapv -Wall -Wextra +CFLAGS += -std=gnu23 -fno-inline -Wno-unused-variable -mno-shared -march=vr4300 -mfix4300 -mabi=32 -mhard-float -mdivide-breaks -fno-unsafe-math-optimizations -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -fno-PIC -mno-abicalls -fno-strict-aliasing -ffreestanding -fwrapv -Wall -Wextra CFLAGS += -Wno-missing-braces -Wno-maybe-uninitialized ASFLAGS := -march=vr4300 -mabi=32 $(foreach i,$(INCLUDE_DIRS),-I$(i)) $(foreach d,$(DEFINES),--defsym $(d)) diff --git a/data/benchmark_replay.m64 b/data/benchmark_replay.m64 new file mode 100644 index 00000000..1962ca3a Binary files /dev/null and b/data/benchmark_replay.m64 differ diff --git a/include/cfg/benchmark.h b/include/cfg/benchmark.h new file mode 100644 index 00000000..70260e86 --- /dev/null +++ b/include/cfg/benchmark.h @@ -0,0 +1,8 @@ +#pragma once + +/* + * Enables a basic performance benchmark over the vanilla game by replaying the 2012 + * 120 star TAS. + */ + +// #define CFG_BENCHMARK diff --git a/lib/hackerlibultra/Makefile b/lib/hackerlibultra/Makefile index 991093a3..d6d6857f 100644 --- a/lib/hackerlibultra/Makefile +++ b/lib/hackerlibultra/Makefile @@ -41,7 +41,7 @@ CC := $(CROSS)gcc WARNINGS := -Wall -Wextra -Wno-format-security -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable -Wno-builtin-declaration-mismatch WARNINGS += -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-implicit-function-declaration # TODO: Try adjusting code to remove these -CFLAGS := -std=gnu23 -G 0 -c -nostdinc -march=vr4300 -mfix4300 -mabi=32 -mno-abicalls -mdivide-breaks -fno-PIC -fno-common -ffreestanding -fbuiltin -fno-builtin-sinf -fno-builtin-cosf -funsigned-char $(WARNINGS) +CFLAGS := -std=gnu23 -G 0 -c -fno-inline -nostdinc -march=vr4300 -mfix4300 -mabi=32 -mno-abicalls -mdivide-breaks -fno-PIC -fno-common -ffreestanding -fbuiltin -fno-builtin-sinf -fno-builtin-cosf -funsigned-char $(WARNINGS) CFLAGS += -fno-strict-aliasing # TODO: Try adjusting code to remove this ASFLAGS := -w -nostdinc -c -G 0 -march=vr4300 -mabi=32 -mgp32 -mfp32 -DMIPSEB -D_LANGUAGE_ASSEMBLY -D_MIPS_SIM=1 -D_ULTRA64 CPPFLAGS = -DMODERN_CC -D_MIPS_SZLONG=32 -D__USE_ISOC99 $(GBIDEFINE) $(VERSION_DEFINE) $(DEBUGFLAG) diff --git a/src/game/benchmark.c b/src/game/benchmark.c new file mode 100644 index 00000000..092108df --- /dev/null +++ b/src/game/benchmark.c @@ -0,0 +1,48 @@ +#include + +#include "cfg/benchmark.h" + +#include "game_init.h" +#include "n64-stdio.h" + +#ifdef CFG_BENCHMARK + +#define REPLAY_FILE "data/benchmark_replay.m64" +#define REPLAY_HEADER_SIZE 0x400 +#define REPLAY_FRAME_SIZE sizeof(ReplayContPad) + +typedef struct { + u16 button; + s8 stickX; + s8 stickY; +} ReplayContPad; + +static alignas(32) u8 replayData[] = { + #embed REPLAY_FILE +}; + +static u8 *replayDataPtr = &replayData[0] + REPLAY_HEADER_SIZE; +static u8 *replayDataEnd = &replayData[0] + (sizeof(replayData)); +static u32 benchmarkFrame = 0; + +void replay_contpad(OSContPad *pad) { + ReplayContPad *input = (ReplayContPad *) replayDataPtr; + + if (replayDataPtr >= replayDataEnd) { + return; + } + + pad->button = input->button; + pad->stick_x = input->stickX; + pad->stick_y = input->stickY; + + replayDataPtr += REPLAY_FRAME_SIZE; +} + +void replay_print_stats() { + OSTime finalTime = osGetTime(); + n64_printf("BENCHMARK END\n"); + n64_printf("Time to complete: %llu us\n", OS_CYCLES_TO_USEC(finalTime)); + n64_printf("Average FPS: %.2f", (f32)gGlobalTimer * 1000000.0f / OS_CYCLES_TO_USEC(finalTime)); +} +#endif diff --git a/src/game/benchmark.h b/src/game/benchmark.h new file mode 100644 index 00000000..e1991445 --- /dev/null +++ b/src/game/benchmark.h @@ -0,0 +1,6 @@ +#pragma once + +#define REPLAY_CONTPAD_BITS 0xF0 + +void replay_contpad(OSContPad *pad); +void replay_print_stats(); diff --git a/src/game/game_init.c b/src/game/game_init.c index 5e02d8b7..aee1a59a 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -1,5 +1,7 @@ #include +#include "cfg/benchmark.h" + #include "sm64.h" #include "gfx_dimensions.h" #include "audio/external.h" @@ -20,6 +22,8 @@ #include "segment_symbols.h" #include "rumble_init.h" +#include "benchmark.h" + // First 3 controller slots struct Controller gControllers[3]; @@ -525,15 +529,23 @@ void run_demo_inputs(void) { */ void read_controller_inputs(void) { s32 i; - +#ifndef CFG_BENCHMARK // If any controllers are plugged in, update the controller information. if (gControllerBits) { osRecvMesg(&gSIEventMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK); osContGetReadData(&gControllerPads[0]); -#if ENABLE_RUMBLE + #if ENABLE_RUMBLE release_rumble_pak_control(); -#endif + #endif } +#else + osRecvMesg(&gSIEventMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK); + replay_contpad(&gControllerPads[0]); + #if ENABLE_RUMBLE + release_rumble_pak_control(); + #endif +#endif + run_demo_inputs(); for (i = 0; i < 2; i++) { @@ -579,10 +591,15 @@ void init_controllers(void) { // Set controller 1 to point to the set of status/pads for input 1 and // init the controllers. +#ifndef CFG_BENCHMARK gControllers[0].statusData = &gControllerStatuses[0]; gControllers[0].controllerData = &gControllerPads[0]; osContInit(&gSIEventMesgQueue, &gControllerBits, &gControllerStatuses[0]); - +#else + gControllers[0].statusData = &gControllerStatuses[0]; + gControllers[0].controllerData = &gControllerPads[0]; + gControllerBits = REPLAY_CONTPAD_BITS; +#endif // Strangely enough, the EEPROM probe for save data is done in this function. // Save Pak detection? gEepromProbe = osEepromProbe(&gSIEventMesgQueue); diff --git a/src/game/geo_misc.c b/src/game/geo_misc.c index e0f58d22..3ed7ede9 100644 --- a/src/game/geo_misc.c +++ b/src/game/geo_misc.c @@ -1,5 +1,7 @@ #include +#include "cfg/benchmark.h" + #include "sm64.h" #include "geo_misc.h" @@ -14,6 +16,7 @@ #include "init/memory.h" #include "object_list_processor.h" #include "rendering_graph_node.h" +#include "benchmark.h" #include "save_file.h" #include "segment2.h" @@ -199,6 +202,12 @@ Gfx *geo_exec_cake_end_screen(s32 callContext, struct GraphNode *node, UNUSED f3 Gfx *displayList = NULL; Gfx *displayListHead = NULL; +#ifdef CFG_BENCHMARK + if (callContext == GEO_CONTEXT_AREA_LOAD) { + replay_print_stats(); + } +#endif + if (callContext == GEO_CONTEXT_RENDER) { displayList = alloc_display_list(3 * sizeof(*displayList)); displayListHead = displayList;