The Disabled state sits between Game Closed and completely Shutdown - stronger than the former, as it refuses to let a game be opened again until AchievementManager is restored (which only happens upon a fresh core boot) but it isn't completely shut down and will still allow the player to be logged in and access the achievement settings and their (global) achievement header.
This change splits LoadGameAsync into three methods: two HashGame methods to accept either a string filepath or a volume, and a common LoadGameSync that both HashGames call to actually process the code. In the process, some minor cleanup, and the hash resolution now takes place on the work queue asynchronously.
Normally we only flush registers right at the end of each PPC
instruction. However, for PPC instructions that use a lot of registers
one at a time, it's beneficial to do this flushing work in the middle
of the instruction instead, reducing the risk of register starvation
and improving pipelining.
This should have been done when rebasing 6cc4f593e5 after the merge of
3a00ff625e. There are no correctness implications as far as I know,
only very minor performance implications.
This worked correctly on the JIT vertex loaders, and for the equivalent case with texture coordinates. I'm not aware of any games this affects (but libogc does mention a semi-related scenario at 6bc0317c7d/gc/ogc/gx.h (L1855-L1857).)
Jimmie Johnson's Anything with an Engine is known to use texture coordinate 7 (and only texture coordinate 7) in some cases. There are a lot of possible edge-cases, so this test brute-forces all combinations with coordinates 0, 1, and 2.
This test fails with the non-JIT vertex loader due to an issue fixed in a later commit in this PR. (Note that the non-JIT vertex loader is only used on machines where no JIT is available or if COMPARE_VERTEXLOADERS is enabled in VertexLoaderBase.cpp.)
This adds the actual switch to turn on Hardcore Mode to the settings tab of the Achievements dialog. It is accompanied by a large tooltip warning explaining what it does and when it can be enabled.
The switch is only enabled to be turned on when no game is running, so that games are started in hardcore mode and can only be loaded via the console's memory card, as in the original hardware. Hardcore may be turned off while a game is running, but cannot be turned back on until the game is disabled.
The toggle trigger for hardcore mode also automatically disables the settings that are not allowed during hardcore mode.
Finally, the original flag in AchievementSettingsWidget to set whether things are enabled in hardcore mode (primarily Leaderboards) is replaced with the actual Hardcore Mode setting.
Play Input Recording would potentially unlock achievements without any player input and needs to be disabled. If a recording is already playing, hardcore mode cannot be enabled.
The player getting a better view of their surroundings than the game would normally allow could possibly give the player an advantage over the original hardware, so Freelook is disabled in hardcore mode. To do this, I disable the config flag for Freelook when it is accessed, to make sure that it is disabled whether it was enabled before or after hardcore mode was enabled.
Memory patches would be an easy way to manipulate the memory needed to calculate achievement logic, so they must be disabled. Riivolution patches that do not affect memory are allowed, as they will be hashed with the game file.
Debug Mode gives players direct read and write access to memory, which could be used to completely manipulate RetroAchievements logic and therefore is not allowed in hardcore mode.
Slowing down the emulator would artificially improve reaction times and is therefore disallowed in hardcore mode. Speeds faster than 1x are allowed, but any speed below 1x is scaled back up to 1x.
Frame advancing is easily exploitable for slowing down a game and artificially improving reaction times and is not allowed in RetroAchievements hardcore mode.
While saving states is allowed (especially for the purpose of debugging), RetroAchievements does not allow loading saved states when hardcore mode is on.
This widget will be used in several places to notify the player that a feature has been disabled because hardcore mode is on. It includes a button to open the Achievement Settings so that Hardcore Mode may be turned off. Also included is the framework required to open AchievementsWindow specifically on the Settings tab.
Hardcore Mode is a RetroAchievements feature for enabling as close to original hardware as possible, to keep a fair, challenging, and competitive playing field for achievements (which get tallied differently and emphasized more in hardcore) and leaderboards (where it is mandatory) at the cost of several common emulator features that provide advantages, such as state loading and slower emulation speeds.
This commit just adds the flag to the AchievementSettings, with more to come.
Dolphin would crash when running with a fastmem arena but without
fastmem. This regression was caused by merging 9192128c50 without
adapting it after the merge of 0606433404.
This has the same effect in the end, but in my opinion, doing it this
way makes it more clear for the reader why we can read from ppcState at
JIT time, something that makes no sense for everything else in ppcState.
By making the JIT cache check if the current state of MMCR0 and MMRC1
matches the state they had at the time the JIT block was compiled, we
solve a correctness issue (marked in a comment as a speed hack).
Not known to affect any games.
This must have broken in a rebase of one of my recently merged PRs.
Dolphin still worked correctly with this bug, for two reasons:
1. Most AArch64 users are not on Windows, and therefore normally do have
the entry points map.
2. When the bug was triggered, Dolphin would fall back to the slower
path rather than crashing.
Instead of shifting left by 1, we can first shift right by 2 and then
left by 3. This is both faster and smaller, because we get the right
shift for free with the masking and the left shift for free with the
address calculation. It also happens to match the pseudocode more
closely, which is always nice for readability.
This slightly improves instruction-level parallelism in Jit64's slow
dispatcher by shifting the PC left instead of the MSR.
In the past, this also enabled an optimization in JitArm64's fast path
where we could use LDP to load normalEntry and msrBits in one
instruction, but this was superseded by fd9c970.
Just to make the code easier to understand at a glance. I especially
found it a bit annoying to reason about whether callee-saved registers
like W28 were being used because we needed a callee-saved register or
just for no reason in particular.
X8 and up is what compilers normally use when they're not register
starved.
AArch64's handling of NaNs in arithmetic instructions matches PowerPC's
as long as no more than one of the operands is NaN. If we know that all
inputs except the last input are non-NaN, we can therefore skip checking
the last input. This is an optimization that in principle only works for
non-SIMD operations, but ps_sumX effectively is non-SIMD as far as the
arithmetic part of it is concerned, so we can use it there too.
FL_EVIL is only used for blocking instructions from being reordered.
There are three types of instructions which have FL_EVIL set:
1. CR operations. The previous commits improved our CR analysis
and removed FL_EVIL from these instructions.
2. Load/store operations. These are always blocked from reordering
due to always having canCauseException set.
3. isync. I don't know if we actually need to prevent reordering
around this one, since as far as I know we only do reorderings
that are guaranteed to not change the behavior of the program.
But just in case, I've renamed FL_EVIL to FL_NO_REORDER instead of
removing it entirely, so that it can be used for this instruction.
Other than the CR instructions, which we now analyze properly,
all the covered instructions are not integer operations and also
have either FL_ENDBLOCK or FL_EVIL set, so there are two other
checks in CanSwapAdjacentOps that will reject them.
This brings the analysis done for condition registers
more in line with the analysis done for GPRs and FPRs.
This gets rid of the old wantsCR member, which wasn't actually
used anyway. In case someone wants it again in the future, they
can compute the bitwise inverse of crDiscardable.
Instead of materializing the quiet bit in a register and ORing the NaN
with it, we can perform an arithmetic operation on the NaN. This is a
cycle or two slower on some CPUs in cases where generating the quiet bit
pipelined well, but this is farcode that rarely runs, so instruction
fetch latency is the bigger concern. And for non-SIMD cases, we also
save a register.
By using MOVI2R+MOVI2R+CSEL in the zero case instead of doing bitwise
operations on the output of the other MOVI2R+MOVI2R+CSEL, we avoid using
BFI, an instruction that takes two cycles on most CPUs. The instruction
count is the same and the pipelining should be at least equally good.
This is a little trick I came up with that lets us restructure our float
classification code so we can exit earlier when the float is normal,
which is the case more often than not.
First we shift left by 1 to get rid of the sign bit, and then we count
the number of leading sign bits. If the result is less than 10 (for
doubles) or 7 (for floats), the float is normal. This is because, if the
float isn't normal, the exponent is either all zeroes or all ones.
Not sure if this was causing correctness issues – it depends on whether
the HLE code was actually reading the discarded registers – but it was
at least causing annoying assert messages in one piece of homebrew.