diff --git a/src/BreathOfTheWild/Mods/FPS++/patch_FenceMethod.asm b/src/BreathOfTheWild/Mods/FPS++/patch_FenceMethod.asm index 38d44b99..41a43100 100644 --- a/src/BreathOfTheWild/Mods/FPS++/patch_FenceMethod.asm +++ b/src/BreathOfTheWild/Mods/FPS++/patch_FenceMethod.asm @@ -3,35 +3,47 @@ moduleMatches = 0x6267BFD0 .origin = codecave -# variables -_fenceMethod: # stores fence method mode from preset -.int $fenceMethod +0x031FAB00 = fullFenceAddr: +0x031FAB04 = skipFenceAddr: -_conditionalPerformanceFence: -lis r11, _fenceMethod@ha -lwz r11, _fenceMethod@l(r11) ; Load the fence method value -cmpwi r11, 1 ; Compare the skip fence method with the fence method that's set, store comparison to the conditional register -bne .+0x0C ; If the conditional register isn't equel to the performance fence method, skip the next two instructions -li r0, 1 # Performance fence always set the fence skip value to 1 -blr ; Return to the instruction that jumped to this part to the code cave -lwz r0, 0x388(r31) ; Instruction that gets executed if performance fence isn't used, this is the original instruction. -blr ; Return to the instruction that jumped to this part to the code cave +conditionalFence: -# Accurate and Skip Fence methods +# Check which fence it's running +li r11, $fenceMethod +cmpwi r11, 1 +beq performanceFence +li r11, $fenceMethod +cmpwi r11, 2 +beq accurateFence +li r11, $fenceMethod +cmpwi r11, 3 +beq doFenceSkip -_conditionalAccurateAndSkipFence: -lis r5, _fenceMethod@ha -lwz r11, _fenceMethod@l(r5) ; Load the fence method value -cmpwi r11, 2 ; Compare the accurate fence method with the fence method that's set, store comparison to the conditional register -li r5, 6 ; Original instruction that got replaced with the jump -add r6, r12, r0 ; Instruction that got replaced with the fence skip check -bne .+0x10 ; If the conditional register isn't equel to accurate fence, skip the next three instructions (the ones that are the accurate fence) to the next comparison instruction. -cmpwi r6, 500 # Accurate fence basically checks if it's the first 500 frames of the game, in which case it does a full fence. -blt .+0x08 -subi r6, r6, 1 -cmpwi r11, 3 ; Compare the skip fence method with the fence method that's set, in preparation of the fence skip since it stores the result in the conditional register. -blr ; Return to the instruction that jumped to this part to the code cave +performanceFence: +li r0, 1 ; Use 1 as the fence value +subf r0, r10, r0 ; replicate previous instruction +add r6, r12, r0 ; original instruction +b doFence -0x31FAAE8 = bla _conditionalPerformanceFence ; Jumps to the conditional performance part of the code cave that creates the performance fence skip if that preset has been chosen. -0x31FAAF8 = bla _conditionalAccurateAndSkipFence ; Jumps to the conditional accurate part of the code cave that creates the accurate fence skip if that preset has been chosen. -0x31FAAFC = beq .+0x08 ; This part is the crucial part of the fence skip method. It skips the GX2SetGPUFence call in which case there's no fence skip, if the conditional register that has previously been set by the accurate code cave was true. \ No newline at end of file +accurateFence: +add r6, r12, r0 ; original instruction +cmpwi r6, 500 ; check if tick is the first 500 frames/ticks +blt doFence ; if so, do a full fence with the original value +b doFenceSkip ; after 500 frames, skip the fence + +allFenceSkip: +add r6, r12, r0 ; original instruction +b doFenceSkip + +doFence: +lis r11, fullFenceAddr@ha +addi r11, r11, fullFenceAddr@l +mtctr r11 +bctrl +doFenceSkip: +lis r11, skipFenceAddr@ha +addi r11, r11, skipFenceAddr@l +mtctr r11 +bctrl + +0x031FAAFC = b conditionalFence \ No newline at end of file diff --git a/src/BreathOfTheWild/Mods/FPS++/patch_GameSpeed.asm b/src/BreathOfTheWild/Mods/FPS++/patch_GameSpeed.asm index 5f902253..613bd135 100644 --- a/src/BreathOfTheWild/Mods/FPS++/patch_GameSpeed.asm +++ b/src/BreathOfTheWild/Mods/FPS++/patch_GameSpeed.asm @@ -36,7 +36,7 @@ floatConvL: # Variables fpsLimit: -.float $fpsLimit +.float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)) lowFPSLimit: .float $lowFPSLimit @@ -45,34 +45,34 @@ bufferSizeDivider: .float $frameAverageAmount averageFPS30: -.float $fpsLimit +.float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)) averageFPS30Inv: -.float 900/$fpsLimit +.float 900/((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)) averageFPS1.5: -.float (1.5*$fpsLimit)/30 +.float (1.5*((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)))/30 averageFPS1.5Inv: -.float 45/$fpsLimit +.float 45/((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)) averageFPS1: -.float $fpsLimit/30 +.float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))/30 averageFPS1Inv: -.float 30/$fpsLimit +.float 30/((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)) averageFPS0.5: -.float $fpsLimit/60 +.float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))/60 averageFPS0.5Inv: -.float 30/(2*$fpsLimit) +.float 30/(2*((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))) averageSum: -.float $fpsLimit*$frameAverageAmount +.float 30*$frameAverageAmount buffer: -.float 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; buffer can only store a max length of 32 frames +.float 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 ; buffer can only store a max length of 32 frames bufferIndex: .int 0 @@ -104,6 +104,8 @@ lfs f10, 0xD0(r30) ; Load the external speed offset fcmpu cr0, f10, f12 ; Compare the value stored in the external memory offset to 0 (f12) bne _setGamespeed +b setSwapInterval + ; Calculate speed of current frame (FPS). It's calculated by using the ticks between the previous frame and now, which is stored in r12, and the amount of ticks that the Wii U executes in a second (the bus speed). _convertTicksToFrametime: xoris r12, r12, 0x8000 ; Flip the sign bit of int ticks for floating point conversion @@ -264,8 +266,6 @@ blr ; Return to the address that's stored in the link register # Patches 0x1031E2C0 = .float 2 -0x031FACD0 = nop ; Disable vsync -0x031FACF4 = nop ; Disable vsync loop 0x031FA97C = bla _calculateGamespeed ; Replace an instruction that gets called every frame to calculate the FPS diff --git a/src/BreathOfTheWild/Mods/FPS++/patch_VSync.asm b/src/BreathOfTheWild/Mods/FPS++/patch_VSync.asm new file mode 100644 index 00000000..e7a73a06 --- /dev/null +++ b/src/BreathOfTheWild/Mods/FPS++/patch_VSync.asm @@ -0,0 +1,81 @@ +[BotW_VSync_V208] +moduleMatches = 0x6267BFD0 + +.origin = codecave + +setSwapInterval: +li r11, $keepVsync +cmpwi r11, 1 +bne setSwapIntervalTo0 + +lis r11, fpsLimit@ha +lfs f12, fpsLimit@l(r11) + +lis r11, const_30@ha +lfs f10, const_30@l(r11) +fcmpu cr0, f10, f12 +beq setSwapIntervalTo2 +# lis r11, const_60@ha +# lfs f10, const_60@l(r11) +# fcmpu cr0, f10, f12 +# beq setSwapIntervalTo1 +; Disable vsync for everything else +b setSwapIntervalTo0 + + +setSwapIntervalTo0: ; Disable vsync +mflr r11 +li r3, 0 +bl import.gx2.GX2SetSwapInterval +mtlr r11 +b _convertTicksToFrametime + +setSwapIntervalTo1: ; Double Buffered 30FPS +mflr r11 +li r3, 1 +bl import.gx2.GX2SetSwapInterval +mtlr r11 +b _convertTicksToFrametime + +setSwapIntervalTo2: ; Double Buffered 30FPS +mflr r11 +li r3, 2 +bl import.gx2.GX2SetSwapInterval +mtlr r11 +b _convertTicksToFrametime + + +waitForVsyncAddr: +.ptr 0x031FACD4 + +conditionalVsyncWait: +stwu r1, -0x20(r1) + +li r12, $keepVsync +cmpwi r12, 1 +beqlr + +lis r12, waitForVsyncAddr@ha +lwz r12, waitForVsyncAddr@l(r12) +mtlr r12 +blr + +0x031FACCC = bla conditionalVsyncWait + + +conditionalSwapStatus: +li r6, $keepVsync +cmpwi r6, 1 +beq continueSwapStatus +cmpw r12, r12 +blr +continueSwapStatus: +cmplw r12, r0 +blr + +0x031FACF0 = bla conditionalSwapStatus + + +# Disable vsync entirely if not running at 30FPS +# 0x031FACD0 = .uint (($keepVsync == 1) * 0x4914DAB9) + (($keepVsync == 0) * 0x60000000) +# 0x031FACF4 = .uint (($keepVsync == 1) * 0x4180FFDC) + (($keepVsync == 0) * 0x60000000) \ No newline at end of file diff --git a/src/BreathOfTheWild/Mods/FPS++/rules.txt b/src/BreathOfTheWild/Mods/FPS++/rules.txt index cad57b0d..1b6de60d 100644 --- a/src/BreathOfTheWild/Mods/FPS++/rules.txt +++ b/src/BreathOfTheWild/Mods/FPS++/rules.txt @@ -12,7 +12,7 @@ $staticFPSMode:int = 0 $fpsLimitNormal = 60 $fpsLimitAdvanced = 60 -$fpsLimit = 0 +$keepVsync:int = 0 $frameAverageAmount = 8 $fenceMethod = 1 @@ -46,42 +46,49 @@ name = 240FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 240 +$keepVsync:int = 0 [Preset] name = 165FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 165 +$keepVsync:int = 0 [Preset] name = 144FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 144 +$keepVsync:int = 0 [Preset] name = 120FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 120 +$keepVsync:int = 0 [Preset] name = 90FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 90 +$keepVsync:int = 0 [Preset] name = 75FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 75 +$keepVsync:int = 0 [Preset] name = 72FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 72 +$keepVsync:int = 0 [Preset] name = 60FPS Limit (Default) @@ -89,30 +96,35 @@ category = FPS Limit condition = $advancedMode == 0 default = 1 $fpsLimitNormal = 60 +$keepVsync:int = 0 [Preset] name = 55FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 55 +$keepVsync:int = 0 [Preset] name = 45FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 45 +$keepVsync:int = 0 [Preset] name = 30FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 30 +$keepVsync:int = 1 [Preset] name = 20FPS Limit category = FPS Limit condition = $advancedMode == 0 $fpsLimitNormal = 20 +$keepVsync:int = 0 # Advanced Settings @@ -124,48 +136,56 @@ name = No FPS Limit (for benchmarking) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 500 +$keepVsync:int = 0 [Preset] name = 244FPS (ideal for 244Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 244 +$keepVsync:int = 0 [Preset] name = 240FPS (ideal for 240Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 240 +$keepVsync:int = 0 [Preset] name = 165FPS (ideal for 165Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 165 +$keepVsync:int = 0 [Preset] name = 144FPS (ideal for 144Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 144 +$keepVsync:int = 0 [Preset] name = 120FPS (ideal for 240/120/60Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 120 +$keepVsync:int = 0 [Preset] name = 75FPS (ideal for 75Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 75 +$keepVsync:int = 0 [Preset] name = 72FPS (ideal for 144Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 72 +$keepVsync:int = 0 [Preset] name = 60FPS (ideal for 240/120/60Hz displays) @@ -173,42 +193,77 @@ category = Framerate Limit condition = $advancedMode == 1 default = 1 $fpsLimitAdvanced = 60 +$keepVsync:int = 0 [Preset] name = 55FPS (ideal for 165Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 55 +$keepVsync:int = 0 [Preset] name = 48FPS (ideal for 144Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 48 +$keepVsync:int = 0 [Preset] name = 40FPS (ideal for 240/120/60Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 40 +$keepVsync:int = 0 [Preset] name = 33FPS (ideal for 165Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 33 +$keepVsync:int = 0 [Preset] name = 30FPS (ideal for 240/120/60Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 30 +$keepVsync:int = 1 [Preset] name = 20FPS (ideal for 240/120/60Hz displays) category = Framerate Limit condition = $advancedMode == 1 $fpsLimitAdvanced = 20 +$keepVsync:int = 0 + +# Double-Buffered VSync + +# [Preset] +# name = Enabled (Recommended, Uses Cemu's Vsync) +# category = Override Double-Buffered Vsync +# condition = (($advancedMode == 1) + ($fpsLimitAdvanced != 30)) == 2 +# default = 1 +# $keepVsync:int = 0 +# +# [Preset] +# name = Disabled (Not Recommended) +# category = Override Double-Buffered Vsync +# condition = (($advancedMode == 1) + ($fpsLimitAdvanced != 30)) == 2 +# $keepVsync:int = 1 +# +# [Preset] +# name = Enabled (Default) +# category = Override Double-Buffered Vsync +# condition = (($advancedMode == 1) + ($fpsLimitAdvanced == 30)) == 2 +# $keepVsync:int = 0 +# +# [Preset] +# name = Disabled (Can Improve Frame Pacing At 30FPS) +# category = Override Double-Buffered Vsync +# condition = (($advancedMode == 1) + ($fpsLimitAdvanced == 30)) == 2 +# default = 1 +# $keepVsync:int = 1 # Cutscene FPS Limit Mode @@ -378,4 +433,4 @@ $debugMultiplier = -100 [Control] -vsyncFrequency = ($advancedMode * $fpsLimitAdvanced) + ((($advancedMode+1) % 2) * $fpsLimitNormal) \ No newline at end of file +vsyncFrequency = (($keepVsync == 1) * 60) + (($keepVsync == 0) * ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))) \ No newline at end of file