[BotW] Experimental 30FPS VSync option in FPS++

* It might improve frame pacing at those framerates, so it should be pretty helpful for a certain handheld device.
* Might make it an option to use BotW's double buffering vsync at other options, but I've disabled it just for now.
* Also rewrote parts of how the fence methods are done since it was a mess previously.
* Also found some pretty concerning mistakes, surprised everything even worked.
This commit is contained in:
Crementif 2022-02-22 03:11:49 +01:00
parent 623703fa69
commit ea6457b416
No known key found for this signature in database
GPG Key ID: 12BB8BD3EA30651E
4 changed files with 191 additions and 43 deletions

View File

@ -3,35 +3,47 @@ moduleMatches = 0x6267BFD0
.origin = codecave .origin = codecave
# variables 0x031FAB00 = fullFenceAddr:
_fenceMethod: # stores fence method mode from preset 0x031FAB04 = skipFenceAddr:
.int $fenceMethod
_conditionalPerformanceFence: conditionalFence:
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
# 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: performanceFence:
lis r5, _fenceMethod@ha li r0, 1 ; Use 1 as the fence value
lwz r11, _fenceMethod@l(r5) ; Load the fence method value subf r0, r10, r0 ; replicate previous instruction
cmpwi r11, 2 ; Compare the accurate fence method with the fence method that's set, store comparison to the conditional register add r6, r12, r0 ; original instruction
li r5, 6 ; Original instruction that got replaced with the jump b doFence
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
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. accurateFence:
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. add r6, r12, r0 ; original instruction
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. 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

View File

@ -36,7 +36,7 @@ floatConvL:
# Variables # Variables
fpsLimit: fpsLimit:
.float $fpsLimit .float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))
lowFPSLimit: lowFPSLimit:
.float $lowFPSLimit .float $lowFPSLimit
@ -45,34 +45,34 @@ bufferSizeDivider:
.float $frameAverageAmount .float $frameAverageAmount
averageFPS30: averageFPS30:
.float $fpsLimit .float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))
averageFPS30Inv: averageFPS30Inv:
.float 900/$fpsLimit .float 900/((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))
averageFPS1.5: averageFPS1.5:
.float (1.5*$fpsLimit)/30 .float (1.5*((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)))/30
averageFPS1.5Inv: averageFPS1.5Inv:
.float 45/$fpsLimit .float 45/((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))
averageFPS1: averageFPS1:
.float $fpsLimit/30 .float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))/30
averageFPS1Inv: averageFPS1Inv:
.float 30/$fpsLimit .float 30/((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))
averageFPS0.5: averageFPS0.5:
.float $fpsLimit/60 .float ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal))/60
averageFPS0.5Inv: averageFPS0.5Inv:
.float 30/(2*$fpsLimit) .float 30/(2*((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)))
averageSum: averageSum:
.float $fpsLimit*$frameAverageAmount .float 30*$frameAverageAmount
buffer: 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: bufferIndex:
.int 0 .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) fcmpu cr0, f10, f12 ; Compare the value stored in the external memory offset to 0 (f12)
bne _setGamespeed 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). ; 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: _convertTicksToFrametime:
xoris r12, r12, 0x8000 ; Flip the sign bit of int ticks for floating point conversion 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 # Patches
0x1031E2C0 = .float 2 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 0x031FA97C = bla _calculateGamespeed ; Replace an instruction that gets called every frame to calculate the FPS

View File

@ -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)

View File

@ -12,7 +12,7 @@ $staticFPSMode:int = 0
$fpsLimitNormal = 60 $fpsLimitNormal = 60
$fpsLimitAdvanced = 60 $fpsLimitAdvanced = 60
$fpsLimit = 0 $keepVsync:int = 0
$frameAverageAmount = 8 $frameAverageAmount = 8
$fenceMethod = 1 $fenceMethod = 1
@ -46,42 +46,49 @@ name = 240FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 240 $fpsLimitNormal = 240
$keepVsync:int = 0
[Preset] [Preset]
name = 165FPS Limit name = 165FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 165 $fpsLimitNormal = 165
$keepVsync:int = 0
[Preset] [Preset]
name = 144FPS Limit name = 144FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 144 $fpsLimitNormal = 144
$keepVsync:int = 0
[Preset] [Preset]
name = 120FPS Limit name = 120FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 120 $fpsLimitNormal = 120
$keepVsync:int = 0
[Preset] [Preset]
name = 90FPS Limit name = 90FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 90 $fpsLimitNormal = 90
$keepVsync:int = 0
[Preset] [Preset]
name = 75FPS Limit name = 75FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 75 $fpsLimitNormal = 75
$keepVsync:int = 0
[Preset] [Preset]
name = 72FPS Limit name = 72FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 72 $fpsLimitNormal = 72
$keepVsync:int = 0
[Preset] [Preset]
name = 60FPS Limit (Default) name = 60FPS Limit (Default)
@ -89,30 +96,35 @@ category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
default = 1 default = 1
$fpsLimitNormal = 60 $fpsLimitNormal = 60
$keepVsync:int = 0
[Preset] [Preset]
name = 55FPS Limit name = 55FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 55 $fpsLimitNormal = 55
$keepVsync:int = 0
[Preset] [Preset]
name = 45FPS Limit name = 45FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 45 $fpsLimitNormal = 45
$keepVsync:int = 0
[Preset] [Preset]
name = 30FPS Limit name = 30FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 30 $fpsLimitNormal = 30
$keepVsync:int = 1
[Preset] [Preset]
name = 20FPS Limit name = 20FPS Limit
category = FPS Limit category = FPS Limit
condition = $advancedMode == 0 condition = $advancedMode == 0
$fpsLimitNormal = 20 $fpsLimitNormal = 20
$keepVsync:int = 0
# Advanced Settings # Advanced Settings
@ -124,48 +136,56 @@ name = No FPS Limit (for benchmarking)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 500 $fpsLimitAdvanced = 500
$keepVsync:int = 0
[Preset] [Preset]
name = 244FPS (ideal for 244Hz displays) name = 244FPS (ideal for 244Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 244 $fpsLimitAdvanced = 244
$keepVsync:int = 0
[Preset] [Preset]
name = 240FPS (ideal for 240Hz displays) name = 240FPS (ideal for 240Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 240 $fpsLimitAdvanced = 240
$keepVsync:int = 0
[Preset] [Preset]
name = 165FPS (ideal for 165Hz displays) name = 165FPS (ideal for 165Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 165 $fpsLimitAdvanced = 165
$keepVsync:int = 0
[Preset] [Preset]
name = 144FPS (ideal for 144Hz displays) name = 144FPS (ideal for 144Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 144 $fpsLimitAdvanced = 144
$keepVsync:int = 0
[Preset] [Preset]
name = 120FPS (ideal for 240/120/60Hz displays) name = 120FPS (ideal for 240/120/60Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 120 $fpsLimitAdvanced = 120
$keepVsync:int = 0
[Preset] [Preset]
name = 75FPS (ideal for 75Hz displays) name = 75FPS (ideal for 75Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 75 $fpsLimitAdvanced = 75
$keepVsync:int = 0
[Preset] [Preset]
name = 72FPS (ideal for 144Hz displays) name = 72FPS (ideal for 144Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 72 $fpsLimitAdvanced = 72
$keepVsync:int = 0
[Preset] [Preset]
name = 60FPS (ideal for 240/120/60Hz displays) name = 60FPS (ideal for 240/120/60Hz displays)
@ -173,42 +193,77 @@ category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
default = 1 default = 1
$fpsLimitAdvanced = 60 $fpsLimitAdvanced = 60
$keepVsync:int = 0
[Preset] [Preset]
name = 55FPS (ideal for 165Hz displays) name = 55FPS (ideal for 165Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 55 $fpsLimitAdvanced = 55
$keepVsync:int = 0
[Preset] [Preset]
name = 48FPS (ideal for 144Hz displays) name = 48FPS (ideal for 144Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 48 $fpsLimitAdvanced = 48
$keepVsync:int = 0
[Preset] [Preset]
name = 40FPS (ideal for 240/120/60Hz displays) name = 40FPS (ideal for 240/120/60Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 40 $fpsLimitAdvanced = 40
$keepVsync:int = 0
[Preset] [Preset]
name = 33FPS (ideal for 165Hz displays) name = 33FPS (ideal for 165Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 33 $fpsLimitAdvanced = 33
$keepVsync:int = 0
[Preset] [Preset]
name = 30FPS (ideal for 240/120/60Hz displays) name = 30FPS (ideal for 240/120/60Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 30 $fpsLimitAdvanced = 30
$keepVsync:int = 1
[Preset] [Preset]
name = 20FPS (ideal for 240/120/60Hz displays) name = 20FPS (ideal for 240/120/60Hz displays)
category = Framerate Limit category = Framerate Limit
condition = $advancedMode == 1 condition = $advancedMode == 1
$fpsLimitAdvanced = 20 $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 # Cutscene FPS Limit Mode
@ -378,4 +433,4 @@ $debugMultiplier = -100
[Control] [Control]
vsyncFrequency = ($advancedMode * $fpsLimitAdvanced) + ((($advancedMode+1) % 2) * $fpsLimitNormal) vsyncFrequency = (($keepVsync == 1) * 60) + (($keepVsync == 0) * ((($advancedMode == 1) * $fpsLimitAdvanced) + (($advancedMode == 0) * $fpsLimitNormal)))