Compare commits

..

148 Commits

Author SHA1 Message Date
comex 644c3ce609 Rename variables to avoid -Wshadow warnings under GCC 2023-07-01 22:03:21 -07:00
comex 0ed1cb7266 ...actually add the SecureTransport backend to Git. 2023-07-01 17:48:36 -07:00
comex 0e191c2711 Updates:
- Address PR feedback.
- Add SecureTransport backend for macOS.
2023-07-01 17:27:35 -07:00
comex 98685d48e3 Merge remote-tracking branch 'origin/master' into ssl 2023-07-01 15:01:11 -07:00
zhaobot 8857911216 Update translations (2023-07-01) (#10972)
Co-authored-by: The yuzu Community <noreply-fake@community.yuzu-emu.org>
2023-07-01 05:41:49 +02:00
Fernando S a10bdaf934 Merge pull request #10953 from FernandoS27/oh-oopsies-yfc
Texture cache: Fix YFC regression due to code testing
2023-06-30 20:25:09 +02:00
Fernando S 9252ad4e10 Merge pull request #10956 from FernandoS27/pikmin-another-game-ill-hate
AccelerateDMA: Don't accelerate 3D texture DMA operations
2023-06-30 09:37:07 +02:00
Fernando Sahmkow 596a6132b9 AccelerateDMA: Don't accelerate 3D texture DMA operations 2023-06-29 17:23:29 +02:00
Charles Lombardo 45be4c3214 Merge pull request #10955 from 8bitDream/gradle
android: Suppress a known incompatibility
2023-06-29 11:07:00 -04:00
liamwhite 5e70db0d43 Merge pull request #10935 from Morph1984/mwaitx
x64: Make use of monitorx instructions for power efficient sleeps (AMD)
2023-06-29 10:01:26 -04:00
liamwhite 4c705db73e Merge pull request #10937 from german77/ring
input_common: Remove duplicated enum and fix ring detection
2023-06-29 10:01:19 -04:00
liamwhite 7de778ad39 Merge pull request #10946 from goldenx86/amdBlending
Blacklist EDS3 blending from new AMD drivers
2023-06-29 10:01:11 -04:00
Abandoned Cart 13506e7782 android: Suppress a known incompatibility
Android Gradle plugin 8.0.2 is designed for API 33, but a newer plugin hasn't been released yet. The warning message is rather extravagant, but also suggests adding this property if you are aware of the risks.
2023-06-29 07:32:12 -04:00
Fernando Sahmkow b62121fd60 Texture cache: Fix YFC regression due to code testing 2023-06-29 11:58:45 +02:00
german77 ac755476cd input_common: Allow timeouts to happen while scanning for a ring 2023-06-29 01:07:39 -06:00
bunnei 217e958a95 Merge pull request #10945 from t895/android-14
android: Android 14 support
2023-06-28 16:30:12 -07:00
Matías Locatti ed93cbd462 Blacklist EDS3 blending from new AMD drivers 2023-06-28 20:10:27 -03:00
Charles Lombardo b76b698c17 android: Android 14 support
Specifies the permissions needed for the changes to foreground services in Android 14.
2023-06-28 16:15:18 -04:00
liamwhite b60b70e86d Merge pull request #10837 from liamwhite/mali-support
android: Mali support
2023-06-28 12:53:17 -04:00
german77 df9685a21c input_common: Remove duplicated DriverResult enum 2023-06-28 09:49:47 -06:00
Morph 295fc7d0f8 x64: cpu_wait: Implement MWAITX for non-MSVC compilers 2023-06-28 01:39:15 -04:00
Morph 2b68a3cbbf x64: cpu_wait: Remove magic values 2023-06-28 01:39:06 -04:00
Morph 3d868baaa4 x64: cpu_wait: Make use of MWAITX in MicroSleep
MWAITX is equivalent to UMWAIT on Intel's Alder Lake CPUs.
We can emulate TPAUSE by using MONITORX in conjunction with MWAITX to wait for 100K cycles.
2023-06-28 01:38:55 -04:00
Morph 4303ed614d x64: Add detection of monitorx instructions
monitorx introduces 2 instructions: MONITORX and MWAITX.
2023-06-28 01:36:06 -04:00
GPUCode ddcc958336 renderer_vulkan: Prevent crashes when blitting depth stencil 2023-06-27 18:00:09 -07:00
GPUCode eac46ad7ce video_core: Add BCn decoding support 2023-06-27 18:00:09 -07:00
GPUCode b8c96cee5f renderer_vulkan: Add more feature checking 2023-06-27 18:00:09 -07:00
GPUCode 220a42896d renderer_vulkan: Don't assume debug tool with debug renderer
* Causes crashes because mali drivers don't support debug utils
2023-06-27 18:00:09 -07:00
GPUCode 1522b95658 renderer_vulkan: Bump minimum SPIRV version
* 1.3 is guaranteed on all 1.1 drivers
2023-06-27 18:00:09 -07:00
GPUCode c339af37a7 renderer_vulkan: Respect viewport limit 2023-06-27 18:00:09 -07:00
GPUCode a9b44d37e1 renderer_vulkan: Don't add transform feedback flag if unsupported 2023-06-27 18:00:09 -07:00
GPUCode 72e7f5b4dd renderer_vulkan: Add suport for debug report callback 2023-06-27 18:00:09 -07:00
liamwhite 0fe44071f8 Merge pull request #10933 from merryhime/dunno
arm_dynarmic_32: Remove disabling of block linking on arm64
2023-06-27 20:50:24 -04:00
liamwhite e0d406042c Merge pull request #10932 from abouvier/git-indent
gitmodules: normalize indentation and url
2023-06-27 20:50:16 -04:00
liamwhite 0b2798be27 Merge pull request #10930 from lat9nq/msvc-inconsistent-time-zones
settings: Catch runtime_error, fallback time zone
2023-06-27 20:50:06 -04:00
lat9nq 21675c9b68 settings: Clean up includes
Adds <version> since we are looking at C++ implementation version
details. Also moves exception header includes into the if preprocessor
command since we only use it there.
2023-06-27 19:13:54 -04:00
Merry e3c548d081 arm_dynarmic_32: Remove disabling of block linking on arm64 2023-06-27 23:51:49 +01:00
lat9nq 32475efbc4 settings: Catch runtime_error, fallback time zone
Windows will let you select time zones that  will fail in their
own C++ implementation library. Evidently from the stack trace, we get a
runtime error to work with, so catch it and use the fallback.
2023-06-27 18:12:26 -04:00
Morph bd4fefe5e7 Merge pull request #10931 from german77/clang
yuzu: Fix clang format
2023-06-27 18:11:22 -04:00
german77 30544fbfa5 yuzu: Fix clang format 2023-06-27 15:55:23 -06:00
Alexandre Bouvier 33cd3a0db0 gitmodules: normalize indentation and url 2023-06-27 23:39:29 +02:00
Narr the Reg 112b660456 Merge pull request #9663 from EBADBEEF/disable-controller-applet
qt: add option to disable controller applet
2023-06-27 10:32:51 -06:00
liamwhite 2b3bfafb9e Merge pull request #10867 from Kelebek1/dma_safe
Use safe reads in DMA engine
2023-06-27 11:21:47 -04:00
liamwhite c6959449d1 Merge pull request #10473 from GPUCode/vma
Use vulkan memory allocator
2023-06-27 11:21:36 -04:00
liamwhite 20111c86b6 Merge pull request #10495 from bm01/master
input_common: Redesign mouse panning
2023-06-27 11:21:28 -04:00
liamwhite f254ce2c60 Merge pull request #10679 from zeltermann/wakelock-reason
Only use SDL wakelock on Linux
2023-06-27 11:21:20 -04:00
liamwhite dafbc86366 Merge pull request #10916 from ameerj/lolmem
OpenGL: Add Local Memory warmup shader for Nvidia
2023-06-27 11:21:10 -04:00
liamwhite 02882d9251 Merge pull request #10925 from t895/fs-agony
android: Fix size check for content uris
2023-06-26 23:33:51 -04:00
Charles Lombardo 9074a70b01 android: Fix size check for content uris
Fix for checking file size for android content uris
2023-06-26 22:24:18 -04:00
liamwhite 2ed8b3cbb9 Merge pull request #10908 from kiri11/clarify-ring-ui
Clarify Ring-Con configuration message in UI
2023-06-26 14:31:30 -04:00
liamwhite b82c649b0f Merge pull request #10903 from german77/nfc_state
input_common: Improve nfc state handling and 3rd party support
2023-06-26 14:31:23 -04:00
liamwhite ce990adae5 Merge pull request #10901 from german77/sdl_fix
input_common: Make use of new SDL features
2023-06-26 14:31:14 -04:00
liamwhite c280a2fe1c Merge pull request #10894 from lat9nq/bsd-tzdb
nx_tzdb: Update tzdb_to_nx
2023-06-26 14:31:07 -04:00
liamwhite 28df6ac0aa Merge pull request #10888 from 8bitDream/native
android: (native) Parameter types from Android Studio
2023-06-26 14:31:00 -04:00
liamwhite 0030fa9721 Merge pull request #10865 from t895/extension-meme
android: Clean up file extension checks
2023-06-26 14:30:49 -04:00
Charles Lombardo 0f31039831 android: Clean up file extension checks 2023-06-26 13:25:56 -04:00
GPUCode b6c6dcc576 externals: Use cmake subdirectory 2023-06-26 18:59:24 +03:00
Kelebek1 ffbaf574ca Use safe reads in DMA engine 2023-06-26 11:34:02 +01:00
comex d885dd5b64 PR feedback + constification 2023-06-25 19:24:49 -07:00
comex b9f9d0a642 network.cpp: include expected.h 2023-06-25 18:51:39 -07:00
comex 8b9c077826 Disable OpenSSL on Android.
Apparently Android uses BoringSSL, but doesn't actually expose headers
for it in the NDK.
2023-06-25 17:36:51 -07:00
comex cd4b8f037c re-format 2023-06-25 17:09:54 -07:00
comex ac939f08a4 Fix more Windows build errors
I did test this beforehand, but not on MinGW, and the error that showed
up on the msvc builder didn't happen for me...
2023-06-25 17:06:57 -07:00
comex 42015de49b ssl: fix compatibility with OpenSSL 1.1.1
Turns out changes were needed after all.
2023-06-25 16:09:16 -07:00
ameerj 4f160633d3 OpenGL: Limit lmem warmup to NVIDIA
🐸
2023-06-25 19:06:51 -04:00
ameerj 405eae3734 shaders: Track local memory usage 2023-06-25 18:59:33 -04:00
ameerj b198339580 emit_glasm: Fix lmem size computation 2023-06-25 18:43:52 -04:00
ameerj 82107b33a2 OpenGL: Add Local Memory warmup shader 2023-06-25 18:43:23 -04:00
comex 4a35569921 Fixes:
- Add missing virtual destructor on `SSLBackend`.

- On Windows, filter out `POLLWRBAND` (one of the new flags added) when
  calling `WSAPoll`, because despite the constant being defined on
  Windows, passing it calls `WSAPoll` to yield `EINVAL`.

- Reduce OpenSSL version requirement to satisfy CI; I haven't tested
  whether it actually builds (or runs) against 1.1.1, but if not, I'll
  figure it out.

- Change an instance of memcpy to memmove, even though the arguments
  cannot overlap, to avoid a [strange GCC
  error](https://github.com/yuzu-emu/yuzu/pull/10912#issuecomment-1606283351).
2023-06-25 15:06:52 -07:00
lat9nq f5569bfed9 nx_tzdb: Update tzdb_to_nx to 212afa2
Moves build data to a separate directory so the build happens out of the source
tree.
2023-06-25 17:20:18 -04:00
comex 8905142f43 ssl: rename argument to avoid false positive codespell warning
The original name `larg` was copied from the OpenSSL documentation and
is not a typo of 'large' but rather an abbreviation of '`long`
argument'.  But whatever, no harm in adding an underscore.
2023-06-25 13:10:41 -07:00
comex 7cc428ddf6 socket_types: Improve comment 2023-06-25 13:10:15 -07:00
comex 8e703e08df Implement SSL service
This implements some missing network APIs including a large chunk of the SSL
service, enough for Mario Maker (with an appropriate mod applied) to connect to
the fan server [Open Course World](https://opencourse.world/).

Connecting to first-party servers is out of scope of this PR and is a
minefield I'd rather not step into.

 ## TLS

TLS is implemented with multiple backends depending on the system's 'native'
TLS library.  Currently there are two backends: Schannel for Windows, and
OpenSSL for Linux.  (In reality Linux is a bit of a free-for-all where there's
no one 'native' library, but OpenSSL is the closest it gets.)  On macOS the
'native' library is SecureTransport but that isn't implemented in this PR.
(Instead, all non-Windows OSes will use OpenSSL unless disabled with
`-DENABLE_OPENSSL=OFF`.)

Why have multiple backends instead of just using a single library, especially
given that Yuzu already embeds mbedtls for cryptographic algorithms?  Well, I
tried implementing this on mbedtls first, but the problem is TLS policies -
mainly trusted certificate policies, and to a lesser extent trusted algorithms,
SSL versions, etc.

...In practice, the chance that someone is going to conduct a man-in-the-middle
attack on a third-party game server is pretty low, but I'm a security nerd so I
like to do the right security things.

My base assumption is that we want to use the host system's TLS policies.  An
alternative would be to more closely emulate the Switch's TLS implementation
(which is based on NSS).  But for one thing, I don't feel like reverse
engineering it.  And I'd argue that for third-party servers such as Open Course
World, it's theoretically preferable to use the system's policies rather than
the Switch's, for two reasons

1. Someday the Switch will stop being updated, and the trusted cert list,
   algorithms, etc. will start to go stale, but users will still want to
   connect to third-party servers, and there's no reason they shouldn't have
   up-to-date security when doing so.  At that point, homebrew users on actual
   hardware may patch the TLS implementation, but for emulators it's simpler to
   just use the host's stack.

2. Also, it's good to respect any custom certificate policies the user may have
   added systemwide.  For example, they may have added custom trusted CAs in
   order to use TLS debugging tools or pass through corporate MitM middleboxes.
   Or they may have removed some CAs that are normally trusted out of paranoia.

Note that this policy wouldn't work as-is for connecting to first-party
servers, because some of them serve certificates based on Nintendo's own CA
rather than a publicly trusted one.  However, this could probably be solved
easily by using appropriate APIs to adding Nintendo's CA as an alternate
trusted cert for Yuzu's connections.  That is not implemented in this PR
because, again, first-party servers are out of scope.

(If anything I'd rather have an option to _block_ connections to Nintendo
servers, but that's not implemented here.)

To use the host's TLS policies, there are three theoretical options:

a) Import the host's trusted certificate list into a cross-platform TLS
   library (presumably mbedtls).

b) Use the native TLS library to verify certificates but use a cross-platform
   TLS library for everything else.

c) Use the native TLS library for everything.

Two problems with option a).  First, importing the trusted certificate list at
minimum requires a bunch of platform-specific code, which mbedtls does not have
built in.  Interestingly, OpenSSL recently gained the ability to import the
Windows certificate trust store... but that leads to the second problem, which
is that a list of trusted certificates is [not expressive
enough](https://bugs.archlinux.org/task/41909) to express a modern certificate
trust policy.  For example, Windows has the concept of [explicitly distrusted
certificates](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/dn265983(v=ws.11)),
and macOS requires Certificate Transparency validation for some certificates
with complex rules for when it's required.

Option b) (using native library just to verify certs) is probably feasible, but
it would miss aspects of TLS policy other than trusted certs (like allowed
algorithms), and in any case it might well require writing more code, not less,
compared to using the native library for everything.

So I ended up at option c), using the native library for everything.

What I'd *really* prefer would be to use a third-party library that does option
c) for me.  Rust has a good library for this,
[native-tls](https://docs.rs/native-tls/latest/native_tls/).  I did search, but
I couldn't find a good option in the C or C++ ecosystem, at least not any that
wasn't part of some much larger framework.  I was surprised - isn't this a
pretty common use case?  Well, many applications only need TLS for HTTPS, and they can
use libcurl, which has a TLS abstraction layer internally but doesn't expose
it.  Other applications only support a single TLS library, or use one of the
aforementioned larger frameworks, or are platform-specific to begin with, or of
course are written in a non-C/C++ language, most of which have some canonical
choice for TLS.  But there are also many applications that have a set of TLS
backends just like this; it's just that nobody has gone ahead and abstracted
the pattern into a library, at least not a widespread one.

Amusingly, there is one TLS abstraction layer that Yuzu already bundles: the
one in ffmpeg.  But it is missing some features that would be needed to use it
here (like reusing an existing socket rather than managing the socket itself).
Though, that does mean that the wiki's build instructions for Linux (and macOS
for some reason?) already recommend installing OpenSSL, so no need to update
those.

 ## Other APIs implemented

- Sockets:
    - GetSockOpt(`SO_ERROR`)
    - SetSockOpt(`SO_NOSIGPIPE`) (stub, I have no idea what this does on Switch)
    - `DuplicateSocket` (because the SSL sysmodule calls it internally)
    - More `PollEvents` values

- NSD:
    - `Resolve` and `ResolveEx` (stub, good enough for Open Course World and
      probably most third-party servers, but not first-party)

- SFDNSRES:
    - `GetHostByNameRequest` and `GetHostByNameRequestWithOptions`
    - `ResolverSetOptionRequest` (stub)

 ## Fixes

- Parts of the socket code were previously allocating a `sockaddr` object on
  the stack when calling functions that take a `sockaddr*` (e.g. `accept`).
  This might seem like the right thing to do to avoid illegal aliasing, but in
  fact `sockaddr` is not guaranteed to be large enough to hold any particular
  type of address, only the header.  This worked in practice because in
  practice `sockaddr` is the same size as `sockaddr_in`, but it's not how the
  API is meant to be used.  I changed this to allocate an `sockaddr_in` on the
  stack and `reinterpret_cast` it.  I could try to do something cleverer with
  `aligned_storage`, but casting is the idiomatic way to use these particular
  APIs, so it's really the system's responsibility to avoid any aliasing
  issues.

- I rewrote most of the `GetAddrInfoRequest[WithOptions]` implementation.  The
  old implementation invoked the host's getaddrinfo directly from sfdnsres.cpp,
  and directly passed through the host's socket type, protocol, etc. values
  rather than looking up the corresponding constants on the Switch.  To be
  fair, these constants don't tend to actually vary across systems, but
  still... I added a wrapper for `getaddrinfo` in
  `internal_network/network.cpp` similar to the ones for other socket APIs, and
  changed the `GetAddrInfoRequest` implementation to use it.  While I was at
  it, I rewrote the serialization to use the same approach I used to implement
  `GetHostByNameRequest`, because it reduces the number of size calculations.
  While doing so I removed `AF_INET6` support because the Switch doesn't
  support IPv6; it might be nice to support IPv6 anyway, but that would have to
  apply to all of the socket APIs.

  I also corrected the IPC wrappers for `GetAddrInfoRequest` and
  `GetAddrInfoRequestWithOptions` based on reverse engineering and hardware
  testing.  Every call to `GetAddrInfoRequestWithOptions` returns *four*
  different error codes (IPC status, getaddrinfo error code, netdb error code,
  and errno), and `GetAddrInfoRequest` returns three of those but in a
  different order, and it doesn't really matter but the existing implementation
  was a bit off, as I discovered while testing `GetHostByNameRequest`.

  - The new serialization code is based on two simple helper functions:

    ```cpp
    template <typename T> static void Append(std::vector<u8>& vec, T t);
    void AppendNulTerminated(std::vector<u8>& vec, std::string_view str);
    ```

    I was thinking there must be existing functions somewhere that assist with
    serialization/deserialization of binary data, but all I could find was the
    helper methods in `IOFile` and `HLERequestContext`, not anything that could
    be used with a generic byte buffer.  If I'm not missing something, then
    maybe I should move the above functions to a new header in `common`...
    right now they're just sitting in `sfdnsres.cpp` where they're used.

- Not a fix, but `SocketBase::Recv`/`Send` is changed to use `std::span<u8>`
  rather than `std::vector<u8>&` to avoid needing to copy the data to/from a
  vector when those methods are called from the TLS implementation.
2023-06-25 12:53:31 -07:00
Kirill Ignatev b6e89bdf2c Hyphenate Joy-Con and clarify further 2023-06-25 12:51:16 -04:00
Kirill Ignatev b6025cf62f Clarify Ring-Con configuration message in UI
Not obvious how left controller should be set up
Mention that it should be left physical dual emulated
2023-06-25 11:52:15 -04:00
german77 bf641e2964 core: hid: Allow to read bin files while switch controller is available 2023-06-24 18:59:55 -06:00
german77 5aa208e264 input_common: Dont try to read/write data from 3rd party controllers 2023-06-24 18:59:55 -06:00
german77 ec9a71b12a externals: Include player led fix on SDL 2023-06-24 17:43:08 -06:00
german77 474fa13a1a input_common: Make use of new SDL features 2023-06-24 17:42:56 -06:00
zeltermann 482fbded9b Only use SDL wakelock on Linux
SDL has internally fixed shenanigans related to wakelocking through DBus
from inside sandboxes from around August 2022, so we can now remove the
workaround we used since 2021.
2023-06-24 14:51:41 +07:00
lat9nq e5769e9467 nx_tzdb: Update tzdb_to_nx
Includes fixes for other BSD's, and axes shell scripts for pure CMake.
2023-06-23 19:07:26 -04:00
Morph 3a991f3aef Merge pull request #10891 from german77/sdl28v2
externals: Include post release SDL fixes
2023-06-23 17:41:54 -04:00
Narr the Reg 142c1b72f9 externals: Include post release SDL fixes 2023-06-23 12:25:47 -06:00
Abandoned Cart b53945a99f android: define [[maybe_unused]] (const) auto 2023-06-23 14:05:14 -04:00
Abandoned Cart a58a1403ba android: Parameter types from Android Studio
Android Studio marked these parameters as errors because it is an instance, not a class, that is being passed from Java.
2023-06-23 10:36:30 -04:00
liamwhite 5ab4aa1edb Merge pull request #10811 from 8bitDream/pip_mute
android: Add a PiP interface to mute / unmute
2023-06-23 09:27:28 -04:00
liamwhite a674022434 Merge pull request #10859 from liamwhite/no-more-atomic-wait
general: remove atomic signal and wait
2023-06-23 09:27:14 -04:00
liamwhite 87b9b5d10f Merge pull request #10842 from german77/native_mifare
input_common: Implement native mifare/skylander support for joycons/pro controller
2023-06-23 09:27:00 -04:00
liamwhite 575d467d95 Merge pull request #10884 from liamwhite/spaghetti-vfs
vfs_real: lock concurrent accesses
2023-06-23 09:26:48 -04:00
Liam 1dd166f766 vfs_real: lock concurrent accesses 2023-06-23 00:57:24 -04:00
bunnei 2fc5dedf69 Merge pull request #10457 from Kelebek1/optimise
Remove memory allocations in some hot paths
2023-06-22 21:53:07 -07:00
bunnei 3f3e4efb30 Merge pull request #10806 from liamwhite/worst-fs-implementation-ever
vfs_real: misc optimizations
2023-06-22 21:46:50 -07:00
bunnei 5558fc4aa5 Merge pull request #10794 from 8bitDream/multiples
android: Add support for multiple installs
2023-06-22 14:26:24 -07:00
Morph 7f12c6159f Merge pull request #10878 from GPUCode/log-droid
android: Log settings
2023-06-22 16:16:20 -04:00
bunnei 8e9c6429b0 Merge pull request #10873 from german77/sdl
externals: Update sdl to 2.28.0
2023-06-22 10:27:01 -07:00
bunnei dfa0e9479d Merge pull request #10869 from 8bitDream/memory
android: Add a notice when RAM inadequate
2023-06-22 10:26:32 -07:00
GPUCode c133509368 android: Log settings 2023-06-22 20:17:52 +03:00
GPUCode 75fb29e08e vulkan_common: Remove required flags
* Allows VMA to fallback to system RAM instead of crashing
2023-06-22 20:03:12 +03:00
Liam 1586f1c0b1 general: remove atomic signal and wait 2023-06-22 09:25:23 -04:00
Kelebek1 5da70f7197 Remove memory allocations in some hot paths 2023-06-22 08:05:10 +01:00
bunnei e3122c5b46 Merge pull request #10086 from Morph1984/coretiming-ng-1
core_timing: Use CNTPCT as the guest CPU tick
2023-06-21 21:12:46 -07:00
bunnei 7eb7d56b1b Merge pull request #10777 from liamwhite/no-barrier
video_core: optionally skip barriers on feedback loops
2023-06-21 21:10:08 -07:00
bunnei 8cb6b33809 Merge pull request #10841 from liamwhite/math-is-hard
vfs_concat: fix offset calculation when not aligned to file boundary
2023-06-21 21:07:08 -07:00
bunnei 8ad64bc253 Merge pull request #10863 from lat9nq/tz-end-of-string
time_zone_manager: Stop on comma
2023-06-21 21:05:03 -07:00
Narr the Reg 84d43489c5 input_common: Implement native mifare support 2023-06-21 17:54:58 -06:00
Narr the Reg 106b61b1e0 externals: Update sdl to 2.28.0 2023-06-21 17:11:14 -06:00
Abandoned Cart 1a85d8804a android: Generalize string message dialog 2023-06-21 18:25:15 -04:00
Abandoned Cart 6c7e284f64 android: Add support for concurrent installs 2023-06-21 18:25:15 -04:00
Abandoned Cart 8b841aa7ba android: Convert memory sizes to resource 2023-06-21 18:24:49 -04:00
Abandoned Cart 699e78c666 android: Add a notice when RAM inadequate 2023-06-21 18:24:49 -04:00
Abandoned Cart cfc6ef42d9 android: Refactor native and corresponding variables 2023-06-21 18:23:13 -04:00
Abandoned Cart e35371e50c Fix JNI and expose mute settings to Android 2023-06-21 18:23:13 -04:00
Abandoned Cart e31152ee34 android: Add a PiP interface to mute / unmute 2023-06-21 17:21:36 -04:00
liamwhite eea2145698 Merge pull request #10864 from t895/disable-mali-driver
android: Don't show custom driver button on mali and x86
2023-06-21 16:50:30 -04:00
Charles Lombardo e684515578 android: Don't show custom driver button on mali and x86 2023-06-20 20:06:36 -04:00
lat9nq ae1a8a7dc7 time_zone_manager: Add null terminator
We aren't null-terminating this string after the copy, and we need to.
2023-06-20 15:54:28 -04:00
lat9nq fd5d7947f6 time_zone_manager: Stop on comma
This is a deviation from the reference time zone implementation. The
actual code will set a pointer to the time zone name here, but for us we
have a limited number of characters to work with, and the name of the
time zone here could be larger than 8 characters.

We can make the assumption that time zone names greater than five
characters in length include a comma that denotes more data. Nintendo
just truncates that data for the name, so we can do the same.

time_zone_manager: Check for length of array

Just to be double sure that we never break past the array length,
directly compare against it.
2023-06-20 15:54:05 -04:00
bunnei a67bdeb2c2 Merge pull request #10853 from lat9nq/update_tzdb_to_nx
externals: Update tzdb_to_nx
2023-06-20 10:42:54 -07:00
liamwhite f1e12e3b08 Merge pull request #10818 from vonchenplus/render_target_samples
video_core: add samples check when find render target
2023-06-20 09:55:23 -04:00
liamwhite 93061d1ea1 Merge pull request #10835 from lat9nq/intel-restrict-compute-disable
vulkan_device: Restrict compute disable only to affected Intel drivers
2023-06-20 09:55:14 -04:00
liamwhite 6d12e7320b Merge pull request #10840 from Kelebek1/unbug_blinks_brain
Use current GPU address when unmapping GPU pages, not the base
2023-06-20 09:55:01 -04:00
toast2903 78ff2862f6 vulkan_device: Remove brace initializer
Co-authored-by: Tobias <thm.frey@gmail.com>
2023-06-19 17:35:12 -04:00
lat9nq 197e13d93d video_core: Check broken compute earlier
Checks it as the system is determining what settings to enable. Reduces
the need to check settings while the system is running.
2023-06-19 17:33:30 -04:00
Liam e5f1b22e16 vfs_concat: verify short read 2023-06-19 09:47:05 -04:00
Liam b0beca52a3 vfs_concat: fix offset calculation when not aligned to file boundary 2023-06-18 22:21:29 -04:00
Kelebek1 711190bb67 Use current GPU address when unmapping GPU pages, not the base 2023-06-19 00:19:50 +01:00
lat9nq b9a86b040b vk_device_info: Check only affected Intel drivers
Renames is_intel_proprietary to has_broken_compute for accuracy.

vk_device_info: Use vulkan::device to check compute
2023-06-18 16:15:51 -04:00
lat9nq 346c253cd2 video_core: Formalize HasBrokenCompute
Also limits it to only affected Intel proprietrary driver versions.

vulkan_device: Move broken compute determination

vk_device: Remove errant back quote
2023-06-18 16:15:47 -04:00
GPUCode ee0d68300e renderer_vulkan: Add missing initializers 2023-06-18 14:14:03 +03:00
GPUCode 7b2f680468 renderer_vulkan: Use VMA for buffers 2023-06-18 12:45:18 +03:00
GPUCode 48e39756f1 renderer_vulkan: Use VMA for images 2023-06-18 12:45:18 +03:00
GPUCode c60eed36b7 memory_allocator: Remove OpenGL interop
* Appears to be unused atm
2023-06-18 12:45:18 +03:00
lat9nq 6448eade2e externals: Add vma and initialize it
video_core: Move vma implementation to library
2023-06-18 12:45:12 +03:00
FengChen 76a676883a video_core: add samples check when find render target 2023-06-17 23:48:51 +08:00
Liam 94e7cb05da vfs_real: ensure size cache is reset on write 2023-06-16 16:43:14 -04:00
Liam bf47f777b1 patch_manager: remove unnecessary GetSize calls 2023-06-16 16:29:10 -04:00
Liam 734242c5bc vfs_real: misc optimizations 2023-06-16 16:29:06 -04:00
Liam 8d6aefdcc4 video_core: optionally skip barriers on feedback loops 2023-06-14 14:11:46 -04:00
Baptiste Marie 8e3d4e3396 input_common: Redesign mouse panning 2023-06-12 00:47:52 +02:00
Morph 3e6d81a008 nvdisp: Fix SingleCore frametime reporting 2023-06-07 22:04:02 -04:00
Morph 2e1e725443 core_timing: Fix SingleCore cycle timer 2023-06-07 21:44:42 -04:00
Morph 907507886d (wall, native)_clock: Add GetGPUTick
Allows us to directly calculate the GPU tick without double conversion to and from the host clock tick.
2023-06-07 21:44:42 -04:00
Morph 9dcc7bde8b time: Use compile time division for TimeSpanType conversion 2023-06-07 21:44:42 -04:00
Morph 8e56a84566 core_timing: Use CNTPCT as the guest CPU tick
Previously, we were mixing the raw CPU frequency and CNTFRQ.
The raw CPU frequency (1020 MHz) should've never been used as CNTPCT (whose frequency is CNTFRQ) is the only counter available.
2023-06-07 21:44:42 -04:00
Morph bbd502f67a nvnflinger: Acquire lock prior to signaling the vsync variable 2023-06-07 21:44:42 -04:00
Morph 1492a65454 (wall, native)_clock: Rework NativeClock 2023-06-07 21:44:42 -04:00
Morph dd12dd4c67 x64: Deduplicate RDTSC usage 2023-06-07 21:44:42 -04:00
EBADBEEF a84ad180e8 qt: add option to disable controller applet
- add checkbox to disable the controller applet UI
- when controller applet is disabled, use the yuzu-cmd fallback
  controller applet that applies controller config based on rules
- See https://github.com/yuzu-emu/yuzu/issues/8552 for some discussion
2023-01-22 23:36:40 -08:00
320 changed files with 26477 additions and 17506 deletions
+23 -20
View File
@@ -2,35 +2,35 @@
# SPDX-License-Identifier: GPL-2.0-or-later
[submodule "enet"]
path = externals/enet
url = https://github.com/lsalzman/enet.git
path = externals/enet
url = https://github.com/lsalzman/enet.git
[submodule "inih"]
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
[submodule "cubeb"]
path = externals/cubeb
url = https://github.com/mozilla/cubeb.git
path = externals/cubeb
url = https://github.com/mozilla/cubeb.git
[submodule "dynarmic"]
path = externals/dynarmic
url = https://github.com/MerryMage/dynarmic.git
path = externals/dynarmic
url = https://github.com/merryhime/dynarmic.git
[submodule "libusb"]
path = externals/libusb/libusb
url = https://github.com/libusb/libusb.git
[submodule "discord-rpc"]
path = externals/discord-rpc
url = https://github.com/yuzu-emu/discord-rpc.git
path = externals/discord-rpc
url = https://github.com/yuzu-emu/discord-rpc.git
[submodule "Vulkan-Headers"]
path = externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
path = externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "sirit"]
path = externals/sirit
url = https://github.com/yuzu-emu/sirit
path = externals/sirit
url = https://github.com/yuzu-emu/sirit.git
[submodule "mbedtls"]
path = externals/mbedtls
url = https://github.com/yuzu-emu/mbedtls
path = externals/mbedtls
url = https://github.com/yuzu-emu/mbedtls.git
[submodule "xbyak"]
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
[submodule "opus"]
path = externals/opus/opus
url = https://github.com/xiph/opus.git
@@ -45,13 +45,16 @@
url = https://github.com/FFmpeg/FFmpeg.git
[submodule "vcpkg"]
path = externals/vcpkg
url = https://github.com/Microsoft/vcpkg.git
url = https://github.com/microsoft/vcpkg.git
[submodule "cpp-jwt"]
path = externals/cpp-jwt
url = https://github.com/arun11299/cpp-jwt.git
[submodule "libadrenotools"]
path = externals/libadrenotools
url = https://github.com/bylaws/libadrenotools
url = https://github.com/bylaws/libadrenotools.git
[submodule "tzdb_to_nx"]
path = externals/nx_tzdb/tzdb_to_nx
url = https://github.com/lat9nq/tzdb_to_nx.git
[submodule "VulkanMemoryAllocator"]
path = externals/vma/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
+18 -2
View File
@@ -63,6 +63,18 @@ option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
set(DEFAULT_ENABLE_OPENSSL ON)
if (ANDROID OR WIN32 OR APPLE)
# - Windows defaults to the Schannel backend.
# - macOS defaults to the SecureTransport backend.
# - Android currently has no SSL backend as the NDK doesn't include any SSL
# library; a proper 'native' backend would have to go through Java.
# But you can force builds for those platforms to use OpenSSL if you have
# your own copy of it.
set(DEFAULT_ENABLE_OPENSSL OFF)
endif()
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
# On Android, fetch and compile libcxx before doing anything else
if (ANDROID)
set(CMAKE_SKIP_INSTALL_RULES ON)
@@ -322,6 +334,10 @@ if (MINGW)
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
endif()
if(ENABLE_OPENSSL)
find_package(OpenSSL 1.1.1 REQUIRED)
endif()
# Please consider this as a stub
if(ENABLE_QT6 AND Qt6_LOCATION)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}")
@@ -489,7 +505,7 @@ if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.18")
set(SDL2_VER "SDL2-2.28.0")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
endif()
@@ -509,7 +525,7 @@ if (ENABLE_SDL2)
elseif (YUZU_USE_EXTERNAL_SDL2)
message(STATUS "Using SDL2 from externals.")
else()
find_package(SDL2 2.0.18 REQUIRED)
find_package(SDL2 2.26.4 REQUIRED)
endif()
endif()
+721 -587
View File
File diff suppressed because it is too large Load Diff
+714 -580
View File
File diff suppressed because it is too large Load Diff
+709 -575
View File
File diff suppressed because it is too large Load Diff
+815 -657
View File
File diff suppressed because it is too large Load Diff
+714 -580
View File
File diff suppressed because it is too large Load Diff
+713 -579
View File
File diff suppressed because it is too large Load Diff
+712 -577
View File
File diff suppressed because it is too large Load Diff
+738 -602
View File
File diff suppressed because it is too large Load Diff
+733 -599
View File
File diff suppressed because it is too large Load Diff
+743 -609
View File
File diff suppressed because it is too large Load Diff
+747 -610
View File
File diff suppressed because it is too large Load Diff
+711 -577
View File
File diff suppressed because it is too large Load Diff
+715 -581
View File
File diff suppressed because it is too large Load Diff
+751 -617
View File
File diff suppressed because it is too large Load Diff
+738 -604
View File
File diff suppressed because it is too large Load Diff
+739 -605
View File
File diff suppressed because it is too large Load Diff
+724 -590
View File
File diff suppressed because it is too large Load Diff
+709 -575
View File
File diff suppressed because it is too large Load Diff
+711 -577
View File
File diff suppressed because it is too large Load Diff
+720 -586
View File
File diff suppressed because it is too large Load Diff
+728 -594
View File
File diff suppressed because it is too large Load Diff
+728 -594
View File
File diff suppressed because it is too large Load Diff
+711 -577
View File
File diff suppressed because it is too large Load Diff
+868 -734
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -143,6 +143,11 @@ endif()
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)
# VMA
add_library(vma vma/vma.cpp)
target_include_directories(vma PUBLIC ./vma/VulkanMemoryAllocator/include)
target_link_libraries(vma PRIVATE Vulkan::Headers)
if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)
@@ -152,6 +157,9 @@ endif()
add_library(stb stb/stb_dxt.cpp)
target_include_directories(stb PUBLIC ./stb)
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
if (ANDROID)
if (ARCHITECTURE_arm64)
add_subdirectory(libadrenotools)
+1 -1
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <cstdint>
namespace bcn {
/**
* @brief Decodes a BC1 encoded image to R8G8B8A8
*/
void DecodeBc1(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
/**
* @brief Decodes a BC2 encoded image to R8G8B8A8
*/
void DecodeBc2(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
/**
* @brief Decodes a BC3 encoded image to R8G8B8A8
*/
void DecodeBc3(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
/**
* @brief Decodes a BC4 encoded image to R8
*/
void DecodeBc4(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
/**
* @brief Decodes a BC5 encoded image to R8G8
*/
void DecodeBc5(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
/**
* @brief Decodes a BC6 encoded image to R16G16B16A16
*/
void DecodeBc6(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
/**
* @brief Decodes a BC7 encoded image to R8G8B8A8
*/
void DecodeBc7(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
}
+8
View File
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define VMA_IMPLEMENTATION
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#include <vk_mem_alloc.h>
+2 -2
View File
@@ -26,7 +26,7 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn
android {
namespace = "org.yuzu.yuzu_emu"
compileSdkVersion = "android-33"
compileSdkVersion = "android-34"
ndkVersion = "25.2.9519653"
buildFeatures {
@@ -51,7 +51,7 @@ android {
// TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.yuzu.yuzu_emu"
minSdk = 30
targetSdk = 33
targetSdk = 34
versionName = getGitVersion()
// If you want to use autoVersion for the versionCode, create a property in local.properties
+4 -1
View File
@@ -13,6 +13,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
@@ -69,7 +70,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:resource="@xml/nfc_tech_filter" />
</activity>
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/>
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
</service>
<provider
android:name=".features.DocumentProvider"
@@ -286,7 +286,7 @@ object NativeLibrary {
/**
* Unpauses emulation from a paused state.
*/
external fun unPauseEmulation()
external fun unpauseEmulation()
/**
* Pauses emulation.
@@ -313,6 +313,21 @@ object NativeLibrary {
*/
external fun isPaused(): Boolean
/**
* Mutes emulation sound
*/
external fun muteAudio(): Boolean
/**
* Unmutes emulation sound
*/
external fun unmuteAudio(): Boolean
/**
* Returns true if emulation audio is muted.
*/
external fun isMuted(): Boolean
/**
* Returns the performance stats for the current game
*/
@@ -27,13 +27,13 @@ import android.view.MotionEvent
import android.view.Surface
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.fragment.NavHostFragment
import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
@@ -44,8 +44,10 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
@@ -63,6 +65,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private val actionPause = "ACTION_EMULATOR_PAUSE"
private val actionPlay = "ACTION_EMULATOR_PLAY"
private val actionMute = "ACTION_EMULATOR_MUTE"
private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
private val settingsViewModel: SettingsViewModel by viewModels()
@@ -102,6 +106,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
inputHandler = InputHandler()
inputHandler.initialize()
val memoryUtil = MemoryUtil(this)
if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) {
Toast.makeText(
this,
getString(
R.string.device_memory_inadequate,
memoryUtil.getDeviceRAM(),
"8 ${getString(R.string.memory_gigabyte)}"
),
Toast.LENGTH_LONG
).show()
}
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
@@ -305,6 +322,41 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
pictureInPictureActions.add(pauseRemoteAction)
}
if (NativeLibrary.isMuted()) {
val unmuteIcon = Icon.createWithResource(
this@EmulationActivity,
R.drawable.ic_pip_unmute
)
val unmutePendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity,
R.drawable.ic_pip_unmute,
Intent(actionUnmute),
pendingFlags
)
val unmuteRemoteAction = RemoteAction(
unmuteIcon,
getString(R.string.unmute),
getString(R.string.unmute),
unmutePendingIntent
)
pictureInPictureActions.add(unmuteRemoteAction)
} else {
val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute)
val mutePendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity,
R.drawable.ic_pip_mute,
Intent(actionMute),
pendingFlags
)
val muteRemoteAction = RemoteAction(
muteIcon,
getString(R.string.mute),
getString(R.string.mute),
mutePendingIntent
)
pictureInPictureActions.add(muteRemoteAction)
}
return this.apply { setActions(pictureInPictureActions) }
}
@@ -322,10 +374,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var pictureInPictureReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == actionPlay) {
if (NativeLibrary.isPaused()) NativeLibrary.unPauseEmulation()
if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation()
} else if (intent.action == actionPause) {
if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
}
if (intent.action == actionUnmute) {
if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
} else if (intent.action == actionMute) {
if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio()
}
buildPictureInPictureParams()
}
}
@@ -339,6 +396,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
IntentFilter().apply {
addAction(actionPause)
addAction(actionPlay)
addAction(actionMute)
addAction(actionUnmute)
}.also {
registerReceiver(pictureInPictureReceiver, it)
}
@@ -347,6 +406,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
unregisterReceiver(pictureInPictureReceiver)
} catch (ignored: Exception) {
}
// Always resume audio, since there is no UI button
if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
}
}
@@ -714,7 +714,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
State.PAUSED -> {
Log.debug("[EmulationFragment] Resuming emulation.")
NativeLibrary.surfaceChanged(surface)
NativeLibrary.unPauseEmulation()
NativeLibrary.unpauseEmulation()
}
else -> Log.debug("[EmulationFragment] Bug, run called while already running.")
@@ -68,79 +68,109 @@ class HomeSettingsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
val optionsList: MutableList<HomeSetting> = mutableListOf(
HomeSetting(
R.string.advanced_settings,
R.string.settings_description,
R.drawable.ic_settings
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") },
HomeSetting(
R.string.open_user_folder,
R.string.open_user_folder_description,
R.drawable.ic_folder_open
) { openFileManager() },
HomeSetting(
R.string.preferences_theme,
R.string.theme_and_color_description,
R.drawable.ic_palette
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") },
HomeSetting(
R.string.install_gpu_driver,
R.string.install_gpu_driver_description,
R.drawable.ic_exit
) { driverInstaller() },
HomeSetting(
R.string.install_amiibo_keys,
R.string.install_amiibo_keys_description,
R.drawable.ic_nfc
) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) },
HomeSetting(
R.string.install_game_content,
R.string.install_game_content_description,
R.drawable.ic_system_update_alt
) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) },
HomeSetting(
R.string.select_games_folder,
R.string.select_games_folder_description,
R.drawable.ic_add
) {
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
},
HomeSetting(
R.string.manage_save_data,
R.string.import_export_saves_description,
R.drawable.ic_save
) {
ImportExportSavesFragment().show(
parentFragmentManager,
ImportExportSavesFragment.TAG
val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
add(
HomeSetting(
R.string.advanced_settings,
R.string.settings_description,
R.drawable.ic_settings
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
)
add(
HomeSetting(
R.string.open_user_folder,
R.string.open_user_folder_description,
R.drawable.ic_folder_open
) { openFileManager() }
)
add(
HomeSetting(
R.string.preferences_theme,
R.string.theme_and_color_description,
R.drawable.ic_palette
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
)
if (GpuDriverHelper.supportsCustomDriverLoading()) {
add(
HomeSetting(
R.string.install_gpu_driver,
R.string.install_gpu_driver_description,
R.drawable.ic_exit
) { driverInstaller() }
)
},
HomeSetting(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
R.drawable.ic_unlock
) { mainActivity.getProdKey.launch(arrayOf("*/*")) },
HomeSetting(
R.string.install_firmware,
R.string.install_firmware_description,
R.drawable.ic_firmware
) { mainActivity.getFirmware.launch(arrayOf("application/zip")) },
HomeSetting(
R.string.share_log,
R.string.share_log_description,
R.drawable.ic_log
) { shareLog() },
HomeSetting(
R.string.about,
R.string.about_description,
R.drawable.ic_info_outline
) {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
parentFragmentManager.primaryNavigationFragment?.findNavController()
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
}
)
add(
HomeSetting(
R.string.install_amiibo_keys,
R.string.install_amiibo_keys_description,
R.drawable.ic_nfc
) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
)
add(
HomeSetting(
R.string.install_game_content,
R.string.install_game_content_description,
R.drawable.ic_system_update_alt
) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
)
add(
HomeSetting(
R.string.select_games_folder,
R.string.select_games_folder_description,
R.drawable.ic_add
) {
mainActivity.getGamesDirectory.launch(
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
)
}
)
add(
HomeSetting(
R.string.manage_save_data,
R.string.import_export_saves_description,
R.drawable.ic_save
) {
ImportExportSavesFragment().show(
parentFragmentManager,
ImportExportSavesFragment.TAG
)
}
)
add(
HomeSetting(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
R.drawable.ic_unlock
) { mainActivity.getProdKey.launch(arrayOf("*/*")) }
)
add(
HomeSetting(
R.string.install_firmware,
R.string.install_firmware_description,
R.drawable.ic_firmware
) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
)
add(
HomeSetting(
R.string.share_log,
R.string.share_log_description,
R.drawable.ic_log
) { shareLog() }
)
add(
HomeSetting(
R.string.about,
R.string.about_description,
R.drawable.ic_info_outline
) {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
parentFragmentManager.primaryNavigationFragment?.findNavController()
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
}
)
}
if (!BuildConfig.PREMIUM) {
optionsList.add(
@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
class LongMessageDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE)
val description = requireArguments().getString(DESCRIPTION)
val helpLinkId = requireArguments().getInt(HELP_LINK)
val dialog = MaterialAlertDialogBuilder(requireContext())
.setPositiveButton(R.string.close, null)
.setTitle(titleId)
.setMessage(description)
if (helpLinkId != 0) {
dialog.setNeutralButton(R.string.learn_more) { _, _ ->
openLink(getString(helpLinkId))
}
}
return dialog.show()
}
private fun openLink(link: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
startActivity(intent)
}
companion object {
const val TAG = "LongMessageDialogFragment"
private const val TITLE = "Title"
private const val DESCRIPTION = "Description"
private const val HELP_LINK = "Link"
fun newInstance(
titleId: Int,
description: String,
helpLinkId: Int = 0
): LongMessageDialogFragment {
val dialog = LongMessageDialogFragment()
val bundle = Bundle()
bundle.apply {
putInt(TITLE, titleId)
putString(DESCRIPTION, description)
putInt(HELP_LINK, helpLinkId)
}
dialog.arguments = bundle
return dialog
}
}
}
@@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@@ -128,10 +127,7 @@ class SearchFragment : Fragment() {
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
R.id.chip_retail -> baseList.filter {
FileUtil.hasExtension(it.path, "xci") ||
FileUtil.hasExtension(it.path, "nsp")
}
R.id.chip_retail -> baseList.filter { !it.isHomebrew }
else -> baseList
}
@@ -43,7 +43,7 @@ class Game(
companion object {
val extensions: Set<String> = HashSet(
listOf(".xci", ".nsp", ".nca", ".nro")
listOf("xci", "nsp", "nca", "nro")
)
}
}
@@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
@@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -294,7 +296,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return@registerForActivityResult
}
if (!FileUtil.hasExtension(result, "keys")) {
if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_prod_keys_failure_extension_description
@@ -391,7 +393,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return@registerForActivityResult
}
if (!FileUtil.hasExtension(result, "bin")) {
if (FileUtil.getExtension(result) != "bin") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_amiibo_keys_failure_extension_description
@@ -481,62 +483,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
val installGameUpdate =
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
if (it == null) {
return@registerForActivityResult
}
val installGameUpdate = registerForActivityResult(
ActivityResultContracts.OpenMultipleDocuments()
) { documents: List<Uri> ->
if (documents.isNotEmpty()) {
IndeterminateProgressDialogFragment.newInstance(
this@MainActivity,
R.string.install_game_content
) {
val result = NativeLibrary.installFileToNand(it.toString())
var installSuccess = 0
var installOverwrite = 0
var errorBaseGame = 0
var errorExtension = 0
var errorOther = 0
var errorTotal = 0
lifecycleScope.launch {
withContext(Dispatchers.Main) {
when (result) {
documents.forEach {
when (NativeLibrary.installFileToNand(it.toString())) {
NativeLibrary.InstallFileToNandResult.Success -> {
Toast.makeText(
applicationContext,
R.string.install_game_content_success,
Toast.LENGTH_SHORT
).show()
installSuccess += 1
}
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
Toast.makeText(
applicationContext,
R.string.install_game_content_success_overwrite,
Toast.LENGTH_SHORT
).show()
installOverwrite += 1
}
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
MessageDialogFragment.newInstance(
R.string.install_game_content_failure,
R.string.install_game_content_failure_base
).show(supportFragmentManager, MessageDialogFragment.TAG)
errorBaseGame += 1
}
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
MessageDialogFragment.newInstance(
R.string.install_game_content_failure,
R.string.install_game_content_failure_file_extension,
R.string.install_game_content_help_link
).show(supportFragmentManager, MessageDialogFragment.TAG)
errorExtension += 1
}
else -> {
MessageDialogFragment.newInstance(
R.string.install_game_content_failure,
R.string.install_game_content_failure_description,
R.string.install_game_content_help_link
).show(supportFragmentManager, MessageDialogFragment.TAG)
errorOther += 1
}
}
}
withContext(Dispatchers.Main) {
val separator = System.getProperty("line.separator") ?: "\n"
val installResult = StringBuilder()
if (installSuccess > 0) {
installResult.append(
getString(
R.string.install_game_content_success_install,
installSuccess
)
)
installResult.append(separator)
}
if (installOverwrite > 0) {
installResult.append(
getString(
R.string.install_game_content_success_overwrite,
installOverwrite
)
)
installResult.append(separator)
}
errorTotal = errorBaseGame + errorExtension + errorOther
if (errorTotal > 0) {
installResult.append(separator)
installResult.append(
getString(
R.string.install_game_content_failed_count,
errorTotal
)
)
installResult.append(separator)
if (errorBaseGame > 0) {
installResult.append(separator)
installResult.append(
getString(R.string.install_game_content_failure_base)
)
installResult.append(separator)
}
if (errorExtension > 0) {
installResult.append(separator)
installResult.append(
getString(R.string.install_game_content_failure_file_extension)
)
installResult.append(separator)
}
if (errorOther > 0) {
installResult.append(
getString(R.string.install_game_content_failure_description)
)
installResult.append(separator)
}
LongMessageDialogFragment.newInstance(
R.string.install_game_content_failure,
installResult.toString().trim(),
R.string.install_game_content_help_link
).show(supportFragmentManager, LongMessageDialogFragment.TAG)
} else {
LongMessageDialogFragment.newInstance(
R.string.install_game_content_success,
installResult.toString().trim()
).show(supportFragmentManager, LongMessageDialogFragment.TAG)
}
}
}
return@newInstance result
return@newInstance installSuccess + installOverwrite + errorTotal
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
}
}
@@ -7,7 +7,6 @@ import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import androidx.documentfile.provider.DocumentFile
import java.io.BufferedInputStream
import java.io.File
@@ -185,19 +184,18 @@ object FileUtil {
/**
* Get file display name from given path
* @param path content uri path
* @param uri content uri
* @return String display name
*/
fun getFilename(context: Context, path: String): String {
val resolver = context.contentResolver
fun getFilename(uri: Uri): String {
val resolver = YuzuApplication.appContext.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_DISPLAY_NAME
)
var filename = ""
var c: Cursor? = null
try {
val mUri = Uri.parse(path)
c = resolver.query(mUri, columns, null, null, null)
c = resolver.query(uri, columns, null, null, null)
c!!.moveToNext()
filename = c.getString(0)
} catch (e: Exception) {
@@ -326,25 +324,9 @@ object FileUtil {
}
}
fun hasExtension(path: String, extension: String): Boolean =
path.substring(path.lastIndexOf(".") + 1).contains(extension)
fun hasExtension(uri: Uri, extension: String): Boolean {
val fileName: String?
val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null)
val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor?.moveToFirst()
if (nameIndex == null) {
return false
}
fileName = cursor.getString(nameIndex)
cursor.close()
if (fileName == null) {
return false
}
return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension)
fun getExtension(uri: Uri): String {
val fileName = getFilename(uri)
return fileName.substring(fileName.lastIndexOf(".") + 1)
.lowercase()
}
}
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceManager
import java.util.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
@@ -33,15 +32,9 @@ object GameHelper {
val children = FileUtil.listFiles(context, gamesUri)
for (file in children) {
if (!file.isDirectory) {
val filename = file.uri.toString()
val extensionStart = filename.lastIndexOf('.')
if (extensionStart > 0) {
val fileExtension = filename.substring(extensionStart)
// Check that the file has an extension we care about before trying to read out of it.
if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) {
games.add(getGame(filename))
}
// Check that the file has an extension we care about before trying to read out of it.
if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
games.add(getGame(file.uri))
}
}
}
@@ -59,21 +52,19 @@ object GameHelper {
return games.toList()
}
private fun getGame(filePath: String): Game {
private fun getGame(uri: Uri): Game {
val filePath = uri.toString()
var name = NativeLibrary.getTitle(filePath)
// If the game's title field is empty, use the filename.
if (name.isEmpty()) {
name = filePath.substring(filePath.lastIndexOf("/") + 1)
name = FileUtil.getFilename(uri)
}
var gameId = NativeLibrary.getGameId(filePath)
// If the game's ID field is empty, use the filename without extension.
if (gameId.isEmpty()) {
gameId = filePath.substring(
filePath.lastIndexOf("/") + 1,
filePath.lastIndexOf(".")
)
gameId = name.substring(0, name.lastIndexOf("."))
}
val newGame = Game(
@@ -113,6 +113,8 @@ object GpuDriverHelper {
initializeDriverParameters(context)
}
external fun supportsCustomDriverLoading(): Boolean
// Parse the custom driver metadata to retrieve the name.
val customDriverName: String?
get() {
@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.app.ActivityManager
import android.content.Context
import org.yuzu.yuzu_emu.R
import java.util.Locale
class MemoryUtil(val context: Context) {
private val Long.floatForm: String
get() = String.format(Locale.ROOT, "%.2f", this.toDouble())
private fun bytesToSizeUnit(size: Long): String {
return when {
size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}"
size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}"
size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}"
size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}"
size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}"
size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}"
else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}"
}
}
private val totalMemory =
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
val memInfo = ActivityManager.MemoryInfo()
getMemoryInfo(memInfo)
memInfo.totalMem
}
fun isLessThan(minimum: Int, size: Long): Boolean {
return when (size) {
Kb -> totalMemory < Mb && totalMemory < minimum
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
Eb -> totalMemory / Eb < minimum
else -> totalMemory < Kb && totalMemory < minimum
}
}
fun getDeviceRAM(): String {
return bytesToSizeUnit(totalMemory)
}
companion object {
const val Kb: Long = 1024
const val Mb = Kb * 1024
const val Gb = Mb * 1024
const val Tb = Gb * 1024
const val Pb = Tb * 1024
const val Eb = Pb * 1024
}
}
@@ -14,7 +14,6 @@ add_library(yuzu-android SHARED
id_cache.cpp
id_cache.h
native.cpp
native.h
)
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
+124 -122
View File
@@ -14,6 +14,7 @@
#include <android/api-level.h>
#include <android/native_window_jni.h>
#include <core/loader/nro.h>
#include <jni.h>
#include "common/detached_tasks.h"
#include "common/dynamic_library.h"
@@ -59,6 +60,9 @@
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#define jconst [[maybe_unused]] const auto
#define jauto [[maybe_unused]] auto
namespace {
class EmulationSession final {
@@ -98,8 +102,8 @@ public:
}
int InstallFileToNand(std::string filename) {
const auto copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
std::size_t block_size) {
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
std::size_t block_size) {
if (src == nullptr || dest == nullptr) {
return false;
}
@@ -108,10 +112,10 @@ public:
}
using namespace Common::Literals;
std::vector<u8> buffer(1_MiB);
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
const auto read = src->Read(buffer.data(), buffer.size(), i);
jconst read = src->Read(buffer.data(), buffer.size(), i);
dest->Write(buffer.data(), read, i);
}
return true;
@@ -128,14 +132,14 @@ public:
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
std::shared_ptr<FileSys::NSP> nsp;
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
if (filename.ends_with("nsp")) {
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
if (nsp->IsExtractedType()) {
return InstallError;
}
} else if (filename.ends_with("xci")) {
const auto xci =
jconst xci =
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
nsp = xci->GetSecurePartitionNSP();
} else {
@@ -150,7 +154,7 @@ public:
return InstallError;
}
const auto res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
*nsp, true, copy_func);
switch (res) {
@@ -233,10 +237,11 @@ public:
m_system.SetFilesystem(m_vfs);
// Initialize system.
auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
m_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false);
m_system.ApplySettings();
Settings::LogSettings();
m_system.HIDCore().ReloadInputDevices();
m_system.SetAppletFrontendSet({
nullptr, // Amiibo Settings
@@ -330,7 +335,7 @@ public:
while (true) {
{
std::unique_lock lock(m_mutex);
[[maybe_unused]] std::unique_lock lock(m_mutex);
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
[&]() { return !m_is_running; })) {
// Emulation halted.
@@ -362,7 +367,7 @@ public:
}
bool IsHandheldOnly() {
const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
if (npad_style_set.fullkey == 1) {
return false;
@@ -375,17 +380,17 @@ public:
return !Settings::values.use_docked_mode.GetValue();
}
void SetDeviceType(int index, int type) {
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
void SetDeviceType([[maybe_unused]] int index, int type) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
}
void OnGamepadConnectEvent(int index) {
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
void OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
// Ensure that player1 is configured correctly and handheld disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
auto handheld =
jauto handheld =
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
@@ -397,7 +402,8 @@ public:
// Ensure that handheld is configured correctly and player 1 disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
jauto player1 =
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
@@ -411,8 +417,8 @@ public:
}
}
void OnGamepadDisconnectEvent(int index) {
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->Disconnect();
}
@@ -428,7 +434,7 @@ private:
};
RomMetadata GetRomMetadata(const std::string& path) {
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
return search->second;
}
@@ -436,14 +442,14 @@ private:
}
RomMetadata CacheRomMetadata(const std::string& path) {
const auto file = Core::GetGameFileFromPath(m_vfs, path);
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
jconst file = Core::GetGameFileFromPath(m_vfs, path);
jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadIcon(entry.icon);
if (loader->GetFileType() == Loader::FileType::NRO) {
auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
@@ -514,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath);
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
if (result != Core::SystemResultStatus::Success) {
return result;
}
@@ -526,83 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
extern "C" {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jobject surf) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
[[maybe_unused]] jobject surf) {
EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
EmulationSession::GetInstance().SurfaceChanged();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) {
ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
EmulationSession::GetInstance().SetNativeWindow(nullptr);
EmulationSession::GetInstance().SurfaceChanged();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring j_directory) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
[[maybe_unused]] jstring j_directory) {
Common::FS::SetAppDirectory(GetJString(env, j_directory));
}
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring j_file) {
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
[[maybe_unused]] jstring j_file) {
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
}
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(
JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
jstring custom_driver_name, jstring file_redirect_dir) {
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
jstring hook_lib_dir,
jstring custom_driver_dir,
jstring custom_driver_name,
jstring file_redirect_dir) {
EmulationSession::GetInstance().InitializeGpuDriver(
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env,
[[maybe_unused]] jclass clazz) {
[[maybe_unused]] static bool CheckKgslPresent() {
constexpr auto KgslPath{"/dev/kgsl-3d0"};
return access(KgslPath, F_OK) == 0;
}
[[maybe_unused]] bool SupportsCustomDriver() {
return android_get_device_api_level() >= 28 && CheckKgslPresent();
}
jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
JNIEnv* env, jobject instance) {
#ifdef ARCHITECTURE_arm64
// If the KGSL device exists custom drivers can be loaded using adrenotools
return SupportsCustomDriver();
#else
return false;
#endif
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
Core::Crypto::KeyManager::Instance().ReloadKeys();
return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().UnPauseEmulation();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().PauseEmulation();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().HaltEmulation();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().ResetRomMetadata();
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
Settings::values.audio_muted = true;
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
Settings::values.audio_muted = false;
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
return EmulationSession::GetInstance().IsHandheldOnly();
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
jint j_device, jint j_type) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
@@ -610,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
jint j_device) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -619,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
jint j_device) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
}
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jint j_device,
jint j_button, jint action) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
jint j_device, jint j_button,
jint action) {
if (EmulationSession::GetInstance().IsRunning()) {
// Ensure gamepad is connected
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -639,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
jint j_device, jint stick_id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
@@ -650,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device,
jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x,
jfloat accel_y, jfloat accel_z) {
JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
@@ -660,8 +683,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
jbyteArray j_data) {
jboolean isCopy{false};
std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
@@ -673,108 +695,92 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNI
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnRemoveNfcTag();
}
return static_cast<jboolean>(true);
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz, jint id,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz, jint id,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz, jint id) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchReleased(id);
}
}
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
jstring j_filename) {
jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data()));
return icon;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
jstring j_filename) {
jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
return env->NewStringUTF(title.c_str());
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
jstring j_filename) {
return env->NewStringUTF("");
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
jstring j_filename) {
return env->NewStringUTF("");
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
jstring j_filename) {
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
// Create the default config.ini.
Config{};
// Initialize the emulated system.
EmulationSession::GetInstance().System().Initialize();
}
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
return {};
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file,
[[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
Config{};
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
jstring j_game_id, jstring j_section,
jstring j_key) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -788,8 +794,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JN
return env->NewStringUTF("");
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
jstring j_game_id, jstring j_section,
jstring j_key, jstring j_value) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -803,20 +808,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEn
env->ReleaseStringUTFChars(j_value, value.data());
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
env->ReleaseStringUTFChars(j_game_id, game_id.data());
}
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
jdoubleArray j_stats = env->NewDoubleArray(4);
if (EmulationSession::GetInstance().IsRunning()) {
const auto results = EmulationSession::GetInstance().PerfStats();
jconst results = EmulationSession::GetInstance().PerfStats();
// Converting the structure into an array makes it easier to pass it to the frontend
double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
@@ -828,11 +831,11 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]]
return j_stats;
}
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {}
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
jclass clazz,
jstring j_path) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
jstring j_path) {
const std::string path = GetJString(env, j_path);
@@ -843,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) {
LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc);
LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
}
-165
View File
@@ -1,165 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
// Function calls from the Java side
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env,
jclass clazz,
jstring j_device,
jstring j_type);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
JNIEnv* env, jclass clazz, jstring j_device);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
JNIEnv* env, jclass clazz, jstring j_device);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent(
JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env,
jclass clazz,
jbyteArray j_data);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
jclass clazz,
jfloat x, jfloat y,
jboolean pressed);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz,
jfloat x, jfloat y);
JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env,
jclass clazz,
jstring j_file);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env,
jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env,
jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env,
jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env,
jclass clazz,
jstring j_directory);
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver(
JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
jstring custom_driver_name, jstring file_redirect_dir);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory(
JNIEnv* env, jclass clazz, jstring path_);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env,
jclass clazz,
jstring path);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz,
jboolean enable);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(
JNIEnv* env, jclass clazz, jint layout_option, jint rotation);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2(
JNIEnv* env, jclass clazz, jstring j_path);
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
jclass clazz,
jobject surf);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting(
JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key,
jstring j_value);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting(
JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText(
JNIEnv* env, jclass clazz, jstring j_text);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput(
JNIEnv* env, jclass clazz, jint j_key_code);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@android:color/white"
android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
</vector>
@@ -104,12 +104,14 @@
<string name="share_log_missing">No log file found</string>
<string name="install_game_content">Install game content</string>
<string name="install_game_content_description">Install game updates or DLC</string>
<string name="install_game_content_failure">Error installing file to NAND</string>
<string name="install_game_content_failure_description">Game content installation failed. Please ensure content is valid and that the prod.keys file is installed.</string>
<string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts. Please select an update or DLC instead.</string>
<string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string>
<string name="install_game_content_success">Game content installed successfully</string>
<string name="install_game_content_success_overwrite">Game content was overwritten successfully</string>
<string name="install_game_content_failure">Error installing file(s) to NAND</string>
<string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string>
<string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string>
<string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string>
<string name="install_game_content_failed_count">%1$d installation error(s)</string>
<string name="install_game_content_success">Game content(s) installed successfully</string>
<string name="install_game_content_success_install">%1$d installed successfully</string>
<string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
<!-- About screen strings -->
@@ -270,6 +272,7 @@
<string name="fatal_error">Fatal Error</string>
<string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
<string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
<string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>
<!-- Region Names -->
<string name="region_japan">Japan</string>
@@ -300,6 +303,15 @@
<string name="language_traditional_chinese">Traditional Chinese (正體中文)</string>
<string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string>
<!-- Memory Sizes -->
<string name="memory_byte">Byte</string>
<string name="memory_kilobyte">KB</string>
<string name="memory_megabyte">MB</string>
<string name="memory_gigabyte">GB</string>
<string name="memory_terabyte">TB</string>
<string name="memory_petabyte">PB</string>
<string name="memory_exabyte">EB</string>
<!-- Renderer APIs -->
<string name="renderer_vulkan">Vulkan</string>
<string name="renderer_none">None</string>
@@ -387,6 +399,8 @@
<string name="picture_in_picture_description">Minimize window when placed in the background</string>
<string name="pause">Pause</string>
<string name="play">Play</string>
<string name="mute">Mute</string>
<string name="unmute">Unmute</string>
<!-- Licenses screen strings -->
<string name="licenses">Licenses</string>
+3
View File
@@ -15,3 +15,6 @@ android.useAndroidX=true
kotlin.code.style=official
kotlin.parallel.tasks.in.project=true
android.defaults.buildfeatures.buildconfig=true
# Android Gradle plugin 8.0.2
android.suppressUnsupportedCompileSdk=34
+5 -3
View File
@@ -7,6 +7,7 @@
#include <mutex>
#include <span>
#include <vector>
#include <boost/container/static_vector.hpp>
#include "audio_buffer.h"
#include "audio_core/device/device_session.h"
@@ -48,7 +49,7 @@ public:
*
* @param out_buffers - The buffers which were registered.
*/
void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) {
void RegisterBuffers(boost::container::static_vector<AudioBuffer, N>& out_buffers) {
std::scoped_lock l{lock};
const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit),
BufferAppendLimit - registered_count)};
@@ -162,7 +163,8 @@ public:
* @param max_buffers - Maximum number of buffers to released.
* @return The number of buffers released.
*/
u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) {
u32 GetRegisteredAppendedBuffers(
boost::container::static_vector<AudioBuffer, N>& buffers_flushed, u32 max_buffers) {
std::scoped_lock l{lock};
if (registered_count + appended_count == 0) {
return 0;
@@ -270,7 +272,7 @@ public:
*/
bool FlushBuffers(u32& buffers_released) {
std::scoped_lock l{lock};
std::vector<AudioBuffer> buffers_flushed{};
boost::container::static_vector<AudioBuffer, N> buffers_flushed{};
buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit);
+6 -6
View File
@@ -79,7 +79,7 @@ void DeviceSession::ClearBuffers() {
}
}
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
for (const auto& buffer : buffers) {
Sink::SinkBuffer new_buffer{
.frames = buffer.size / (channel_count * sizeof(s16)),
@@ -88,13 +88,13 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
.consumed = false,
};
tmp_samples.resize_destructive(buffer.size / sizeof(s16));
if (type == Sink::StreamType::In) {
std::vector<s16> samples{};
stream->AppendBuffer(new_buffer, samples);
stream->AppendBuffer(new_buffer, tmp_samples);
} else {
std::vector<s16> samples(buffer.size / sizeof(s16));
system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size);
stream->AppendBuffer(new_buffer, samples);
system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(),
buffer.size);
stream->AppendBuffer(new_buffer, tmp_samples);
}
}
}
+4 -3
View File
@@ -10,6 +10,7 @@
#include "audio_core/common/common.h"
#include "audio_core/sink/sink.h"
#include "common/scratch_buffer.h"
#include "core/hle/service/audio/errors.h"
namespace Core {
@@ -62,7 +63,7 @@ public:
*
* @param buffers - The buffers to play.
*/
void AppendBuffers(std::span<const AudioBuffer> buffers) const;
void AppendBuffers(std::span<const AudioBuffer> buffers);
/**
* (Audio In only) Pop samples from the backend, and write them back to this buffer's address.
@@ -146,8 +147,8 @@ private:
std::shared_ptr<Core::Timing::EventType> thread_event;
/// Is this session initialised?
bool initialized{};
/// Buffer queue
std::vector<AudioBuffer> buffer_queue{};
/// Temporary sample buffer
Common::ScratchBuffer<s16> tmp_samples{};
};
} // namespace AudioCore
+3 -2
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "audio_core/audio_event.h"
#include "audio_core/audio_manager.h"
#include "audio_core/in/audio_in_system.h"
@@ -89,7 +90,7 @@ Result System::Start() {
session->Start();
state = State::Started;
std::vector<AudioBuffer> buffers_to_flush{};
boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
buffers.RegisterBuffers(buffers_to_flush);
session->AppendBuffers(buffers_to_flush);
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
@@ -134,7 +135,7 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) {
void System::RegisterBuffers() {
if (state == State::Started) {
std::vector<AudioBuffer> registered_buffers{};
boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
buffers.RegisterBuffers(registered_buffers);
session->AppendBuffers(registered_buffers);
}
+2 -2
View File
@@ -89,7 +89,7 @@ Result System::Start() {
session->Start();
state = State::Started;
std::vector<AudioBuffer> buffers_to_flush{};
boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
buffers.RegisterBuffers(buffers_to_flush);
session->AppendBuffers(buffers_to_flush);
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
@@ -134,7 +134,7 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
void System::RegisterBuffers() {
if (state == State::Started) {
std::vector<AudioBuffer> registered_buffers{};
boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
buffers.RegisterBuffers(registered_buffers);
session->AppendBuffers(registered_buffers);
}
-1
View File
@@ -7,7 +7,6 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/memory.h"
namespace AudioCore::AudioRenderer::ADSP {
@@ -13,7 +13,6 @@
#include "common/thread.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
@@ -144,6 +143,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK);
// 0.12 seconds (2304000 / 19200000)
constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) {
@@ -184,8 +184,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
u64 max_time{max_process_time};
if (index == 1 && command_buffer.applet_resource_user_id ==
mailbox->GetCommandBuffer(0).applet_resource_user_id) {
max_time = max_process_time -
Core::Timing::CyclesToNs(render_times_taken[0]).count();
max_time = max_process_time - render_times_taken[0];
if (render_times_taken[0] > max_process_time) {
max_time = 0;
}
@@ -9,7 +9,6 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/memory.h"
namespace AudioCore::AudioRenderer::ADSP {
@@ -8,6 +8,7 @@
#include "audio_core/renderer/command/resample/resample.h"
#include "common/fixed_point.h"
#include "common/logging/log.h"
#include "common/scratch_buffer.h"
#include "core/memory.h"
namespace AudioCore::AudioRenderer {
@@ -27,6 +28,7 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
template <typename T>
static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
const DecodeArg& req) {
std::array<T, TempBufferSize> tmp_samples{};
constexpr s32 min{std::numeric_limits<s16>::min()};
constexpr s32 max{std::numeric_limits<s16>::max()};
@@ -49,18 +51,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
const u64 size{channel_count * samples_to_decode};
const u64 size_bytes{size * sizeof(T)};
std::vector<T> samples(size);
memory.ReadBlockUnsafe(source, samples.data(), size_bytes);
memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes);
if constexpr (std::is_floating_point_v<T>) {
for (u32 i = 0; i < samples_to_decode; i++) {
auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
std::numeric_limits<s16>::max())};
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
}
} else {
for (u32 i = 0; i < samples_to_decode; i++) {
out_buffer[i] = samples[i * channel_count + req.target_channel];
out_buffer[i] = tmp_samples[i * channel_count + req.target_channel];
}
}
} break;
@@ -73,17 +74,16 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
}
const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
std::vector<T> samples(samples_to_decode);
memory.ReadBlockUnsafe(source, samples.data(), samples_to_decode * sizeof(T));
memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T));
if constexpr (std::is_floating_point_v<T>) {
for (u32 i = 0; i < samples_to_decode; i++) {
auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
std::numeric_limits<s16>::max())};
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
}
} else {
std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16));
std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16));
}
break;
}
@@ -101,6 +101,7 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
*/
static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
const DecodeArg& req) {
std::array<u8, TempBufferSize> wavebuffer{};
constexpr u32 SamplesPerFrame{14};
constexpr u32 NibblesPerFrame{16};
@@ -138,9 +139,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
}
const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
std::vector<u8> wavebuffer(size);
memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(),
wavebuffer.size());
memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size);
auto context{req.adpcm_context};
auto header{context->header};
@@ -258,7 +257,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
u32 offset{voice_state.offset};
auto output_buffer{args.output};
std::vector<s16> temp_buffer(TempBufferSize, 0);
std::array<s16, TempBufferSize> temp_buffer{};
while (remaining_sample_count > 0) {
const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)};
@@ -44,8 +44,8 @@ static void InitializeCompressorEffect(const CompressorInfo::ParameterVersion2&
static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params,
CompressorInfo::State& state, bool enabled,
std::vector<std::span<const s32>> input_buffers,
std::vector<std::span<s32>> output_buffers, u32 sample_count) {
std::span<std::span<const s32>> input_buffers,
std::span<std::span<s32>> output_buffers, u32 sample_count) {
if (enabled) {
auto state_00{state.unk_00};
auto state_04{state.unk_04};
@@ -124,8 +124,8 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
}
void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
std::array<std::span<const s32>, MaxChannels> input_buffers{};
std::array<std::span<s32>, MaxChannels> output_buffers{};
for (s16 i = 0; i < parameter.channel_count; i++) {
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -51,7 +51,7 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor();
state.delay_lines[channel].sample_count = sample_count.to_int_floor();
state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0);
if (state.delay_lines[channel].buffer.size() == 0) {
if (state.delay_lines[channel].sample_count == 0) {
state.delay_lines[channel].buffer.push_back(0);
}
state.delay_lines[channel].buffer_pos = 0;
@@ -74,8 +74,8 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
*/
template <size_t NumChannels>
static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
std::vector<std::span<const s32>>& inputs,
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
std::span<std::span<const s32>> inputs, std::span<std::span<s32>> outputs,
const u32 sample_count) {
for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};
for (u32 channel = 0; channel < NumChannels; channel++) {
@@ -153,8 +153,8 @@ static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::St
* @param sample_count - Number of samples to process.
*/
static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
const bool enabled, std::vector<std::span<const s32>>& inputs,
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
const bool enabled, std::span<std::span<const s32>> inputs,
std::span<std::span<s32>> outputs, const u32 sample_count) {
if (!IsChannelCountValid(params.channel_count)) {
LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count);
@@ -208,8 +208,8 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce
}
void DelayCommand::Process(const ADSP::CommandListProcessor& processor) {
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
std::array<std::span<const s32>, MaxChannels> input_buffers{};
std::array<std::span<s32>, MaxChannels> output_buffers{};
for (s16 i = 0; i < parameter.channel_count; i++) {
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -408,8 +408,8 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
}
void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
std::array<std::span<const s32>, MaxChannels> input_buffers{};
std::array<std::span<s32>, MaxChannels> output_buffers{};
for (u32 i = 0; i < parameter.channel_count; i++) {
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -47,8 +47,8 @@ static void InitializeLightLimiterEffect(const LightLimiterInfo::ParameterVersio
*/
static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,
LightLimiterInfo::State& state, const bool enabled,
std::vector<std::span<const s32>>& inputs,
std::vector<std::span<s32>>& outputs, const u32 sample_count,
std::span<std::span<const s32>> inputs,
std::span<std::span<s32>> outputs, const u32 sample_count,
LightLimiterInfo::StatisticsInternal* statistics) {
constexpr s64 min{std::numeric_limits<s32>::min()};
constexpr s64 max{std::numeric_limits<s32>::max()};
@@ -147,8 +147,8 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP
}
void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
std::array<std::span<const s32>, MaxChannels> input_buffers{};
std::array<std::span<s32>, MaxChannels> output_buffers{};
for (u32 i = 0; i < parameter.channel_count; i++) {
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -190,8 +190,8 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP
}
void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
std::array<std::span<const s32>, MaxChannels> input_buffers{};
std::array<std::span<s32>, MaxChannels> output_buffers{};
for (u32 i = 0; i < parameter.channel_count; i++) {
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -250,8 +250,8 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine&
*/
template <size_t NumChannels>
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
std::vector<std::span<const s32>>& inputs,
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
std::span<std::span<const s32>> inputs,
std::span<std::span<s32>> outputs, const u32 sample_count) {
static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
@@ -369,8 +369,8 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever
* @param sample_count - Number of samples to process.
*/
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
const bool enabled, std::vector<std::span<const s32>>& inputs,
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
const bool enabled, std::span<std::span<const s32>> inputs,
std::span<std::span<s32>> outputs, const u32 sample_count) {
if (enabled) {
switch (params.channel_count) {
case 0:
@@ -412,8 +412,8 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc
}
void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
std::array<std::span<const s32>, MaxChannels> input_buffers{};
std::array<std::span<s32>, MaxChannels> output_buffers{};
for (u32 i = 0; i < parameter.channel_count; i++) {
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -5,7 +5,6 @@
#include "audio_core/renderer/command/performance/performance.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
namespace AudioCore::AudioRenderer {
@@ -18,20 +17,18 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) {
auto base{entry_address.translated_address};
if (state == PerformanceState::Start) {
auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)};
*start_time_ptr = static_cast<u32>(
Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() -
processor.start_time - processor.current_processing_time)
.count());
*start_time_ptr =
static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
processor.current_processing_time);
} else if (state == PerformanceState::Stop) {
auto processed_time_ptr{
reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)};
auto entry_count_ptr{
reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)};
*processed_time_ptr = static_cast<u32>(
Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() -
processor.start_time - processor.current_processing_time)
.count());
*processed_time_ptr =
static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
processor.current_processing_time);
(*entry_count_ptr)++;
}
}
@@ -24,7 +24,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
constexpr s32 min{std::numeric_limits<s16>::min()};
constexpr s32 max{std::numeric_limits<s16>::max()};
std::vector<s16> output(processor.sample_count);
std::array<s16, TargetSampleCount * MaxChannels> output{};
for (u32 channel = 0; channel < input_count; channel++) {
auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count,
processor.sample_count)};
@@ -33,7 +33,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
}
processor.memory->WriteBlockUnsafe(address + pos, output.data(),
output.size() * sizeof(s16));
processor.sample_count * sizeof(s16));
pos += static_cast<u32>(processor.sample_count * sizeof(s16));
if (pos >= size) {
pos = 0;
@@ -33,8 +33,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
.consumed{false},
};
std::vector<s16> samples(out_buffer.frames * input_count);
std::array<s16, TargetSampleCount * MaxChannels> samples{};
for (u32 channel = 0; channel < input_count; channel++) {
const auto offset{inputs[channel] * out_buffer.frames};
@@ -45,7 +44,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
}
out_buffer.tag = reinterpret_cast<u64>(samples.data());
stream->AppendBuffer(out_buffer, samples);
stream->AppendBuffer(out_buffer, {samples.data(), out_buffer.frames * input_count});
if (stream->IsPaused()) {
stream->Start();
+3 -3
View File
@@ -125,10 +125,10 @@ bool MixContext::TSortInfo(const SplitterContext& splitter_context) {
return false;
}
std::vector<s32> sorted_results{node_states.GetSortedResuls()};
const auto result_size{std::min(count, static_cast<s32>(sorted_results.size()))};
auto sorted_results{node_states.GetSortedResuls()};
const auto result_size{std::min(count, static_cast<s32>(sorted_results.second))};
for (s32 i = 0; i < result_size; i++) {
sorted_mix_infos[i] = &mix_infos[sorted_results[i]];
sorted_mix_infos[i] = &mix_infos[sorted_results.first[i]];
}
CalcMixBufferOffset();
@@ -134,8 +134,8 @@ u32 NodeStates::GetNodeCount() const {
return node_count;
}
std::vector<s32> NodeStates::GetSortedResuls() const {
return {results.rbegin(), results.rbegin() + result_pos};
std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls() const {
return {results.rbegin(), result_pos};
}
} // namespace AudioCore::AudioRenderer
+1 -1
View File
@@ -175,7 +175,7 @@ public:
*
* @return Vector of nodes in reverse order.
*/
std::vector<s32> GetSortedResuls() const;
std::pair<std::span<u32>::reverse_iterator, size_t> GetSortedResuls() const;
private:
/// Number of nodes in the graph
+1
View File
@@ -444,6 +444,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
std::scoped_lock l{lock};
const auto start_time{core.CoreTiming().GetClockTicks()};
std::memset(output.data(), 0, output.size());
InfoUpdater info_updater(input, output, process_handle, behavior);
+1 -1
View File
@@ -20,7 +20,7 @@ public:
explicit NullSinkStreamImpl(Core::System& system_, StreamType type_)
: SinkStream{system_, type_} {}
~NullSinkStreamImpl() override {}
void AppendBuffer(SinkBuffer&, std::vector<s16>&) override {}
void AppendBuffer(SinkBuffer&, std::span<s16>) override {}
std::vector<s16> ReleaseBuffer(u64) override {
return {};
}
+8 -8
View File
@@ -15,11 +15,10 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
namespace AudioCore::Sink {
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
if (type == StreamType::In) {
queue.enqueue(buffer);
queued_buffers++;
@@ -67,15 +66,16 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
static_cast<s16>(std::clamp(right_sample, min, max));
}
samples.resize(samples.size() / system_channels * device_channels);
samples = samples.subspan(0, samples.size() / system_channels * device_channels);
} else if (system_channels == 2 && device_channels == 6) {
// We need moar samples! Not all games will provide 6 channel audio.
// TODO: Implement some upmixing here. Currently just passthrough, with other
// channels left as silence.
std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0);
auto new_size = samples.size() / system_channels * device_channels;
tmp_samples.resize_destructive(new_size);
for (u32 read_index = 0, write_index = 0; read_index < samples.size();
for (u32 read_index = 0, write_index = 0; read_index < new_size;
read_index += system_channels, write_index += device_channels) {
const auto left_sample{static_cast<s16>(std::clamp(
static_cast<s32>(
@@ -83,7 +83,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
volume),
min, max))};
new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
const auto right_sample{static_cast<s16>(std::clamp(
static_cast<s32>(
@@ -91,9 +91,9 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
volume),
min, max))};
new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
}
samples = std::move(new_samples);
samples = std::span<s16>(tmp_samples);
} else if (volume != 1.0f) {
for (u32 i = 0; i < samples.size(); i++) {
+4 -1
View File
@@ -16,6 +16,7 @@
#include "common/polyfill_thread.h"
#include "common/reader_writer_queue.h"
#include "common/ring_buffer.h"
#include "common/scratch_buffer.h"
#include "common/thread.h"
namespace Core {
@@ -170,7 +171,7 @@ public:
* @param buffer - Audio buffer information to be queued.
* @param samples - The s16 samples to be queue for playback.
*/
virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples);
virtual void AppendBuffer(SinkBuffer& buffer, std::span<s16> samples);
/**
* Release a buffer. Audio In only, will fill a buffer with recorded samples.
@@ -255,6 +256,8 @@ private:
/// Signalled when ring buffer entries are consumed
std::condition_variable_any release_cv;
std::mutex release_mutex;
/// Temporary buffer for appending samples when upmixing
Common::ScratchBuffer<s16> tmp_samples{};
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;
+2
View File
@@ -172,6 +172,8 @@ if(ARCHITECTURE_x86_64)
x64/cpu_wait.h
x64/native_clock.cpp
x64/native_clock.h
x64/rdtsc.cpp
x64/rdtsc.h
x64/xbyak_abi.h
x64/xbyak_util.h
)
+10 -4
View File
@@ -436,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
if (True(filter & DirEntryFilter::File) &&
entry.status().type() == fs::file_type::regular) {
if (!callback(entry.path())) {
if (!callback(entry)) {
callback_error = true;
break;
}
@@ -444,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
if (True(filter & DirEntryFilter::Directory) &&
entry.status().type() == fs::file_type::directory) {
if (!callback(entry.path())) {
if (!callback(entry)) {
callback_error = true;
break;
}
@@ -493,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
if (True(filter & DirEntryFilter::File) &&
entry.status().type() == fs::file_type::regular) {
if (!callback(entry.path())) {
if (!callback(entry)) {
callback_error = true;
break;
}
@@ -501,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
if (True(filter & DirEntryFilter::Directory) &&
entry.status().type() == fs::file_type::directory) {
if (!callback(entry.path())) {
if (!callback(entry)) {
callback_error = true;
break;
}
@@ -605,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) {
}
u64 GetSize(const fs::path& path) {
#ifdef ANDROID
if (Android::IsContentUri(path)) {
return Android::GetSize(path);
}
#endif
std::error_code ec;
const auto file_size = fs::file_size(path, ec);
+1 -1
View File
@@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);
* @returns A boolean value.
* Return true to indicate whether the callback is successful, false otherwise.
*/
using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>;
using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>;
} // namespace Common::FS
+41 -4
View File
@@ -75,8 +75,10 @@ enum class DriverResult {
ErrorWritingData,
NoDeviceDetected,
InvalidHandle,
InvalidParameters,
NotSupported,
Disabled,
Delayed,
Unknown,
};
@@ -86,7 +88,7 @@ enum class NfcState {
NewAmiibo,
WaitingForAmiibo,
AmiiboRemoved,
NotAnAmiibo,
InvalidTagType,
NotSupported,
WrongDeviceState,
WriteFailed,
@@ -218,8 +220,22 @@ struct CameraStatus {
};
struct NfcStatus {
NfcState state{};
std::vector<u8> data{};
NfcState state{NfcState::Unknown};
u8 uuid_length;
u8 protocol;
u8 tag_type;
std::array<u8, 10> uuid;
};
struct MifareData {
u8 command;
u8 sector;
std::array<u8, 0x6> key;
std::array<u8, 0x10> data;
};
struct MifareRequest {
std::array<MifareData, 0x10> data;
};
// List of buttons to be passed to Qt that can be translated
@@ -294,7 +310,7 @@ struct CallbackStatus {
BatteryStatus battery_status{};
VibrationStatus vibration_status{};
CameraFormat camera_status{CameraFormat::None};
NfcState nfc_status{NfcState::Unknown};
NfcStatus nfc_status{};
std::vector<u8> raw_data{};
};
@@ -356,9 +372,30 @@ public:
return NfcState::NotSupported;
}
virtual NfcState StartNfcPolling() {
return NfcState::NotSupported;
}
virtual NfcState StopNfcPolling() {
return NfcState::NotSupported;
}
virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) {
return NfcState::NotSupported;
}
virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
return NfcState::NotSupported;
}
virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request,
[[maybe_unused]] MifareRequest& out_data) {
return NfcState::NotSupported;
}
virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) {
return NfcState::NotSupported;
}
};
/// An abstract class template for a factory that can create input devices.
+2 -1
View File
@@ -9,6 +9,7 @@
#include <cstddef>
#include <cstring>
#include <new>
#include <span>
#include <type_traits>
#include <vector>
@@ -53,7 +54,7 @@ public:
return push_count;
}
std::size_t Push(const std::vector<T>& input) {
std::size_t Push(const std::span<T> input) {
return Push(input.data(), input.size());
}
+9
View File
@@ -3,6 +3,9 @@
#pragma once
#include <iterator>
#include "common/concepts.h"
#include "common/make_unique_for_overwrite.h"
namespace Common {
@@ -16,6 +19,12 @@ namespace Common {
template <typename T>
class ScratchBuffer {
public:
using iterator = T*;
using const_iterator = const T*;
using value_type = T;
using element_type = T;
using iterator_category = std::contiguous_iterator_tag;
ScratchBuffer() = default;
explicit ScratchBuffer(size_t initial_capacity)
+16 -3
View File
@@ -1,8 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <version>
#if __cpp_lib_chrono >= 201907L
#include <chrono>
#include <exception>
#include <stdexcept>
#endif
#include <string_view>
@@ -25,9 +28,19 @@ std::string GetTimeZoneString() {
if (time_zone_index == 0) { // Auto
#if __cpp_lib_chrono >= 201907L
const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
std::string_view current_zone_name = current_zone->name();
location_name = current_zone_name;
try {
const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
std::string_view current_zone_name = current_zone->name();
location_name = current_zone_name;
} catch (std::runtime_error& runtime_error) {
// VCRUNTIME will throw a runtime_error if the operating system's selected time zone
// cannot be found
location_name = Common::TimeZone::FindSystemTimeZone();
LOG_WARNING(Common,
"Error occurred when trying to determine system time zone:\n{}\nFalling "
"back to hour offset \"{}\"",
runtime_error.what(), location_name);
}
#else
location_name = Common::TimeZone::FindSystemTimeZone();
#endif
+10 -2
View File
@@ -483,6 +483,7 @@ struct Values {
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
"astc_recompression"};
SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"};
SwitchableSetting<u8> bg_red{0, "bg_red"};
SwitchableSetting<u8> bg_green{0, "bg_green"};
@@ -524,9 +525,16 @@ struct Values {
Setting<bool> tas_loop{false, "tas_loop"};
Setting<bool> mouse_panning{false, "mouse_panning"};
Setting<u8, true> mouse_panning_sensitivity{50, 1, 100, "mouse_panning_sensitivity"};
Setting<bool> mouse_enabled{false, "mouse_enabled"};
Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"};
Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"};
Setting<u8, true> mouse_panning_deadzone_x_counterweight{
0, 0, 100, "mouse_panning_deadzone_x_counterweight"};
Setting<u8, true> mouse_panning_deadzone_y_counterweight{
0, 0, 100, "mouse_panning_deadzone_y_counterweight"};
Setting<u8, true> mouse_panning_decay_strength{22, 0, 100, "mouse_panning_decay_strength"};
Setting<u8, true> mouse_panning_min_decay{5, 0, 100, "mouse_panning_min_decay"};
Setting<bool> mouse_enabled{false, "mouse_enabled"};
Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
+15 -1
View File
@@ -5,15 +5,19 @@
#include "common/common_types.h"
#include <optional>
namespace Network {
/// Address families
enum class Domain : u8 {
INET, ///< Address family for IPv4
Unspecified, ///< Represents 0, used in getaddrinfo hints
INET, ///< Address family for IPv4
};
/// Socket types
enum class Type {
Unspecified, ///< Represents 0, used in getaddrinfo hints
STREAM,
DGRAM,
RAW,
@@ -22,6 +26,7 @@ enum class Type {
/// Protocol values for sockets
enum class Protocol : u8 {
Unspecified, ///< Represents 0, usable in various places
ICMP,
TCP,
UDP,
@@ -48,4 +53,13 @@ constexpr u32 FLAG_MSG_PEEK = 0x2;
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
constexpr u32 FLAG_O_NONBLOCK = 0x800;
/// Cross-platform addrinfo structure
struct AddrInfo {
Domain family;
Type socket_type;
Protocol protocol;
SockAddrIn addr;
std::optional<std::string> canon_name;
};
} // namespace Network
+2 -3
View File
@@ -28,13 +28,12 @@ static s64 GetSystemTimeNS() {
// GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
static constexpr s64 Multiplier = 100;
// Convert Windows epoch to Unix epoch.
static constexpr s64 WindowsEpochToUnixEpochNS = 0x19DB1DED53E8000LL;
static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL;
FILETIME filetime;
GetSystemTimePreciseAsFileTime(&filetime);
return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) +
static_cast<s64>(filetime.dwLowDateTime)) -
WindowsEpochToUnixEpochNS;
static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch);
}
#endif
+1
View File
@@ -93,6 +93,7 @@ void AppendCPUInfo(FieldCollection& fc) {
add_field("CPU_Extension_x64_GFNI", caps.gfni);
add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
add_field("CPU_Extension_x64_MONITORX", caps.monitorx);
add_field("CPU_Extension_x64_MOVBE", caps.movbe);
add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
+1 -1
View File
@@ -55,7 +55,7 @@ public:
is_set = false;
}
[[nodiscard]] bool IsSet() {
[[nodiscard]] bool IsSet() const {
return is_set;
}
+32 -45
View File
@@ -2,88 +2,75 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/wall_clock.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#include "common/x64/native_clock.h"
#include "common/x64/rdtsc.h"
#endif
namespace Common {
class StandardWallClock final : public WallClock {
public:
explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_)
: WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false},
start_time{SteadyClock::Now()} {}
explicit StandardWallClock() : start_time{SteadyClock::Now()} {}
std::chrono::nanoseconds GetTimeNS() override {
std::chrono::nanoseconds GetTimeNS() const override {
return SteadyClock::Now() - start_time;
}
std::chrono::microseconds GetTimeUS() override {
return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS());
std::chrono::microseconds GetTimeUS() const override {
return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den);
}
std::chrono::milliseconds GetTimeMS() override {
return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS());
std::chrono::milliseconds GetTimeMS() const override {
return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den);
}
u64 GetClockCycles() override {
const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency);
return Common::Divide128On32(temp, NS_RATIO).first;
u64 GetCNTPCT() const override {
return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
u64 GetCPUCycles() override {
const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency);
return Common::Divide128On32(temp, NS_RATIO).first;
u64 GetGPUTick() const override {
return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
void Pause([[maybe_unused]] bool is_paused) override {
// Do nothing in this clock type.
u64 GetHostTicksNow() const override {
return static_cast<u64>(SteadyClock::Now().time_since_epoch().count());
}
u64 GetHostTicksElapsed() const override {
return static_cast<u64>(GetTimeNS().count());
}
bool IsNative() const override {
return false;
}
private:
SteadyClock::time_point start_time;
};
std::unique_ptr<WallClock> CreateOptimalClock() {
#ifdef ARCHITECTURE_x86_64
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency) {
const auto& caps = GetCPUCaps();
u64 rtsc_frequency = 0;
if (caps.invariant_tsc) {
rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
}
// Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
// - A nanosecond
// - The emulated CPU frequency
// - The emulated clock counter frequency (CNTFRQ)
if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
rtsc_frequency <= emulated_clock_frequency) {
return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
emulated_clock_frequency);
if (caps.invariant_tsc && caps.tsc_frequency >= WallClock::GPUTickFreq) {
return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
} else {
return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency,
rtsc_frequency);
// Fallback to StandardWallClock if the hardware TSC
// - Is not invariant
// - Is not more precise than GPUTickFreq
return std::make_unique<StandardWallClock>();
}
}
#else
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency) {
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
return std::make_unique<StandardWallClock>();
#endif
}
#endif
std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency) {
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
std::unique_ptr<WallClock> CreateStandardWallClock() {
return std::make_unique<StandardWallClock>();
}
} // namespace Common
+61 -28
View File
@@ -5,6 +5,7 @@
#include <chrono>
#include <memory>
#include <ratio>
#include "common/common_types.h"
@@ -12,50 +13,82 @@ namespace Common {
class WallClock {
public:
static constexpr u64 NS_RATIO = 1'000'000'000;
static constexpr u64 US_RATIO = 1'000'000;
static constexpr u64 MS_RATIO = 1'000;
static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
virtual ~WallClock() = default;
/// Returns current wall time in nanoseconds
[[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
/// @returns The time in nanoseconds since the construction of this clock.
virtual std::chrono::nanoseconds GetTimeNS() const = 0;
/// Returns current wall time in microseconds
[[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0;
/// @returns The time in microseconds since the construction of this clock.
virtual std::chrono::microseconds GetTimeUS() const = 0;
/// Returns current wall time in milliseconds
[[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0;
/// @returns The time in milliseconds since the construction of this clock.
virtual std::chrono::milliseconds GetTimeMS() const = 0;
/// Returns current wall time in emulated clock cycles
[[nodiscard]] virtual u64 GetClockCycles() = 0;
/// @returns The guest CNTPCT ticks since the construction of this clock.
virtual u64 GetCNTPCT() const = 0;
/// Returns current wall time in emulated cpu cycles
[[nodiscard]] virtual u64 GetCPUCycles() = 0;
/// @returns The guest GPU ticks since the construction of this clock.
virtual u64 GetGPUTick() const = 0;
virtual void Pause(bool is_paused) = 0;
/// @returns The raw host timer ticks since an indeterminate epoch.
virtual u64 GetHostTicksNow() const = 0;
/// Tells if the wall clock, uses the host CPU's hardware clock
[[nodiscard]] bool IsNative() const {
return is_native;
/// @returns The raw host timer ticks since the construction of this clock.
virtual u64 GetHostTicksElapsed() const = 0;
/// @returns Whether the clock directly uses the host's hardware clock.
virtual bool IsNative() const = 0;
static inline u64 NSToCNTPCT(u64 ns) {
return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
static inline u64 NSToGPUTick(u64 ns) {
return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
// Cycle Timing
static inline u64 CPUTickToNS(u64 cpu_tick) {
return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den;
}
static inline u64 CPUTickToUS(u64 cpu_tick) {
return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den;
}
static inline u64 CPUTickToCNTPCT(u64 cpu_tick) {
return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den;
}
static inline u64 CPUTickToGPUTick(u64 cpu_tick) {
return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
}
protected:
explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_)
: emulated_cpu_frequency{emulated_cpu_frequency_},
emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {}
using NsRatio = std::nano;
using UsRatio = std::micro;
using MsRatio = std::milli;
u64 emulated_cpu_frequency;
u64 emulated_clock_frequency;
using NsToUsRatio = std::ratio_divide<std::nano, std::micro>;
using NsToMsRatio = std::ratio_divide<std::nano, std::milli>;
using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
private:
bool is_native;
// Cycle Timing
using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
};
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency);
std::unique_ptr<WallClock> CreateOptimalClock();
[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency);
std::unique_ptr<WallClock> CreateStandardWallClock();
} // namespace Common
+4
View File
@@ -14,6 +14,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/x64/cpu_detect.h"
#include "common/x64/rdtsc.h"
#ifdef _WIN32
#include <windows.h>
@@ -167,6 +168,7 @@ static CPUCaps Detect() {
__cpuid(cpu_id, 0x80000001);
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
caps.fma4 = Common::Bit<16>(cpu_id[2]);
caps.monitorx = Common::Bit<29>(cpu_id[2]);
}
if (max_ex_fn >= 0x80000007) {
@@ -187,6 +189,8 @@ static CPUCaps Detect() {
caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
caps.tsc_crystal_ratio_numerator /
caps.tsc_crystal_ratio_denominator;
} else {
caps.tsc_frequency = X64::EstimateRDTSCFrequency();
}
}
+1
View File
@@ -63,6 +63,7 @@ struct CPUCaps {
bool gfni : 1;
bool invariant_tsc : 1;
bool lzcnt : 1;
bool monitorx : 1;
bool movbe : 1;
bool pclmulqdq : 1;
bool popcnt : 1;
+38 -32
View File
@@ -9,58 +9,64 @@
#include "common/x64/cpu_detect.h"
#include "common/x64/cpu_wait.h"
#include "common/x64/rdtsc.h"
namespace Common::X64 {
namespace {
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
// For reference:
// At 1 GHz, 100K cycles is 100us
// At 2 GHz, 100K cycles is 50us
// At 4 GHz, 100K cycles is 25us
constexpr auto PauseCycles = 100'000U;
} // Anonymous namespace
#ifdef _MSC_VER
__forceinline static u64 FencedRDTSC() {
_mm_lfence();
_ReadWriteBarrier();
const u64 result = __rdtsc();
_mm_lfence();
_ReadWriteBarrier();
return result;
__forceinline static void TPAUSE() {
static constexpr auto RequestC02State = 0U;
_tpause(RequestC02State, FencedRDTSC() + PauseCycles);
}
__forceinline static void TPAUSE() {
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
// For reference:
// At 1 GHz, 100K cycles is 100us
// At 2 GHz, 100K cycles is 50us
// At 4 GHz, 100K cycles is 25us
static constexpr auto PauseCycles = 100'000;
_tpause(0, FencedRDTSC() + PauseCycles);
__forceinline static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
_mm_monitorx(&monitor_var, 0, 0);
_mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
}
#else
static u64 FencedRDTSC() {
u64 eax;
u64 edx;
asm volatile("lfence\n\t"
"rdtsc\n\t"
"lfence\n\t"
: "=a"(eax), "=d"(edx));
return (edx << 32) | eax;
}
static void TPAUSE() {
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
// For reference:
// At 1 GHz, 100K cycles is 100us
// At 2 GHz, 100K cycles is 50us
// At 4 GHz, 100K cycles is 25us
static constexpr auto PauseCycles = 100'000;
static constexpr auto RequestC02State = 0U;
const auto tsc = FencedRDTSC() + PauseCycles;
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
const auto edx = static_cast<u32>(tsc >> 32);
asm volatile("tpause %0" : : "r"(0), "d"(edx), "a"(eax));
asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
}
static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
}
#endif
void MicroSleep() {
static const bool has_waitpkg = GetCPUCaps().waitpkg;
static const bool has_monitorx = GetCPUCaps().monitorx;
if (has_waitpkg) {
TPAUSE();
} else if (has_monitorx) {
MWAITX();
} else {
std::this_thread::yield();
}
+26 -140
View File
@@ -1,164 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <chrono>
#include <thread>
#include "common/atomic_ops.h"
#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/x64/native_clock.h"
#include "common/x64/rdtsc.h"
#ifdef _MSC_VER
#include <intrin.h>
#endif
namespace Common::X64 {
namespace Common {
NativeClock::NativeClock(u64 rdtsc_frequency_)
: start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_},
ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)},
us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
#ifdef _MSC_VER
__forceinline static u64 FencedRDTSC() {
_mm_lfence();
_ReadWriteBarrier();
const u64 result = __rdtsc();
_mm_lfence();
_ReadWriteBarrier();
return result;
}
#else
static u64 FencedRDTSC() {
u64 eax;
u64 edx;
asm volatile("lfence\n\t"
"rdtsc\n\t"
"lfence\n\t"
: "=a"(eax), "=d"(edx));
return (edx << 32) | eax;
}
#endif
template <u64 Nearest>
static u64 RoundToNearest(u64 value) {
const auto mod = value % Nearest;
return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)};
}
u64 EstimateRDTSCFrequency() {
// Discard the first result measuring the rdtsc.
FencedRDTSC();
std::this_thread::sleep_for(std::chrono::milliseconds{1});
FencedRDTSC();
// Get the current time.
const auto start_time = Common::RealTimeClock::Now();
const u64 tsc_start = FencedRDTSC();
// Wait for 250 milliseconds.
std::this_thread::sleep_for(std::chrono::milliseconds{250});
const auto end_time = Common::RealTimeClock::Now();
const u64 tsc_end = FencedRDTSC();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
const u64 tsc_diff = tsc_end - tsc_start;
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
return RoundToNearest<1000>(tsc_freq);
std::chrono::microseconds NativeClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)};
}
namespace X64 {
NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_,
u64 rtsc_frequency_)
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
rtsc_frequency_} {
// Thread to re-adjust the RDTSC frequency after 10 seconds has elapsed.
time_sync_thread = std::jthread{[this](std::stop_token token) {
// Get the current time.
const auto start_time = Common::RealTimeClock::Now();
const u64 tsc_start = FencedRDTSC();
// Wait for 10 seconds.
if (!Common::StoppableTimedWait(token, std::chrono::seconds{10})) {
return;
}
const auto end_time = Common::RealTimeClock::Now();
const u64 tsc_end = FencedRDTSC();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
const u64 tsc_diff = tsc_end - tsc_start;
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
rtsc_frequency = tsc_freq;
CalculateAndSetFactors();
}};
time_point.inner.last_measure = FencedRDTSC();
time_point.inner.accumulated_ticks = 0U;
CalculateAndSetFactors();
std::chrono::milliseconds NativeClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)};
}
u64 NativeClock::GetRTSC() {
TimePoint new_time_point{};
TimePoint current_time_point{};
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
const u64 current_measure = FencedRDTSC();
u64 diff = current_measure - current_time_point.inner.last_measure;
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
? current_measure
: current_time_point.inner.last_measure;
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack, current_time_point.pack));
return new_time_point.inner.accumulated_ticks;
u64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor);
}
void NativeClock::Pause(bool is_paused) {
if (!is_paused) {
TimePoint current_time_point{};
TimePoint new_time_point{};
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
new_time_point.pack = current_time_point.pack;
new_time_point.inner.last_measure = FencedRDTSC();
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack, current_time_point.pack));
}
u64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor);
}
std::chrono::nanoseconds NativeClock::GetTimeNS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
u64 NativeClock::GetHostTicksNow() const {
return FencedRDTSC();
}
std::chrono::microseconds NativeClock::GetTimeUS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
u64 NativeClock::GetHostTicksElapsed() const {
return FencedRDTSC() - start_ticks;
}
std::chrono::milliseconds NativeClock::GetTimeMS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
bool NativeClock::IsNative() const {
return true;
}
u64 NativeClock::GetClockCycles() {
const u64 rtsc_value = GetRTSC();
return MultiplyHigh(rtsc_value, clock_rtsc_factor);
}
u64 NativeClock::GetCPUCycles() {
const u64 rtsc_value = GetRTSC();
return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
}
void NativeClock::CalculateAndSetFactors() {
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
}
} // namespace X64
} // namespace Common
} // namespace Common::X64
+20 -39
View File
@@ -3,58 +3,39 @@
#pragma once
#include "common/polyfill_thread.h"
#include "common/wall_clock.h"
namespace Common {
namespace Common::X64 {
namespace X64 {
class NativeClock final : public WallClock {
public:
explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_,
u64 rtsc_frequency_);
explicit NativeClock(u64 rdtsc_frequency_);
std::chrono::nanoseconds GetTimeNS() override;
std::chrono::nanoseconds GetTimeNS() const override;
std::chrono::microseconds GetTimeUS() override;
std::chrono::microseconds GetTimeUS() const override;
std::chrono::milliseconds GetTimeMS() override;
std::chrono::milliseconds GetTimeMS() const override;
u64 GetClockCycles() override;
u64 GetCNTPCT() const override;
u64 GetCPUCycles() override;
u64 GetGPUTick() const override;
void Pause(bool is_paused) override;
u64 GetHostTicksNow() const override;
u64 GetHostTicksElapsed() const override;
bool IsNative() const override;
private:
u64 GetRTSC();
u64 start_ticks;
u64 rdtsc_frequency;
void CalculateAndSetFactors();
union alignas(16) TimePoint {
TimePoint() : pack{} {}
u128 pack{};
struct Inner {
u64 last_measure{};
u64 accumulated_ticks{};
} inner;
};
TimePoint time_point;
// factors
u64 clock_rtsc_factor{};
u64 cpu_rtsc_factor{};
u64 ns_rtsc_factor{};
u64 us_rtsc_factor{};
u64 ms_rtsc_factor{};
u64 rtsc_frequency;
std::jthread time_sync_thread;
u64 ns_rdtsc_factor;
u64 us_rdtsc_factor;
u64 ms_rdtsc_factor;
u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor;
};
} // namespace X64
u64 EstimateRDTSCFrequency();
} // namespace Common
} // namespace Common::X64
+39
View File
@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/x64/rdtsc.h"
namespace Common::X64 {
template <u64 Nearest>
static u64 RoundToNearest(u64 value) {
const auto mod = value % Nearest;
return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
}
u64 EstimateRDTSCFrequency() {
// Discard the first result measuring the rdtsc.
FencedRDTSC();
std::this_thread::sleep_for(std::chrono::milliseconds{1});
FencedRDTSC();
// Get the current time.
const auto start_time = RealTimeClock::Now();
const u64 tsc_start = FencedRDTSC();
// Wait for 100 milliseconds.
std::this_thread::sleep_for(std::chrono::milliseconds{100});
const auto end_time = RealTimeClock::Now();
const u64 tsc_end = FencedRDTSC();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
const u64 tsc_diff = tsc_end - tsc_start;
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
return RoundToNearest<100'000>(tsc_freq);
}
} // namespace Common::X64
+37
View File
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/common_types.h"
namespace Common::X64 {
#ifdef _MSC_VER
__forceinline static u64 FencedRDTSC() {
_mm_lfence();
_ReadWriteBarrier();
const u64 result = __rdtsc();
_mm_lfence();
_ReadWriteBarrier();
return result;
}
#else
static inline u64 FencedRDTSC() {
u64 eax;
u64 edx;
asm volatile("lfence\n\t"
"rdtsc\n\t"
"lfence\n\t"
: "=a"(eax), "=d"(edx));
return (edx << 32) | eax;
}
#endif
u64 EstimateRDTSCFrequency();
} // namespace Common::X64
+18 -1
View File
@@ -14,7 +14,6 @@ add_library(core STATIC
core.h
core_timing.cpp
core_timing.h
core_timing_util.h
cpu_manager.cpp
cpu_manager.h
crypto/aes_util.cpp
@@ -723,6 +722,7 @@ add_library(core STATIC
hle/service/spl/spl_types.h
hle/service/ssl/ssl.cpp
hle/service/ssl/ssl.h
hle/service/ssl/ssl_backend.h
hle/service/time/clock_types.h
hle/service/time/ephemeral_network_system_clock_context_writer.h
hle/service/time/ephemeral_network_system_clock_core.h
@@ -864,6 +864,23 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(core PRIVATE dynarmic::dynarmic)
endif()
if(ENABLE_OPENSSL)
target_sources(core PRIVATE
hle/service/ssl/ssl_backend_openssl.cpp)
target_link_libraries(core PRIVATE OpenSSL::SSL)
elseif (APPLE)
target_sources(core PRIVATE
hle/service/ssl/ssl_backend_securetransport.cpp)
target_link_libraries(core PRIVATE "-framework Security")
elseif (WIN32)
target_sources(core PRIVATE
hle/service/ssl/ssl_backend_schannel.cpp)
target_link_libraries(core PRIVATE secur32)
else()
target_sources(core PRIVATE
hle/service/ssl/ssl_backend_none.cpp)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(core PRIVATE precompiled_headers.h)
endif()

Some files were not shown because too many files have changed in this diff Show More