This improves my PC's performance on RS2 Hoth by... 0.1% or so, which I
think is within the margin of error. But this change also cuts down on
boilerplate.
If the call to `Open` a perf map fails don't set `s_is_enabled` (though
it could already be true if you're also using VTUNE) and don't call
`std::setvbuf` with a null stream.
Also fix a typo in a comment (`if` -> `in`)
If all inputs to an fmadds instruction (including cousins like fmsubs,
fnmadd...) are single-precision, then the result is identical between a
double-precision calculation with an error-free transform (whether the
calculation is fused or not) and a single-precision FMA instruction
(must be fused). So as a performance optimization in JitArm64, if we
were going to use double precision with EFT but the inputs are singles,
instead we'll use a normal single-precision FMA instruction without
anything extra. This lets us skip both the EFT and double-to-single
conversions.
Also renaming `inaccurate_fma` to `nonfused` because it's confusing that
`inaccurate_fma` and `m_accurate_fmadds` have such similar names
despite controlling separate things.
If result_reg is set to a temporary register instead of VD because of
accurate NaNs, there's no need to allocate a secondary temporary
register because of inaccurate FMA.
TextureCacheBase::LoadImpl has a hot path where the passed-in
TextureInfo never gets used. Instead of passing in a TextureInfo, let's
pass in the stage and create the TextureInfo from the stage if needed.
This unlocks somewhere above an additional 4% performance boost in the
Hoth level of Rogue Squadron 2 on my PC. Performance varies, making it
difficult for me to measure, so treat this as a very approximate number.
When we're emulating single-precision FMA using an FMA instruction,
there's no precision benefit from using a double-precision instruction,
assuming all inputs are single-precision. But when we're emulating
single-precision FMA using separate multiplication and addition
instructions, there is.
This change increases the precision of inaccurate FMA to the same level
as Jit64, which matters since the only reason we have the inaccurate
FMA mode is for sync compatibility with Jit64.
The check only happened if `USE_VTUNE` wasn't defined, and in that case
it was immediately followed by the same check again.
The check used to avoid an unnecessary call to `StringFromFormatV` in
certain circumstances, but that call was removed in
be81fe86e1.
Building on macOS with a recent CMake version would result in the
following error:
CMake Error at Source/Core/MacUpdater/CMakeLists.txt:48 (add_custom_command):
The following keywords are not supported when using
add_custom_command(TARGET): DEPENDS
As it turns out, this form of `add_custom_command` does not accept
DEPENDS, but versions of CMake prior to 3.31 silently dropped it.
The TextureInfo constructor creates a vector of MipLevels. This could be
good for performance if MipLevels are accessed very often for each
TextureInfo, but that's not the case. Dolphin creates thousands of
TextureInfos per second that it never accesses the mipmap levels of
because there's a hit in the texture cache, and in the uncommon case of
a texture cache miss, the mipmap levels only get looped through once.
To make the common case of texture cache hits as fast as possible, let's
not create a vector in the TextureInfo constructor. This commit
implements a custom iterator for MipLevels instead.
In my testing on the Death Star level of Rogue Squadron 2, this speeds
up TextureInfo::FromStage by 200%, giving an overall emulation speedup
of a bit over 1%. Results on the Hoth level are even better, with
TextureInfo::FromStage being close to 300% faster and overall emulation
being over 4% faster. (Single core, no GPU texture decoding.)
Profiling Dolphin using Heaptrack, this turns out to be the cause of
more than half of all temporary allocations. Though since it's in a
separate thread, I suppose it wasn't affecting performance very much.
Combined with the previous commit, this brings the TrampolineInfo struct
down to 48 bytes. This matters, because Jit64 has a big
std::unordered_map where it stores many megabytes of TrampolineInfo
entries.
The key saving comes from shrinking the len member from u32 to u16. It
should be safe to even turn it into a u8, but going that far brings no
additional savings due to how the padding works out.
By moving members of the OpArg struct around, we can cut down on how
much padding the struct needs. Now it has a size of 16 bytes, small
enough for function calls to pass it in two registers instead of on the
stack.
Re-added a line of code from a cancelled PR (that I thought was in
main already) that RetroAchievements uses to properly synchronize
memory reads and writes with the emulator frames. This appears to
fix some pretty major freezing and deadlocks in the RA dev tools.
CMake support for c++23 was added in 3.20.
Remove statements explicitly setting the following policies to NEW.
These policies were introduced in or before 3.20, and now that 3.20 is
the minimum version they will automatically have the NEW behavior.
Policy: Introduced in
CMP0079: 3.13
CMP0084: 3.14
CMP0091: 3.15
CMP0092: 3.15
CMP0099: 3.17
CMP0117: 3.20
Disable scanning c++ source files for module imports (introduced as
CMP0155 in 3.28) since we don't use modules and that policy triggers
build errors with Clang if the clang-scan-deps tool isn't installed.
cpp-ipc is explicitely only available on Windows, Linux, QNX, and FreeBSD. Trying to build dolphin on any another BSD such as OpenBSD or Haiku currently leads to failure because of this.
During my quest to rid Dolphin of the memory-unsafe GetPointer function,
I didn't realize that DSPHLE had its own set of memory access functions
that were essentially duplicating the standard ones. This commit gets
rid of them and replaces calls to them with calls to MemoryManager.
Move the constructor and destructor after the definition of the class
`Internal`.
This fixes an error generated by Clang from the destructor of
`std::unique_ptr<Internal>` when setting the standard version to c++23:
`invalid application of 'sizeof' to an incomplete type 'Metal::ObjectCache::Internal'`.
Fix an error generated by Clang from the destructor of
`std::unique_ptr<FileInfo> m_file_info` when setting the standard
version to c++23:
`invalid application of 'sizeof' to an incomplete type 'DiscIO::FileInfo'`
Checked on hardware that this bias was not added because I had assumed the other way around would be true, forgot to ask about making a PR for this when I initially had done so
If we're on an x64 CPU that doesn't have the MOVBE extension, trying to
SwapAndStore a host register results in that register's value getting
clobbered with the swapped value. Jit64::stX and Jit64::stXx detect this
case, and if necessary, emit a MOV to a register that's fine to clobber.
This logic was broken by the merge of PR 12134. Jit64::stX and
Jit64::stXx were assuming that if RegCache::IsImm returns true for a
guest register, calling RegCache::Use or RegCache::BindOrImm for that
guest register would result in an immediate. However, PR 12134 made it
possible for a guest register to have both a host register and an
immediate in the register cache at the same time. When this happens,
RegCache::IsImm returns true, yet RegCache::Use and RegCache::BindForImm
return an RCOpArg whose Location returns a host register. (To make it
extra confusing, RCOpArg::IsImm calls RegCache::IsImm if the RCOpArg
came from RegCache, so RCOpArg::IsImm returns true!)
To fix this, in cases where Jit64::stX and Jit64::stXx explicitly need
an immediate to avoid having to emit an extra MOV, let's call
RegCache::Imm32 so that we're certain that we're getting an immediate.
This fixes an issue on older x64 CPUs that manifested as e.g. completely
broken graphics in Spyro: Enter the Dragonfly.
The constant propagation PR made it so that a guest register can be
present in the register cache as both a host register and an immediate
at the same time. If such a guest register is requested from the
register cache, the register cache prefers returning it as a host
register. However, RCOpArg::IsImm still returns true in this case. This
is confusing, especially since OpArg::IsImm does not return true if the
RCOpArg is converted into an OpArg.
This commit makes RCOpArg::IsImm check whether RCOpArg::Location returns
an immediate, so that RCOpArg::IsImm returns false when a host register
is being used. Code that wants to know whether an immediate exists in
the register cache rather than whether an immediate is currently being
used should call RegCache::IsImm instead.
Recently came across a strange issue where Dolphin would hard crash in most games with this error:
```sh
/usr/include/c++/15.2.1/optional:1165: constexpr const _Tp* std::optional<_Tp>::operator->() const [with _Tp = InputCommon::ImagePixelData]: Assertion 'this->_M_is_engaged()' failed.
```
The culprit turned out to be accessing `host_key_image` which is an `std::optional` thay may return `std::nullopt`. I'm not sure why this issue started occuring for me since I've had no issue with my Dynamic Input Textures in the past? But this fixes a crash if the image fails to load.
Aside from being unnecessary, on Windows the flag prevents two instances
of Dolphin (one instance from before 2509-371 when the flag was
introduced and the other after) from running the same ROM
simultaneously.
Attempting to do so generated the false error `"[Rom]" is an invalid
GCM/ISO file, or is not a GC/Wii ISO.` followed by `Failed to init core`
and emulation shutdown on the second instance to start the game. Fixing
the incorrect error message is a task I'm deferring to another PR.
The problem didn't happen when both instances were 2509-371 or later,
but I ran into it while bisecting an issue and it'd be nice to avoid
that problem in the future.
We have an optimization where the guest carry flag is kept in the host
carry flag between certain back-to-back pairs of integer instructions.
If the second instruction falls back to the interpreter, then
FallBackToInterpreter should flush the carry flag to m_ppc_state,
otherwise the interpreter reads a stale carry flag and at some later
point Jit64 trips the "Attempt to modify flags while flags locked!"
assertion.
An alternative solution would be to not store the guest carry flag in
the host carry flag to begin with if we know the next instruction is
going to fall back to the interpreter, but knowing that in advance is
non-trivial. Since interpreter fallbacks aren't exactly intended to be
super optimized, I went for the flushing solution instead, which is how
JitArm64 already works. In most cases, the emitted code shouldn't even
differ between these two solutions.
Note that the problematic situation only happens if the first integer
instruction doesn't fall back to the interpreter but the second one
does. This used to be impossible because there's no "JIT disable"
setting that's granular enough to disable some integer instructions but
not all, but with the constant propagation PR, it's possible if constant
propagation is able to entirely evaluate the first instruction but not
the second.
This makes SetUID take more emulated time giving the host more time to actually do the work.
The Wii menu "Data Management" -> "Save Data" -> "Wii" screen is no longer nearly as hard to emulate at full speed.
Constructing the UIDSys from the filesystem is a major bottleneck in the Wii menu "Data Management" -> "Save Data" -> "Wii" screen and this change makes it about twice as fast.
Call `UseNoImm` instead of `Use` on parameter `a` of `MultiplyImmediate`
since `Ra` gets passed to `IMUL` which asserts that parameter is not an
immediate.
There was a LINUX check added in b3bdad4, but this should be removed as this change applies to all Qt supported platforms. Simply put, GuiPrivate CMake files were introduced in Qt 6.9 and are now enforced in Qt 6.10 and are not platform-dependent.
Having it be static leads to a race condition if two different threads
call RunOnCPUThread with wait_for_completion set to true. (There's
currently nobody calling RunOnCPUThread from anything other than the
host thread, so this hasn't led to any consequences yet.)
The optimizations for subfcx introduced in #13852 also apply to subfx.
Rather than duplicating the logic, we merge the handlers, like we did
in #10120 for x86.
In 405baed805, we made the assumption that whether OpenAndroidContent
is able to create a new file depends on the open mode, the document
provider, and the positions of the celestial bodies. However, I'm
fairly sure that it can't create files at all as currently implemented.
First, ContentResolver.openFileDescriptor is documented as throwing
FileNotFoundException if the file doesn't exist. Now, the SAF
documentation is notoriously unreliable on matters like these, and
document providers can do whatever they want anyway, so we can't
actually trust this to mean that FileNotFoundException will always
be thrown if the file doesn't exist. But second, the Dolphin function
ContentHandler.unmangle is also unable to handle files that don't
already exist (unless the passed-in URI isn't mangled to begin with,
but to the best of my knowledge, there's no way to get a non-mangled URI
for a file that doesn't exist (unless you make assumptions about how the
document provider's URI scheme works, which we don't do in Dolphin)).
Summed up, it looks a lot like OpenAndroidContent can't create files,
or at the very least can't do so reliably.
Therefore I'm making DirectIOFile throw an assertion and return false
in situations where it's being asked to create a file under SAF. For
reference, there's no code in Dolphin that actually tries to create a
file under SAF. In all instances where we use SAF to write to files, the
file is created by the system file picker before it returns the URI of
the file to us.
What does this change gain us? First, if someone in the future tries to
use DirectIOFile to create a file under SAF, they'll be immediately
informed that this isn't supported rather than discovering it doesn't
work and chalking it up to SAF being bad for unpredictable reasons.
Second, we get rid of a call to File::Exists, which is a notable
performance improvement for game list scanning due to SAF and Dolphin's
"unmangling" being bad for reasons that unfortunately are entirely
predictable to those of us who have stared into the SAF void.
I've tested that game list scanning and game conversion still works.
When BindToRegister is called, the register cache marks the relevant
guest register as no longer containing an immediate. However, subfcx was
calling GetImm after BindToRegister. This led to a lot of panic alerts
after 2995aa5be4 added an assert to GetImm to check that the passed-in
register is an immediate.
Both before and after 2995aa5be4, the actual value of the immediate
wasn't overwritten by BindForRegister, only the fact that the register
is an immediate. Because of this, the emitted code happened to work
correctly.
If the build is an Android build, identify it as such in the AchievementManager user agent so that android builds can be tracked separately for debug purposes.
This fixes a regression in ac84ea17a6. That commit was intended to
reduce the number of MapInMemoryRegion calls while otherwise keeping
behavior the same, but it accidentally messed up our logic for writing
to m_logical_page_mappings. This commit restores the old
m_logical_page_mappings behavior, fixing a crash that would occur when
running JitArm64 without fastmem.
Like Jit64, JitArm64 now keeps track of the location of a guest register
using three booleans: Whether it is in ppcState, whether it is in a host
register, and whether it is a known immediate. The RegType enum remains
only for the purpose of keeping track of what format FPRs are stored in
in host registers.
Like the previous commit did for Jit64, JitArm64 can now handle the
combination of a value simultaneously being in a host register and being
a known immediate.
Unlike with Jit64, I've put the codegen-affecting changes in this commit
and the move away from the RegType enum in a follow-up commit. This is
in part because the design of JitArm64 made it easy to implement the
codegen-affecting changes without combining it with a big bang
refactorization, and in part because we need to keep RegType around for
keeping track of different float formats in Arm64FPRCache, complicating
the refactorization a bit.
They're now stored in ConstantPropagation instead.
I've also removed the LocationType enum. The location of each guest
register is now tracked using three booleans: Whether it is in ppcState,
whether it is in a host register, and whether it is a known immediate.
The first two of these booleans are stored in the register cache, and
the last one is stored in ConstantPropagation. This new model allows us
to handle the combination of a value simultaneously being in a host
register and being a known immediate. It also keeps track of which
registers are dirty, which was previously kept track of in X64CachedReg.
The old model maps to the new model as follows:
default host_reg immediate
Default true false false
Discarded false false false
Bound (!dirty) true false
Immediate false false true
SpeculativeImmediate true false true
[previously unrepresentable] (!dirty) true true
This commit makes the JIT set/clear the individual registers of
ConstantPropagation immediately instead of at the end of the
instruction. This is needed to prevent Jit64::ComputeRC, which reads
from a register written to earlier during the same instruction, from
reading back stale register values from ConstantPropagation in the next
commit.
To find out whether a host register needs to be unlocked, FlushRegisters
checks if the guest register is known to be a zero immediate. This works
right now, but it will stop working correctly once we gain the ability
to have a guest register be a known immediate and be in a host register
at the same time, because a register that's known to be a zero immediate
may have had a host register allocated prior to the call to
FlushRegisters. Instead, we should check whether the register is
RegType::Register after we're done calling BindForRead.
Restructuring things in this way brings two immediate benefits:
* Code is deduplicated between Jit64 and JitArm64.
* Materializing an immediate value in a register no longer results in us
forgetting what the immediate value was.
As a more long-term benefit, this lets us also run constant propagation
as part of PPCAnalyst, which could let us do cool stuff in the future
like statically determining whether a conditional branch will be taken.
But I have nothing concrete planned for that right now.
warning: definition of implicit copy constructor for 'BitField<2, 2, ColorChannel>' is deprecated because it has a user-declared copy assignment operator [-Wdeprecated-copy]
Redundant access specifier has the same accessibility as the previous access specifier.