Compare commits

...

137 Commits

Author SHA1 Message Date
MerryMage f54f29198f libusb: Apple is a POSIX system 2021-06-26 20:24:18 +01:00
Ameer J d9fb6dbd37 Merge pull request #6528 from ReinUsesLisp/device-memory
vulkan_device: Make device memory match the rest of the file
2021-06-25 17:07:24 -04:00
ReinUsesLisp 99b859db55 vulkan_device: Make device memory match the rest of the file
Match the style in the file.
2021-06-25 02:38:58 -03:00
bunnei c805c0b395 Merge pull request #6496 from ameerj/astc-fixes
astc: Various robustness enhancements for the gpu decoder
2021-06-24 21:47:05 -07:00
bunnei b9c2732121 Merge pull request #6519 from Wunkolo/mem-size-literal
common: Replace common_sizes into user-literals
2021-06-24 19:09:12 -07:00
Wunkolo 4569f39c7c common: Replace common_sizes into user-literals
Removes common_sizes.h in favor of having `_KiB`, `_MiB`, `_GiB`, etc
user-literals within literals.h.

To keep the global namespace clean, users will have to use:

```
using namespace Common::Literals;
```

to access these literals.
2021-06-24 09:27:40 -07:00
bunnei 4dc2f5a341 Merge pull request #6522 from Morph1984/pragma
general: Add missing #pragma once directives
2021-06-24 08:53:27 -07:00
Morph eed0d1f33b general: Add missing #pragma once directives 2021-06-24 07:20:57 -04:00
Chloe f216a9432b Add missing includes (#6521)
* Add missing includes

* Add array
2021-06-24 03:24:17 -04:00
Schplee 5ed87152c3 Fix bold tag 2021-06-23 23:37:03 -07:00
Schplee 373bc6512d Fix typo in Readme 2021-06-23 23:35:46 -07:00
Schplee 496327490f Update Readme 2021-06-23 23:32:41 -07:00
bunnei 1b09d6628b Merge pull request #6517 from lioncash/fmtlib
externals: Update fmt to 8.0.0
2021-06-23 15:31:04 -07:00
bunnei 809e5fd523 Merge pull request #6504 from Kelebek1/samples-played
[audout] Implement GetAudioOutPlayedSampleCount
2021-06-23 11:31:12 -07:00
Lioncash d0b1f2bd05 General: Resolve fmt specifiers to adhere to 8.0.0 API where applicable
Also removes some deprecated API usages.
2021-06-23 13:48:21 -04:00
bunnei d8d9bb0dfb Merge pull request #6518 from lioncash/func
maxwell3d: Add missing return in default SizeInBytes() case
2021-06-23 09:43:00 -07:00
Lioncash be6844c1ed maxwell3d: Add missing return in default SizeInBytes() case
We were returning '1' in ComponentCount()'s default case but were
neglecting to do the same with SizeInBytes().
2021-06-23 11:50:40 -04:00
Lioncash c3fe071723 externals: Update dynarmic to allow fmt compilation to succeed 2021-06-23 09:31:59 -04:00
Mai M 17fff10e06 Merge pull request #6465 from FernandoS27/sex-on-the-beach
GPU: Implement a garbage collector for GPU Caches (project Reaper+)
2021-06-23 08:03:01 -04:00
Lioncash 8593e6f1e6 externals: Update fmt to 8.0.0
Keeps us up to date with the latest major release.

Also allows compilers that support it to perform compile-time format
string checking.
2021-06-23 02:43:36 -04:00
Mai M 20f474b09a Merge pull request #6508 from ReinUsesLisp/bootmanager-stop-token
bootmanager: Use std::stop_source for stopping emulation
2021-06-23 02:35:42 -04:00
Morph c1a9fa9db7 Merge pull request #6514 from OZtistic/master
Simple resizing of Per-Game configuration window and removal of useless Help question mark button in the title bar
2021-06-22 23:19:13 -04:00
Mai M d6b51e5e21 Merge pull request #6512 from ReinUsesLisp/wait-detached-stasks
common/detached_tasks: Wait for tasks before shutting down
2021-06-22 22:20:14 -04:00
Mai M 95b4c78b07 Merge pull request #6509 from ReinUsesLisp/mouse-datarace
input_common/mouse_input: Fix data race
2021-06-22 22:19:34 -04:00
Mai M 4ec7d79174 Merge pull request #6510 from ReinUsesLisp/npad-data-race
npad: Fix data race when updating devices
2021-06-22 22:17:57 -04:00
OZtistic 36aacf62ad Simple resizing of the Per-Game configuration window and removal of useless Help question mark button in the title bar 2021-06-23 12:02:10 +10:00
bunnei 0308a2679e Merge pull request #6493 from Morph1984/fs-nodiscard
common: fs: Miscellaneous changes
2021-06-22 17:21:59 -07:00
bunnei 255f8d22d7 Merge pull request #6472 from Morph1984/spl
service: spl: Implement general SPL service
2021-06-22 15:43:10 -07:00
bunnei 791d3d1bea Merge pull request #6483 from Morph1984/get-tz-file
service: time: Use GetFileRelative to get files within subdirectories
2021-06-22 14:25:41 -07:00
Fernando Sahmkow f9b940a442 Reaper: Set minimum cleaning limit on OGL. 2021-06-22 22:07:17 +02:00
Morph 2fa207058b common: fs: Add a description of a regular file in IsFile
This provides a more concrete example of what a regular file is and isn't.
2021-06-22 15:07:51 -04:00
Morph 0394893354 vfs_real: Fix Mode to FileAccessMode conversion
These enforce requiring the file to exist prior to opening.
2021-06-22 15:07:51 -04:00
Morph 76b2313b25 common: fs: Amend IsFile check in FileOpen / (Write/Append)StringToFile
This check was preventing files with the Write or Append file access modes from being created, as per the documented behavior in FileAccessMode.
This amends the check to test for the existence of a filesystem object prior to checking whether it is a regular file.
Thanks to liushuyu for pointing out that removing the check altogether would not guard against attempting to open non-regular files such as directories, symlinks, FIFO (pipes), sockets, block devices, or character devices.
The documentation has also been updated for these functions to clarify that a file refers to a regular file.
2021-06-22 15:06:58 -04:00
Morph cf0b9d1de2 common: fs: file: Remove [[nodiscard]] attribute from Flush
Similarly, Flush() is typically called to attempt to flush a file into the disk. In the one case where this is used, we do not care whether the flush has succeeded or not, making [[nodiscard]] unnecessary.
2021-06-22 13:36:24 -04:00
Morph 81b1b71993 common: fs: Remove [[nodiscard]] attribute on Remove* functions
There are a lot of scenarios where we don't particularly care whether or not the removal operation and just simply attempt a removal.

As such, removing the [[nodiscard]] attribute is best for these functions.
2021-06-22 13:36:24 -04:00
bunnei faf57c183f Merge pull request #6506 from ReinUsesLisp/master-semaphore-jthread
vk_master_semaphore: Use jthread for debug thread
2021-06-22 08:44:25 -07:00
bunnei 0e2c1a5739 Merge pull request #6495 from lat9nq/mingw-vista-style
ci: windows: Copy the Qt styles directory when packaging
2021-06-22 08:39:28 -07:00
Mai M 698add8541 Merge pull request #6511 from ReinUsesLisp/core-is-powered-data-race
core: Make is_powered_on atomic
2021-06-22 04:28:38 -04:00
Rodrigo Locatti c9c8537643 core: Make is_powered_on atomic
Fixes potential data races when shutting down.
2021-06-22 04:33:07 -03:00
Rodrigo Locatti 1ca9a13e50 common/detached_tasks: Wait for tasks before shutting down
If this is not waited on, the synchronization primitives are destroyed
whe main exits and the detached task ends up signalling garbage and not
properly finishing.
2021-06-22 04:27:44 -03:00
Rodrigo Locatti 15cc561d12 npad: Fix data race when updating devices
Add a lock to avoid data races.
This reduces the number of -fsanitize=thread errors significantly.
2021-06-22 03:16:21 -03:00
Rodrigo Locatti 0a39163a90 input_common/mouse_input: Fix data race
Fix data race using std::jthread and std::stop_token.
2021-06-22 02:31:39 -03:00
bunnei 2a7a65c944 Merge pull request #6481 from Morph1984/missing-peak-set
kernel: Fix missing peak set in KResourceLimit::SetLimitValue
2021-06-21 22:16:48 -07:00
Kelebek1 ba3af04da1 Implement audout GetAudioOutPlayedSampleCount
Used in Ninja Gaiden games.
2021-06-22 04:39:17 +01:00
ReinUsesLisp 4009ae1da2 bootmanager: Use std::stop_source for stopping emulation
Use its std::stop_token to abort shader cache loading.

Using std::stop_token instead of std::atomic_bool allows the usage of
other utilities like std::stop_callback.
2021-06-22 00:04:57 -03:00
ReinUsesLisp cf116a28a6 vk_master_semaphore: Use jthread for debug thread 2021-06-21 19:56:07 -03:00
bunnei 0485b8e84b Merge pull request #6499 from FernandoS27/we-were-on-a-break
Update dynarmic and add new unsafe CPU option.
2021-06-21 14:56:08 -07:00
bunnei 2a3d3d3895 Merge pull request #6475 from ameerj/unlimit-fps
nvflinger: Add experimental toggle to disable buffer swap interval limits
2021-06-21 11:58:12 -07:00
Mai M 83ac715e76 Merge pull request #6486 from CaptV0rt3x/httplib
externals: httplib: replace custom httplib header with upstream as submodule
2021-06-21 11:56:33 -04:00
Fernando Sahmkow 2298508465 Update dynarmic and add new unsafe CPU option. 2021-06-20 20:40:02 +02:00
Fernando Sahmkow 569a1962c0 Reaper: Guarantee correct deletion. 2021-06-20 19:11:41 +02:00
Fernando Sahmkow 865dd615ca Reaper: Upgrade label from unsafe to experimental as no regressions are known now. 2021-06-20 12:35:19 +02:00
ameerj 851c76233d util_shaders: Specify ASTC decoder memory barrier bits 2021-06-19 11:16:25 -04:00
ameerj ace20ba4a4 astc_decoder.comp: Remove unnecessary LUT SSBOs
We can move them to instead be compile time constants within the shader.
2021-06-19 10:56:13 -04:00
ameerj 31b125ef57 astc: Various robustness enhancements for the gpu decoder
These changes should help in reducing crashes/drivers panics that may
occur due to synchronization issues between the shader completion and
later access of the decoded texture.
2021-06-19 09:00:33 -04:00
lat9nq ad8aab915b ci: windows: Copy the styles directory when packaging
Qt can make use of qwindowsvistastyle.dll if present, and our MinGW
container has the library, but it was not being copied during the
packaging process. Thus, yuzu looked like a Windows 98 application when
using the PR-verify artifacts.

This copies over the DLL during packaging, for that sweet-sweet Windows
Vista style.

In addition, set the Qt plugins path instead of the plugins/platforms
path. This way we can use the directory directly, rather than appending
a `..` everytime we need something just outside of it.
2021-06-19 05:24:56 -04:00
Morph 03da34b330 Merge pull request #6494 from lat9nq/mingw-fix-fastmem
host_memory: Correct MEM_RESERVE_PLACEHOLDER
2021-06-19 04:40:58 -04:00
lat9nq 9a06b85b24 host_memory: Correct MEM_RESERVE_PLACEHOLDER
Microsoft defines `MEM_RESERVE_PLACEHOLDER` as `0x00040000`, but our
manually imported version of it drops the last zero.
2021-06-19 04:38:33 -04:00
Vortex e704da9192 externals: httplib: replace custom httplib header with upstream as submodule.
This also includes a minor change to web_service.cpp - to fix compatibility with upstream changes.
2021-06-19 02:18:58 +05:30
Mai M 3870ba670f Merge pull request #6484 from CaptV0rt3x/discord-rpc
update submodule discord-rpc to latest [now deprecated]
2021-06-18 15:18:15 -04:00
Vortex 8a83e3412a update submodule discord-rpc to latest [now deprecated] 2021-06-18 23:13:17 +05:30
Morph fd5ef1970c service: time: Use GetFileRelative to get files within subdirectories
The timezone info file can be within subdirectories (such as Asia/Tokyo), use GetFileRelative instead of GetFile to get files within subdirectories.
2021-06-18 11:25:26 -04:00
Rodrigo Locatti bb18a6533d Merge pull request #6478 from ameerj/vk-layer-settings
vulkan_debug_callback: Skip logging known false-positive validation errors
2021-06-18 10:10:49 -03:00
Morph 1a5eceeb9c kernel: Fix missing peak set in KResourceLimit::SetLimitValue 2021-06-18 07:27:48 -04:00
ameerj 0b172d12c0 vulkan_debug_callback: Skip logging known false-positive validation errors
Avoids overwhelming the log with validation errors that are not applicable
2021-06-17 22:16:32 -04:00
Fernando Sahmkow 719a6dd5a1 Reaper: Correct size calculation on Vulkan. 2021-06-17 08:48:41 +02:00
ameerj 36250a4730 config: Add frame limiter toggle hotkey 2021-06-17 01:41:57 -04:00
ameerj 3522fc019c nvflinger: Add toggle to disable buffer swap interval limits
Enabling this setting will allow some titles to present more frames to
the screen as they become available in the nvflinger buffer queue.
2021-06-17 01:41:56 -04:00
bunnei abb0124b84 Merge pull request #6418 from clementgallet/sdl-audio-backend
Audio: SDL2 audio backend
2021-06-16 21:38:20 -07:00
Ameer J c5b517aa5f Merge pull request #6469 from ReinUsesLisp/blit-view-compat
texture_cache/util: Avoid relaxed image views on different bytes per block
2021-06-16 21:08:07 -04:00
Fernando Sahmkow ca6f47c686 Reaper: Change memory restrictions on TC depending on host memory on VK. 2021-06-17 00:29:48 +02:00
Fernando Sahmkow 0dd98842bf Reaper: Address Feedback. 2021-06-16 21:35:03 +02:00
Fernando Sahmkow 954ad2a61e Reaper: Setup settings and final tuning. 2021-06-16 21:35:03 +02:00
Fernando Sahmkow d8ad6aa187 Reaper: Tune it up to be an smart GC. 2021-06-16 21:35:02 +02:00
ReinUsesLisp a11bc4a382 Initial Reaper Setup
WIP
2021-06-16 21:35:02 +02:00
ReinUsesLisp 5b1efe522e vulkan_memory_allocator: Release allocations with no commits 2021-06-16 21:35:01 +02:00
bunnei 973bf306ed Merge pull request #6464 from ameerj/disable-astc
textures: Add a toggle for GPU Accelerated ASTC decoder
2021-06-16 11:29:10 -07:00
Morph 92942fe01b Merge pull request #6460 from Morph1984/fs-access-log-fix
fsp_srv: Fix filesystem access logging
2021-06-16 14:03:01 -04:00
Morph 4a4e304319 spl: Mark the other functions as unimplemented 2021-06-16 01:46:45 -04:00
Morph 8ba83c4c2a spl: Implement spl::GetConfig 2021-06-16 01:46:45 -04:00
Morph e4318a1914 hle: api_version: Add HLE API version constants 2021-06-16 01:46:45 -04:00
Morph ded36b8688 spl: Add the general SPL interface 2021-06-16 01:46:45 -04:00
Morph faf11fe46d spl: Add SPL types 2021-06-16 01:46:45 -04:00
Morph 95f203b7c7 spl: Add SPL result codes 2021-06-16 01:07:58 -04:00
Morph 74790e4f33 common: fs: file: Remove redundant call to WriteStringToFile
The Append open mode will create a new file if said file does not exist at a given path, making this call redundant.
2021-06-16 00:06:02 -04:00
Morph 0f48292de1 fsp_srv: Fix filesystem access logging
This introduces a new setting Enable FS Access Log which saves the filesystem access log to sdmc:/FsAccessLog.txt

If this setting is not enabled, this will indicate to FS to not call OutputAccessLogToSdCard.

Fixes softlocks during loading in Xenoblade Chronicles 2 when certain DLC is enabled.
2021-06-16 00:06:02 -04:00
bunnei 78651b5476 Merge pull request #6462 from Morph1984/proper-flush
common: fs: file: Flush the file to the disk when Flush() is called
2021-06-15 19:03:19 -07:00
ameerj 5fc8393125 astc_decoder: Fix LDR CEM1 endpoint calculation
Per the spec, L1 is clamped to the value 0xff if it is greater than 0xff. An oversight caused us to take the maximum of L1 and 0xff, rather than the minimum.

Huge thanks to wwylele for finding this.

Co-Authored-By: Weiyi Wang <wwylele@gmail.com>
2021-06-15 20:19:01 -04:00
ameerj f9bfeaa2bc yuzu_cmd/config: Add Accelerate ASTC and missing NVDEC emulation settings 2021-06-15 20:19:00 -04:00
ameerj b2955479e5 configure_graphics: Add Accelerate ASTC decoding setting 2021-06-15 20:19:00 -04:00
ameerj c4ff7ecf51 textures: Reintroduce CPU ASTC decoder
Users may want to fall back to the CPU ASTC texture decoder due to hangs
and crashes that may be caused by keeping the GPU under compute heavy
loads for extended periods of time. This is especially the case in games
such as Astral Chain which make extensive use of ASTC textures.
2021-06-15 20:19:00 -04:00
bunnei f3caf53648 Merge pull request #6470 from ameerj/lm-silence
lm: Demote LM guest logs to LOG_DEBUG
2021-06-14 20:40:25 -07:00
ameerj 422a15ee75 lm: Demote guest logs to LOG_DEBUG
Guest logs are not very useful, as they are intended for use by the game developers during development. As such, they provide little meaning to be logged by yuzu and tend to overwhelm the log output at times.
2021-06-14 22:23:27 -04:00
ReinUsesLisp 3d89398b84 texture_cache/util: Avoid relaxed image views on different bytes per pixel
Avoids API usage errors on UE4 titles leading to crashes.
2021-06-14 21:03:57 -03:00
bunnei c8f86edee6 Merge pull request #6456 from Morph1984/very-important-changes
configure_cpu_debug: Clarify settings behavior
2021-06-14 15:05:44 -07:00
Fernando Sahmkow 8d4dfc98ec Merge pull request #6448 from Morph1984/recursive-dir-iterator
common: fs: Use the normal directory iterator in *Recursively functions
2021-06-14 15:47:04 +02:00
Mai M de12dbb01a Merge pull request #6463 from Morph1984/restructure-logging
common: logging: Restructure logging backend
2021-06-13 14:29:39 -04:00
Morph a4454329c1 general: Remove extraneous includes 2021-06-13 11:32:43 -04:00
Morph 391e823c79 common: logging: Restructure backend code 2021-06-13 11:05:58 -04:00
Morph 8150c65c07 common: logging: backend: Wrap IOFile in a unique_ptr
Allows us to forward declare Common::FS::IOFile.
2021-06-13 11:05:58 -04:00
Morph a98b6c8f07 common: fs: file: Flush the file to the disk when Flush() is called
std::fflush does not guarantee that file buffers are flushed to the disk.

Use _commit on Windows and fsync on all other OSes to ensure that the file is flushed to the disk.
2021-06-13 07:47:57 -04:00
Morph 56afd4ab4b Merge pull request #6452 from german77/sixaxis_firmware_stub
hid: Stub IsFirmwareUpdateAvailableForSixAxisSensor
2021-06-13 05:28:32 -04:00
Morph c11b4c45e1 configure_cpu_debug: Clarify settings behavior
This makes it clear that the disabled settings only take effect when CPU Accuracy is set to Debug Mode.
2021-06-13 00:27:33 -04:00
Morph c978f3144c common: fs: Use the normal directory iterator in *Recursively functions
MSVC's implementation of recursive_directory_iterator throws an exception on an error despite a std::error_code being passed into its constructor. This is most likely a bug in MSVC's implementation since directory_iterator does not throw an exception on an error.

We can replace the usage of recursive_directory_iterator for now until MSVC fixes their implementation of it.
2021-06-12 01:39:07 -04:00
bunnei 8b5655a98e Merge pull request #6453 from lat9nq/libusb-fix-msvc
externals: Don't set FOUND or VERSION on LIBUSB
2021-06-11 16:29:34 -07:00
lat9nq 2817ef1a53 externals: Don't set FOUND or VERSION on LIBUSB
Fixes an issue where libusb.h wouldn't be found when building yuzu on
MSVC.

This only affects the "traditional" CMake pathway for linking libusb to
yuzu AKA MSVC. For autotools we still want to set these variables before
configuring SDL.
2021-06-11 16:57:04 -04:00
bunnei 58180f9fa8 Merge pull request #6451 from Morph1984/check-disk-space-dump
yuzu: main: Ensure enough space is available for RomFS dumping
2021-06-11 13:23:23 -07:00
german77 827483409b hid: Stub IsFirmwareUpdateAvailableForSixAxisSensor 2021-06-11 14:44:46 -05:00
Mai M 9951322e5a Merge pull request #6422 from FernandoS27/i-am-the-senate
Implement/Port Fastmem from Citra to Yuzu
2021-06-11 14:26:54 -04:00
Morph fa2aac1bf5 yuzu: main: Ensure enough space is available for RomFS dumping
This warns the user if there isn't enough free space to dump the entire RomFS to disk. It requires at least the size of the extracted RomFS + 1 GiB as a buffer of free space.
2021-06-11 14:04:11 -04:00
bunnei 0c0c1a039e Merge pull request #6443 from Morph1984/k-light-condition-variable
kernel: KLightConditionVariable: Update implementation to 12.x
2021-06-11 11:03:55 -07:00
Markus Wick 7f85abb281 common/host_memory: Implement a fallback if fastmem fails.
This falls back to the old approach of using a virtual buffer.

Windows is untested, but this build should fix support for Windows < 10 v1803. However without fastmem support at all.
2021-06-11 17:27:17 +02:00
ReinUsesLisp f332d4a9b5 common/host_shader: Load Windows 10 functions dynamically
Workaround old headers and libraries shipped on MinGW.
2021-06-11 17:27:17 +02:00
Fernando Sahmkow 588ab44470 GPUTHread: Remove async reads from Normal Accuracy. 2021-06-11 17:27:17 +02:00
ReinUsesLisp 7b0d8bd1fb rasterizer: Update pages in batches 2021-06-11 17:27:17 +02:00
ReinUsesLisp ee67460ff0 host_memory: Support staged VirtualProtect calls 2021-06-11 17:27:17 +02:00
FernandoS27 5ba28325b2 General: Add settings for fastmem and disabling adress space check. 2021-06-11 17:27:17 +02:00
Markus Wick c4609c92ee common/host_memory: Optimize for huge tables.
In theory, if we have 2 MB continously mapped, this should save one layer of TLB.
Let's make it at least more likely by aligning the memory.
2021-06-11 17:27:06 +02:00
Markus Wick 621f3f5f47 core: Make use of fastmem 2021-06-11 17:27:06 +02:00
ReinUsesLisp 740edacc8d tests: Add tests for host memory 2021-06-11 17:27:06 +02:00
Markus Wick 5105318bbc common/host_memory: Add Linux implementation 2021-06-11 17:27:06 +02:00
ReinUsesLisp a7837a3791 common/host_memory: Add interface and Windows implementation 2021-06-11 17:27:06 +02:00
Morph fbb170857f Merge pull request #6450 from lat9nq/update-sdl
externals: Update SDL to 2f248a2a
2021-06-11 06:33:50 -04:00
lat9nq f738c6b231 externals: Update SDL to 2f248a2a 2021-06-11 04:40:16 -04:00
bunnei c1b8e59ea0 Merge pull request #6407 from lat9nq/fix-libusb-2
cmake: Use autotools for libusb linking generally on GNU, and cleanup
2021-06-10 23:35:30 -07:00
bunnei 46ec0ee55b Merge pull request #6445 from degasus/fix_ubsn
Fix GCC undefined behavior sanitizer.
2021-06-10 22:17:33 -07:00
Morph ebd38d66db kernel: Unconditionally set thread state when appropriate 2021-06-11 00:58:04 -04:00
Morph aa79ca7a7a kernel: KLightConditionVariable: Update implementation to 12.x
Updates the implementation of KLightConditionVariable to FW 12.x
2021-06-11 00:58:04 -04:00
bunnei 4547b2735a Merge pull request #6444 from bunnei/fix-sm-sessions
hle: service: sm: Remove redundant session reservation, etc.
2021-06-10 12:17:13 -07:00
bunnei 781c85b951 hle: service: sm: Remove redundant session reservation, etc.
- We were double-reserving, causing us to run out of sessions in Pokemon Sword & Shield.
2021-06-10 11:34:41 -07:00
Clément Gallet f611506dca Various suggestions by v1993 and lioncash 2021-06-07 12:51:59 +02:00
Clément Gallet df8a2e3ad8 Add sdl2 audio description in the yuzu-cmd config file 2021-06-06 11:29:02 +02:00
Clément Gallet c7c99905f4 Add SDL2 audio backend 2021-06-06 11:28:38 +02:00
lat9nq 7395cd3124 externals: libusb: Call program names not full paths 2021-06-03 04:53:01 -04:00
lat9nq 890acfa2c0 externals: libusb: Link libusb statically on Linux
Turns out that this is possible. Also addresses my own review comment.
2021-06-03 04:38:29 -04:00
lat9nq ddc47e6df8 cmake: General improvements to libusb linking
Delegates libusb external communication to externals/CMakeLists.txt
Ensures an interface library `usb` for every pathway
input_common just links to the `usb` library now
externals/libusb/CMakeLists.txt sets variables to override SDL2's libusb
finding
Other minor cleanup
2021-06-03 03:49:35 -04:00
lat9nq 55dd027115 cmake: Use autotools to build libusb generally for GNU
Building libusb was also broken on GCC (and maybe Clang) on our
CMakeLists after upgrading to 1.0.24, but it was not being checked
because our 18.04 container had libusb installed on it.
This builds on the MinGW work from earlier and extends it to the rest of
the GNU toolchains. In addition we make use of pkg-config when present
to find libusb. pkg-config is preferrable because we can specify a
minimum required version.
2021-06-03 02:49:53 -04:00
163 changed files with 4901 additions and 7810 deletions
+6 -5
View File
@@ -18,19 +18,20 @@ cd ..
mkdir package
if [ -d "/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/" ]; then
QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/'
QT_PLUGINS_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins'
else
#fallback to qt
QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt/plugins/platforms/'
QT_PLUGINS_PATH='/usr/x86_64-w64-mingw32/lib/qt/plugins'
fi
find build/ -name "yuzu*.exe" -exec cp {} 'package' \;
# copy Qt plugins
mkdir package/platforms
cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/
cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/
cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/
cp -v "${QT_PLUGINS_PATH}/platforms/qwindows.dll" package/platforms/
cp -rv "${QT_PLUGINS_PATH}/mediaservice/" package/
cp -rv "${QT_PLUGINS_PATH}/imageformats/" package/
cp -rv "${QT_PLUGINS_PATH}/styles/" package/
rm -f package/mediaservice/*d.dll
for i in package/*.exe; do
+4 -1
View File
@@ -18,7 +18,7 @@
url = https://github.com/libusb/libusb.git
[submodule "discord-rpc"]
path = externals/discord-rpc
url = https://github.com/discordapp/discord-rpc.git
url = https://github.com/discord/discord-rpc.git
[submodule "Vulkan-Headers"]
path = externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
@@ -43,3 +43,6 @@
[submodule "SDL"]
path = externals/SDL
url = https://github.com/libsdl-org/SDL.git
[submodule "externals/cpp-httplib"]
path = externals/cpp-httplib
url = https://github.com/yhirose/cpp-httplib.git
+18 -8
View File
@@ -23,6 +23,8 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ON "WIN32" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
@@ -170,7 +172,7 @@ macro(yuzu_find_packages)
set(REQUIRED_LIBS
# Cmake Pkg Prefix Version Conan Pkg
"Catch2 2.13 catch2/2.13.0"
"fmt 7.1 fmt/7.1.2"
"fmt 8.0 fmt/8.0.0"
"lz4 1.8 lz4/1.9.2"
"nlohmann_json 3.8 nlohmann_json/3.8.0"
"ZLIB 1.2 zlib/1.2.11"
@@ -420,14 +422,22 @@ elseif (TARGET Boost::boost)
endif()
# Ensure libusb is properly configured (based on dolphin libusb include)
if(NOT APPLE)
if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
include(FindPkgConfig)
find_package(LibUSB)
endif()
if (NOT LIBUSB_FOUND)
add_subdirectory(externals/libusb)
set(LIBUSB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/externals/libusb/libusb/libusb")
set(LIBUSB_LIBRARIES usb)
if (PKG_CONFIG_FOUND)
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
else()
find_package(LibUSB)
endif()
if (LIBUSB_FOUND)
add_library(usb INTERFACE)
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}")
else()
message(WARNING "libusb not found, falling back to externals")
set(YUZU_USE_BUNDLED_LIBUSB ON)
endif()
endif()
# List of all FFmpeg components required
+57 -20
View File
@@ -1,43 +1,80 @@
yuzu emulator
=============
[![Travis CI Build Status](https://travis-ci.com/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.com/yuzu-emu/yuzu)
[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
[![Discord](https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white)](https://discord.com/invite/u77vRWY)
<h1 align="center">
<br>
<a href="https://yuzu-emu.org/"><img src="https://raw.githubusercontent.com/yuzu-emu/yuzu-assets/master/icons/icon.png" alt="yuzu" width="200"></a>
<br>
<b>yuzu</b>
<br>
</h1>
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
<h4 align="center"><b>yuzu</b> is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of <a href="https://citra-emu.org" target="_blank">Citra</a>.
<br>
It is written in C++ with portability in mind, and we actively maintain builds for Windows and Linux.
</h4>
It is written in C++ with portability in mind, with builds actively maintained for Windows and Linux. The emulator is capable of running several commercial games.
<p align="center">
<a href="https://dev.azure.com/yuzu-emu/yuzu/">
<img src="https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master"
alt="Azure Mainline CI Build Status">
</a>
<a href="https://discord.com/invite/u77vRWY">
<img src="https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white"
alt="Discord">
</a>
</p>
yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
<p align="center">
<a href="#compatibility">Compatibility</a> |
<a href="#development">Development</a> |
<a href="#building">Building</a> |
<a href="#download">Download</a> |
<a href="#support">Support</a> |
<a href="#license">License</a>
</p>
Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
## Compatibility
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
The emulator is capable of running most commercial games at full speed, provided you meet the [necessary hardware requirements](https://yuzu-emu.org/help/quickstart/#hardware-requirements).
Check out our [website](https://yuzu-emu.org/)!
For a full list of games yuzu support, please visit our [Compatibility page](https://yuzu-emu.org/game/)
For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
Check out our [website](https://yuzu-emu.org/) for the latest news on exciting features, monthly progress reports, and more!
### Development
## Development
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted.
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted. For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator.
If you want to contribute, please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information).
You can also contact any of the developers on Discord in order to know about the current state of the emulator.
If you want to contribute to the user interface translation, please check out the [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu). We centralize translation work there, and periodically upstream translations.
If you want to contribute to the user interface translation project, please check out the [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu). We centralize translation work there, and periodically upstream translations.
### Building
## Building
* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)
* __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux)
## Download
### Support
We happily accept monetary donations, or donated games and hardware. Please see our [donations page](https://yuzu-emu.org/donate/) for more information on how you can contribute to yuzu. Any donations received will go towards things like:
You can download the latest releases automatically via the installer on our [downloads](https://yuzu-emu.org/downloads/) page.
## Support
If you enjoy the project and want to support us financially, check out our Patreon!
<a href="https://www.patreon.com/yuzuteam">
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
</a>
Any donations received will go towards things like:
* Switch consoles to explore and reverse-engineer the hardware
* Switch games for testing, reverse-engineering, and implementing new features
* Web hosting and infrastructure setup
* Software licenses (e.g. Visual Studio, IDA Pro, etc.)
* Additional hardware (e.g. GPUs as-needed to improve rendering support, other peripherals to add support for, etc.)
We also more than gladly accept used Switch consoles, preferably ones with firmware 3.0.0 or lower! If you would like to give yours away, don't hesitate to join our [Discord](https://discord.gg/VXqngT3) and talk to bunnei. You may also contact: donations@yuzu-emu.org.
If you wish to support us a different way, please join our [Discord](https://discord.gg/u77vRWY) and talk to bunnei. You may also contact: donations@yuzu-emu.org.
## License
yuzu is licensed under the GPLv2 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
+8 -3
View File
@@ -45,13 +45,18 @@ target_include_directories(microprofile INTERFACE ./microprofile)
add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# libusb
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
add_subdirectory(libusb)
endif()
# SDL2
if (NOT SDL2_FOUND AND ENABLE_SDL2)
if (NOT WIN32)
# Yuzu itself needs: Events Joystick Haptic Sensor Timers
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
set(SDL_UNUSED_SUBSYSTEMS
Atomic Audio Render Power Threads
Atomic Render Power Threads
File CPUinfo Filesystem Locale)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
@@ -110,7 +115,7 @@ if (ENABLE_WEB_SERVICE)
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
target_include_directories(httplib INTERFACE ./cpp-httplib)
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
if (WIN32)
+1 -1
Vendored Submodule
+1
Submodule externals/cpp-httplib added at 9648f950f5
-15
View File
@@ -1,15 +0,0 @@
From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
MIT License
===
cpp-httplib
A C++11 header-only HTTP library.
It's extremely easy to setup. Just include httplib.h file in your code!
Inspired by Sinatra and express.
© 2017 Yuji Hirose
-6714
View File
File diff suppressed because it is too large Load Diff
+47 -27
View File
@@ -1,10 +1,13 @@
if (MINGW)
# The MinGW toolchain for some reason doesn't work with this CMakeLists file after updating to
# 1.0.24, so we do it the old-fashioned way for now. We may want to move native Linux toolchains
# to here, too (TODO lat9nq?).
if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
# GNU toolchains for some reason doesn't work with the later half of this CMakeLists after
# updating to 1.0.24, so we do it the old-fashioned way for now.
set(LIBUSB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libusb")
set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb")
# Workarounds for MSYS/MinGW
if (MSYS)
# CMake on Windows passes `C:/`, but we need `/C/` or `/c/` to use `configure`
@@ -19,36 +22,42 @@ if (MINGW)
set(LIBUSB_CONFIGURE "${LIBUSB_SRC_DIR}/configure")
set(LIBUSB_MAKEFILE "${LIBUSB_PREFIX}/Makefile")
set(LIBUSB_LIBRARY "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll.a")
set(LIBUSB_SHARED_LIBRARY "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll")
set(LIBUSB_SHARED_LIBRARY_DEST "${CMAKE_BINARY_DIR}/bin/libusb-1.0.dll")
# Causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
# set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
if (MINGW)
set(LIBUSB_LIBRARIES "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll.a" CACHE PATH "libusb library path" FORCE)
set(LIBUSB_SHARED_LIBRARY "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll")
set(LIBUSB_SHARED_LIBRARY_DEST "${CMAKE_BINARY_DIR}/bin/libusb-1.0.dll")
set(LIBUSB_CONFIGURE_ARGS --host=x86_64-w64-mingw32 --build=x86_64-windows)
else()
set(LIBUSB_LIBRARIES "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.a" CACHE PATH "libusb library path" FORCE)
endif()
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
if (NOT MINGW)
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
endif()
make_directory("${LIBUSB_PREFIX}")
add_custom_command(
OUTPUT
"${LIBUSB_LIBRARY}"
"${LIBUSB_LIBRARIES}"
COMMAND
make
WORKING_DIRECTORY
"${LIBUSB_PREFIX}"
)
# We may use this path for other GNU toolchains, so put all of the MinGW-specific stuff here
if (MINGW)
set(LIBUSB_CONFIGURE_ARGS --host=x86_64-w64-mingw32 --build=x86_64-windows)
endif()
add_custom_command(
OUTPUT
"${LIBUSB_MAKEFILE}"
COMMAND
# /bin/env
# CFLAGS="${LIBUSB_CFLAGS}"
/bin/sh "${LIBUSB_CONFIGURE}"
env
CFLAGS="${LIBUSB_CFLAGS}"
sh "${LIBUSB_CONFIGURE}"
${LIBUSB_CONFIGURE_ARGS}
--srcdir="${LIBUSB_SRC_DIR}"
WORKING_DIRECTORY
@@ -59,7 +68,7 @@ if (MINGW)
OUTPUT
"${LIBUSB_CONFIGURE}"
COMMAND
/bin/sh "${LIBUSB_SRC_DIR}/bootstrap.sh"
sh "${LIBUSB_SRC_DIR}/bootstrap.sh"
WORKING_DIRECTORY
"${LIBUSB_SRC_DIR}"
)
@@ -68,19 +77,30 @@ if (MINGW)
OUTPUT
"${LIBUSB_SHARED_LIBRARY_DEST}"
COMMAND
/bin/cp "${LIBUSB_SHARED_LIBRARY}" "${LIBUSB_SHARED_LIBRARY_DEST}"
cp "${LIBUSB_SHARED_LIBRARY}" "${LIBUSB_SHARED_LIBRARY_DEST}"
)
add_custom_target(usb-bootstrap ALL DEPENDS "${LIBUSB_CONFIGURE}")
add_custom_target(usb-configure ALL DEPENDS "${LIBUSB_MAKEFILE}" usb-bootstrap)
add_custom_target(usb-build ALL DEPENDS "${LIBUSB_LIBRARY}" usb-configure)
add_custom_target(usb-bootstrap DEPENDS "${LIBUSB_CONFIGURE}")
add_custom_target(usb-configure DEPENDS "${LIBUSB_MAKEFILE}" usb-bootstrap)
add_custom_target(usb-build ALL DEPENDS "${LIBUSB_LIBRARIES}" usb-configure)
# Workaround since static linking didn't work out -- We need to copy the DLL to the bin directory
add_custom_target(usb-copy ALL DEPENDS "${LIBUSB_SHARED_LIBRARY_DEST}" usb-build)
# Make `usb` alias to LIBUSB_LIBRARY
add_library(usb INTERFACE)
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARY}")
else() # MINGW
add_dependencies(usb usb-copy)
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}")
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
Include(FindPkgConfig)
pkg_check_modules(LIBUDEV REQUIRED libudev)
if (LIBUDEV_FOUND)
target_include_directories(usb INTERFACE "${LIBUDEV_INCLUDE_DIRS}")
target_link_libraries(usb INTERFACE "${LIBUDEV_STATIC_LIBRARIES}")
endif()
endif()
else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# Ensure libusb compiles with UTF-8 encoding on MSVC
if(MSVC)
add_compile_options(/utf-8)
@@ -236,4 +256,4 @@ else() # MINGW
configure_file(config.h.in config.h)
endif() # MINGW
endif() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+5
View File
@@ -42,6 +42,7 @@ add_library(audio_core STATIC
voice_context.h
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
$<$<BOOL:${ENABLE_SDL2}>:sdl2_sink.cpp sdl2_sink.h>
)
create_target_directory_groups(audio_core)
@@ -71,3 +72,7 @@ if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb)
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
endif()
if(ENABLE_SDL2)
target_link_libraries(audio_core PRIVATE SDL2)
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
endif()
+163
View File
@@ -0,0 +1,163 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <atomic>
#include <cstring>
#include "audio_core/sdl2_sink.h"
#include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
//#include "common/settings.h"
// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
#include <SDL.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
namespace AudioCore {
class SDLSinkStream final : public SinkStream {
public:
SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
: num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} {
SDL_AudioSpec spec;
spec.freq = sample_rate;
spec.channels = static_cast<u8>(num_channels);
spec.format = AUDIO_S16SYS;
spec.samples = 4096;
spec.callback = nullptr;
SDL_AudioSpec obtained;
if (output_device.empty()) {
dev = SDL_OpenAudioDevice(nullptr, 0, &spec, &obtained, 0);
} else {
dev = SDL_OpenAudioDevice(output_device.c_str(), 0, &spec, &obtained, 0);
}
if (dev == 0) {
LOG_CRITICAL(Audio_Sink, "Error opening sdl audio device: {}", SDL_GetError());
return;
}
SDL_PauseAudioDevice(dev, 0);
}
~SDLSinkStream() override {
if (dev == 0) {
return;
}
SDL_CloseAudioDevice(dev);
}
void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
if (source_num_channels > num_channels) {
// Downsample 6 channels to 2
ASSERT_MSG(source_num_channels == 6, "Channel count must be 6");
std::vector<s16> buf;
buf.reserve(samples.size() * num_channels / source_num_channels);
for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
// Downmixing implementation taken from the ATSC standard
const s16 left{samples[i + 0]};
const s16 right{samples[i + 1]};
const s16 center{samples[i + 2]};
const s16 surround_left{samples[i + 4]};
const s16 surround_right{samples[i + 5]};
// Not used in the ATSC reference implementation
[[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]};
constexpr s32 clev{707}; // center mixing level coefficient
constexpr s32 slev{707}; // surround mixing level coefficient
buf.push_back(static_cast<s16>(left + (clev * center / 1000) +
(slev * surround_left / 1000)));
buf.push_back(static_cast<s16>(right + (clev * center / 1000) +
(slev * surround_right / 1000)));
}
int ret = SDL_QueueAudio(dev, static_cast<const void*>(buf.data()),
static_cast<u32>(buf.size() * sizeof(s16)));
if (ret < 0)
LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError());
return;
}
int ret = SDL_QueueAudio(dev, static_cast<const void*>(samples.data()),
static_cast<u32>(samples.size() * sizeof(s16)));
if (ret < 0)
LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError());
}
std::size_t SamplesInQueue(u32 channel_count) const override {
if (dev == 0)
return 0;
return SDL_GetQueuedAudioSize(dev) / (channel_count * sizeof(s16));
}
void Flush() override {
should_flush = true;
}
u32 GetNumChannels() const {
return num_channels;
}
private:
SDL_AudioDeviceID dev = 0;
u32 num_channels{};
std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
};
SDLSink::SDLSink(std::string_view target_device_name) {
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
return;
}
}
if (target_device_name != auto_device_name && !target_device_name.empty()) {
output_device = target_device_name;
} else {
output_device.clear();
}
}
SDLSink::~SDLSink() = default;
SinkStream& SDLSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, const std::string&) {
sink_streams.push_back(
std::make_unique<SDLSinkStream>(sample_rate, num_channels, output_device));
return *sink_streams.back();
}
std::vector<std::string> ListSDLSinkDevices() {
std::vector<std::string> device_list;
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
return {};
}
}
const int device_count = SDL_GetNumAudioDevices(0);
for (int i = 0; i < device_count; ++i) {
device_list.emplace_back(SDL_GetAudioDeviceName(i, 0));
}
return device_list;
}
} // namespace AudioCore
+29
View File
@@ -0,0 +1,29 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <vector>
#include "audio_core/sink.h"
namespace AudioCore {
class SDLSink final : public Sink {
public:
explicit SDLSink(std::string_view device_id);
~SDLSink() override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) override;
private:
std::string output_device;
std::vector<SinkStreamPtr> sink_streams;
};
std::vector<std::string> ListSDLSinkDevices();
} // namespace AudioCore
+2
View File
@@ -4,6 +4,8 @@
#pragma once
#include <array>
#include <vector>
#include "audio_core/common.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
+10
View File
@@ -11,6 +11,9 @@
#ifdef HAVE_CUBEB
#include "audio_core/cubeb_sink.h"
#endif
#ifdef HAVE_SDL2
#include "audio_core/sdl2_sink.h"
#endif
#include "common/logging/log.h"
namespace AudioCore {
@@ -35,6 +38,13 @@ constexpr SinkDetails sink_details[] = {
return std::make_unique<CubebSink>(device_id);
},
&ListCubebSinkDevices},
#endif
#ifdef HAVE_SDL2
SinkDetails{"sdl2",
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<SDLSink>(device_id);
},
&ListSDLSinkDevices},
#endif
SinkDetails{"null",
[](std::string_view device_id) -> std::unique_ptr<Sink> {
+5 -2
View File
@@ -107,9 +107,12 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
active_buffer = queued_buffers.front();
queued_buffers.pop();
VolumeAdjustSamples(active_buffer->GetSamples(), game_volume);
auto& samples = active_buffer->GetSamples();
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
VolumeAdjustSamples(samples, game_volume);
sink_stream.EnqueueSamples(GetNumChannels(), samples);
played_samples += samples.size();
const auto buffer_release_ns = GetBufferReleaseNS(*active_buffer);
+6
View File
@@ -89,6 +89,11 @@ public:
return sample_rate;
}
/// Gets the number of samples played so far
[[nodiscard]] u64 GetPlayedSampleCount() const {
return played_samples;
}
/// Gets the number of channels
[[nodiscard]] u32 GetNumChannels() const;
@@ -106,6 +111,7 @@ private:
[[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
u32 sample_rate; ///< Sample rate of the stream
u64 played_samples{}; ///< The current played sample count
Format format; ///< Format of the stream
float game_volume = 1.0f; ///< The volume the game currently has set
ReleaseCallback release_callback; ///< Buffer release callback for the stream
+4 -1
View File
@@ -110,7 +110,6 @@ add_library(common STATIC
cityhash.cpp
cityhash.h
common_funcs.h
common_sizes.h
common_types.h
concepts.h
div_ceil.h
@@ -131,7 +130,10 @@ add_library(common STATIC
hash.h
hex_util.cpp
hex_util.h
host_memory.cpp
host_memory.h
intrusive_red_black_tree.h
literals.h
logging/backend.cpp
logging/backend.h
logging/filter.cpp
@@ -139,6 +141,7 @@ add_library(common STATIC
logging/log.h
logging/text_formatter.cpp
logging/text_formatter.h
logging/types.h
lz4_compression.cpp
lz4_compression.h
math_util.h
-43
View File
@@ -1,43 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <limits>
#include "common/common_types.h"
namespace Common {
enum : u64 {
Size_1_KB = 0x400ULL,
Size_64_KB = 64ULL * Size_1_KB,
Size_128_KB = 128ULL * Size_1_KB,
Size_1_MB = 0x100000ULL,
Size_2_MB = 2ULL * Size_1_MB,
Size_4_MB = 4ULL * Size_1_MB,
Size_5_MB = 5ULL * Size_1_MB,
Size_14_MB = 14ULL * Size_1_MB,
Size_32_MB = 32ULL * Size_1_MB,
Size_33_MB = 33ULL * Size_1_MB,
Size_128_MB = 128ULL * Size_1_MB,
Size_448_MB = 448ULL * Size_1_MB,
Size_507_MB = 507ULL * Size_1_MB,
Size_562_MB = 562ULL * Size_1_MB,
Size_1554_MB = 1554ULL * Size_1_MB,
Size_2048_MB = 2048ULL * Size_1_MB,
Size_2193_MB = 2193ULL * Size_1_MB,
Size_3285_MB = 3285ULL * Size_1_MB,
Size_4916_MB = 4916ULL * Size_1_MB,
Size_1_GB = 0x40000000ULL,
Size_2_GB = 2ULL * Size_1_GB,
Size_4_GB = 4ULL * Size_1_GB,
Size_6_GB = 6ULL * Size_1_GB,
Size_8_GB = 8ULL * Size_1_GB,
Size_64_GB = 64ULL * Size_1_GB,
Size_512_GB = 512ULL * Size_1_GB,
Size_Invalid = std::numeric_limits<u64>::max(),
};
} // namespace Common
+2
View File
@@ -21,6 +21,8 @@ void DetachedTasks::WaitForAllTasks() {
}
DetachedTasks::~DetachedTasks() {
WaitForAllTasks();
std::unique_lock lock{mutex};
ASSERT(count == 0);
instance = nullptr;
+7 -7
View File
@@ -172,7 +172,7 @@ std::string ReadStringFromFile(const std::filesystem::path& path, FileType type)
size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
if (!IsFile(path)) {
if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -183,11 +183,7 @@ size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
if (!Exists(path)) {
return WriteStringToFile(path, type, string);
}
if (!IsFile(path)) {
if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -309,7 +305,11 @@ bool IOFile::Flush() const {
errno = 0;
const auto flush_result = std::fflush(file) == 0;
#ifdef _WIN32
const auto flush_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
#else
const auto flush_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
#endif
if (!flush_result) {
const auto ec = std::error_code{errno, std::generic_category()};
+8 -7
View File
@@ -49,7 +49,7 @@ void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::op
/**
* Reads an entire file at path and returns a string of the contents read from the file.
* If the filesystem object at path is not a file, this function returns an empty string.
* If the filesystem object at path is not a regular file, this function returns an empty string.
*
* @param path Filesystem path
* @param type File type
@@ -71,8 +71,9 @@ template <typename Path>
/**
* Writes a string to a file at path and returns the number of characters successfully written.
* If an file already exists at path, its contents will be erased.
* If the filesystem object at path is not a file, this function returns 0.
* If a file already exists at path, its contents will be erased.
* If a file does not exist at path, it creates and opens a new empty file for writing.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
@@ -95,8 +96,8 @@ template <typename Path>
/**
* Appends a string to a file at path and returns the number of characters successfully written.
* If a file does not exist at path, WriteStringToFile is called instead.
* If the filesystem object at path is not a file, this function returns 0.
* If a file does not exist at path, it creates and opens a new empty file for appending.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
@@ -395,11 +396,11 @@ public:
[[nodiscard]] size_t WriteString(std::span<const char> string) const;
/**
* Flushes any unwritten buffered data into the file.
* Attempts to flush any unwritten buffered data into the file and flush the file into the disk.
*
* @returns True if the flush was successful, false otherwise.
*/
[[nodiscard]] bool Flush() const;
bool Flush() const;
/**
* Resizes the file to a given size.
+19 -4
View File
@@ -135,8 +135,9 @@ std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, File
return nullptr;
}
if (!IsFile(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file",
if (Exists(path) && !IsFile(path)) {
LOG_ERROR(Common_Filesystem,
"Filesystem object at path={} exists and is not a regular file",
PathToUTF8String(path));
return nullptr;
}
@@ -321,7 +322,8 @@ bool RemoveDirContentsRecursively(const fs::path& path) {
std::error_code ec;
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
for (const auto& entry : fs::directory_iterator(path, ec)) {
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to completely enumerate the directory at path={}, ec_message={}",
@@ -337,6 +339,12 @@ bool RemoveDirContentsRecursively(const fs::path& path) {
PathToUTF8String(entry.path()), ec.message());
break;
}
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
if (entry.status().type() == fs::file_type::directory) {
return RemoveDirContentsRecursively(entry.path());
}
}
if (ec) {
@@ -475,7 +483,8 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
std::error_code ec;
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
for (const auto& entry : fs::directory_iterator(path, ec)) {
if (ec) {
break;
}
@@ -495,6 +504,12 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
break;
}
}
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
if (entry.status().type() == fs::file_type::directory) {
IterateDirEntriesRecursively(entry.path(), callback, filter);
}
}
if (callback_error || ec) {
+16 -14
View File
@@ -48,18 +48,18 @@ template <typename Path>
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a file
* - Filesystem object at path is not a regular file
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if file removal succeeds or file does not exist, false otherwise.
*/
[[nodiscard]] bool RemoveFile(const std::filesystem::path& path);
bool RemoveFile(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveFile(const Path& path) {
bool RemoveFile(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveFile(ToU8String(path));
} else {
@@ -74,7 +74,7 @@ template <typename Path>
* Failures occur when:
* - One or both input path(s) is not valid
* - Filesystem object at old_path does not exist
* - Filesystem object at old_path is not a file
* - Filesystem object at old_path is not a regular file
* - Filesystem object at new_path exists
* - Filesystem at either path is read only
*
@@ -110,8 +110,8 @@ template <typename Path1, typename Path2>
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a file
* - The file is not opened
* - Filesystem object at path exists and is not a regular file
* - The file is not open
*
* @param path Filesystem path
* @param mode File access mode
@@ -251,11 +251,11 @@ template <typename Path>
*
* @returns True if directory removal succeeds or directory does not exist, false otherwise.
*/
[[nodiscard]] bool RemoveDir(const std::filesystem::path& path);
bool RemoveDir(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveDir(const Path& path) {
bool RemoveDir(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDir(ToU8String(path));
} else {
@@ -276,11 +276,11 @@ template <typename Path>
*
* @returns True if the directory and all of its contents are removed successfully, false otherwise.
*/
[[nodiscard]] bool RemoveDirRecursively(const std::filesystem::path& path);
bool RemoveDirRecursively(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveDirRecursively(const Path& path) {
bool RemoveDirRecursively(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDirRecursively(ToU8String(path));
} else {
@@ -301,11 +301,11 @@ template <typename Path>
*
* @returns True if all of the directory's contents are removed successfully, false otherwise.
*/
[[nodiscard]] bool RemoveDirContentsRecursively(const std::filesystem::path& path);
bool RemoveDirContentsRecursively(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveDirContentsRecursively(const Path& path) {
bool RemoveDirContentsRecursively(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDirContentsRecursively(ToU8String(path));
} else {
@@ -435,11 +435,13 @@ template <typename Path>
#endif
/**
* Returns whether a filesystem object at path is a file.
* Returns whether a filesystem object at path is a regular file.
* A regular file is a file that stores text or binary data.
* It is not a directory, symlink, FIFO, socket, block device, or character device.
*
* @param path Filesystem path
*
* @returns True if a filesystem object at path is a file, false otherwise.
* @returns True if a filesystem object at path is a regular file, false otherwise.
*/
[[nodiscard]] bool IsFile(const std::filesystem::path& path);
+2 -1
View File
@@ -53,8 +53,9 @@ template <typename ContiguousContainer>
std::string out;
out.reserve(std::size(data) * pad_width);
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
for (const u8 c : data) {
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
out += fmt::format(format_str, c);
}
return out;
+538
View File
@@ -0,0 +1,538 @@
#ifdef _WIN32
#include <iterator>
#include <unordered_map>
#include <boost/icl/separate_interval_set.hpp>
#include <windows.h>
#include "common/dynamic_library.h"
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#endif // ^^^ Linux ^^^
#include <mutex>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/host_memory.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
namespace Common {
constexpr size_t PageAlignment = 0x1000;
constexpr size_t HugePageSize = 0x200000;
#ifdef _WIN32
// Manually imported for MinGW compatibility
#ifndef MEM_RESERVE_PLACEHOLDER
#define MEM_RESERVE_PLACEHOLDER 0x00040000
#endif
#ifndef MEM_REPLACE_PLACEHOLDER
#define MEM_REPLACE_PLACEHOLDER 0x00004000
#endif
#ifndef MEM_COALESCE_PLACEHOLDERS
#define MEM_COALESCE_PLACEHOLDERS 0x00000001
#endif
#ifndef MEM_PRESERVE_PLACEHOLDER
#define MEM_PRESERVE_PLACEHOLDER 0x00000002
#endif
using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)(
_In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess,
_In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize,
_In_opt_ PCWSTR Name,
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
_In_ ULONG ParameterCount);
using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)(
_In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size,
_In_ ULONG AllocationType, _In_ ULONG PageProtection,
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
_In_ ULONG ParameterCount);
using PFN_MapViewOfFile3 = _Ret_maybenull_ PVOID(WINAPI*)(
_In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress,
_In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection,
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
_In_ ULONG ParameterCount);
using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress,
_In_ ULONG UnmapFlags);
template <typename T>
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
if (!dll.GetSymbol(name, &pfn)) {
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
throw std::bad_alloc{};
}
}
class HostMemory::Impl {
public:
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
kernelbase_dll("Kernelbase") {
if (!kernelbase_dll.IsOpen()) {
LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
throw std::bad_alloc{};
}
GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
// Allocate backing file map
backing_handle =
pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
if (!backing_handle) {
LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
backing_size >> 20);
throw std::bad_alloc{};
}
// Allocate a virtual memory for the backing file map as placeholder
backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0));
if (!backing_base) {
Release();
LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
backing_size >> 20);
throw std::bad_alloc{};
}
// Map backing placeholder
void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
if (ret != backing_base) {
Release();
LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
throw std::bad_alloc{};
}
// Allocate virtual address placeholder
virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0));
if (!virtual_base) {
Release();
LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
virtual_size >> 30);
throw std::bad_alloc{};
}
}
~Impl() {
Release();
}
void Map(size_t virtual_offset, size_t host_offset, size_t length) {
std::unique_lock lock{placeholder_mutex};
if (!IsNiechePlaceholder(virtual_offset, length)) {
Split(virtual_offset, length);
}
ASSERT(placeholders.find({virtual_offset, virtual_offset + length}) == placeholders.end());
TrackPlaceholder(virtual_offset, host_offset, length);
MapView(virtual_offset, host_offset, length);
}
void Unmap(size_t virtual_offset, size_t length) {
std::lock_guard lock{placeholder_mutex};
// Unmap until there are no more placeholders
while (UnmapOnePlaceholder(virtual_offset, length)) {
}
}
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
DWORD new_flags{};
if (read && write) {
new_flags = PAGE_READWRITE;
} else if (read && !write) {
new_flags = PAGE_READONLY;
} else if (!read && !write) {
new_flags = PAGE_NOACCESS;
} else {
UNIMPLEMENTED_MSG("Protection flag combination read={} write={}", read, write);
}
const size_t virtual_end = virtual_offset + length;
std::lock_guard lock{placeholder_mutex};
auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
while (it != end) {
const size_t offset = std::max(it->lower(), virtual_offset);
const size_t protect_length = std::min(it->upper(), virtual_end) - offset;
DWORD old_flags{};
if (!VirtualProtect(virtual_base + offset, protect_length, new_flags, &old_flags)) {
LOG_CRITICAL(HW_Memory, "Failed to change virtual memory protect rules");
}
++it;
}
}
const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
u8* backing_base{};
u8* virtual_base{};
private:
/// Release all resources in the object
void Release() {
if (!placeholders.empty()) {
for (const auto& placeholder : placeholders) {
if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
MEM_PRESERVE_PLACEHOLDER)) {
LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
}
}
Coalesce(0, virtual_size);
}
if (virtual_base) {
if (!VirtualFree(virtual_base, 0, MEM_RELEASE)) {
LOG_CRITICAL(HW_Memory, "Failed to free virtual memory");
}
}
if (backing_base) {
if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
}
if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
LOG_CRITICAL(HW_Memory, "Failed to free backing memory");
}
}
if (!CloseHandle(backing_handle)) {
LOG_CRITICAL(HW_Memory, "Failed to free backing memory file handle");
}
}
/// Unmap one placeholder in the given range (partial unmaps are supported)
/// Return true when there are no more placeholders to unmap
bool UnmapOnePlaceholder(size_t virtual_offset, size_t length) {
const auto it = placeholders.find({virtual_offset, virtual_offset + length});
const auto begin = placeholders.begin();
const auto end = placeholders.end();
if (it == end) {
return false;
}
const size_t placeholder_begin = it->lower();
const size_t placeholder_end = it->upper();
const size_t unmap_begin = std::max(virtual_offset, placeholder_begin);
const size_t unmap_end = std::min(virtual_offset + length, placeholder_end);
ASSERT(unmap_begin >= placeholder_begin && unmap_begin < placeholder_end);
ASSERT(unmap_end <= placeholder_end && unmap_end > placeholder_begin);
const auto host_pointer_it = placeholder_host_pointers.find(placeholder_begin);
ASSERT(host_pointer_it != placeholder_host_pointers.end());
const size_t host_offset = host_pointer_it->second;
const bool split_left = unmap_begin > placeholder_begin;
const bool split_right = unmap_end < placeholder_end;
if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
MEM_PRESERVE_PLACEHOLDER)) {
LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
}
// If we have to remap memory regions due to partial unmaps, we are in a data race as
// Windows doesn't support remapping memory without unmapping first. Avoid adding any extra
// logic within the panic region described below.
// Panic region, we are in a data race right now
if (split_left || split_right) {
Split(unmap_begin, unmap_end - unmap_begin);
}
if (split_left) {
MapView(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
}
if (split_right) {
MapView(unmap_end, host_offset + unmap_end - placeholder_begin,
placeholder_end - unmap_end);
}
// End panic region
size_t coalesce_begin = unmap_begin;
if (!split_left) {
// Try to coalesce pages to the left
coalesce_begin = it == begin ? 0 : std::prev(it)->upper();
if (coalesce_begin != placeholder_begin) {
Coalesce(coalesce_begin, unmap_end - coalesce_begin);
}
}
if (!split_right) {
// Try to coalesce pages to the right
const auto next = std::next(it);
const size_t next_begin = next == end ? virtual_size : next->lower();
if (placeholder_end != next_begin) {
// We can coalesce to the right
Coalesce(coalesce_begin, next_begin - coalesce_begin);
}
}
// Remove and reinsert placeholder trackers
UntrackPlaceholder(it);
if (split_left) {
TrackPlaceholder(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
}
if (split_right) {
TrackPlaceholder(unmap_end, host_offset + unmap_end - placeholder_begin,
placeholder_end - unmap_end);
}
return true;
}
void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
}
}
void Split(size_t virtual_offset, size_t length) {
if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
LOG_CRITICAL(HW_Memory, "Failed to split placeholder");
}
}
void Coalesce(size_t virtual_offset, size_t length) {
if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
LOG_CRITICAL(HW_Memory, "Failed to coalesce placeholders");
}
}
void TrackPlaceholder(size_t virtual_offset, size_t host_offset, size_t length) {
placeholders.insert({virtual_offset, virtual_offset + length});
placeholder_host_pointers.emplace(virtual_offset, host_offset);
}
void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) {
placeholders.erase(it);
placeholder_host_pointers.erase(it->lower());
}
/// Return true when a given memory region is a "nieche" and the placeholders don't have to be
/// splitted.
bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
if (it != placeholders.end() && it->lower() == virtual_offset + length) {
const bool is_root = it == placeholders.begin() && virtual_offset == 0;
return is_root || std::prev(it)->upper() == virtual_offset;
}
return false;
}
HANDLE process{}; ///< Current process handle
HANDLE backing_handle{}; ///< File based backing memory
DynamicLibrary kernelbase_dll;
PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
std::mutex placeholder_mutex; ///< Mutex for placeholders
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
};
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
class HostMemory::Impl {
public:
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_} {
bool good = false;
SCOPE_EXIT({
if (!good) {
Release();
}
});
// Backing memory initialization
fd = memfd_create("HostMemory", 0);
if (fd == -1) {
LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
throw std::bad_alloc{};
}
// Defined to extend the file with zeros
int ret = ftruncate(fd, backing_size);
if (ret != 0) {
LOG_CRITICAL(HW_Memory, "ftruncate failed with {}, are you out-of-memory?",
strerror(errno));
throw std::bad_alloc{};
}
backing_base = static_cast<u8*>(
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
if (backing_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
throw std::bad_alloc{};
}
// Virtual memory initialization
virtual_base = static_cast<u8*>(
mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
throw std::bad_alloc{};
}
good = true;
}
~Impl() {
Release();
}
void Map(size_t virtual_offset, size_t host_offset, size_t length) {
void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, fd, host_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
}
void Unmap(size_t virtual_offset, size_t length) {
// The method name is wrong. We're still talking about the virtual range.
// We don't want to unmap, we want to reserve this memory.
void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
}
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
int flags = 0;
if (read) {
flags |= PROT_READ;
}
if (write) {
flags |= PROT_WRITE;
}
int ret = mprotect(virtual_base + virtual_offset, length, flags);
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
}
const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
private:
/// Release all resources in the object
void Release() {
if (virtual_base != MAP_FAILED) {
int ret = munmap(virtual_base, virtual_size);
ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
}
if (backing_base != MAP_FAILED) {
int ret = munmap(backing_base, backing_size);
ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
}
if (fd != -1) {
int ret = close(fd);
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
}
}
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
};
#else // ^^^ Linux ^^^ vvv Generic vvv
class HostMemory::Impl {
public:
explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
// This is just a place holder.
// Please implement fastmem in a propper way on your platform.
throw std::bad_alloc{};
}
void Map(size_t virtual_offset, size_t host_offset, size_t length) {}
void Unmap(size_t virtual_offset, size_t length) {}
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {}
u8* backing_base{nullptr};
u8* virtual_base{nullptr};
};
#endif // ^^^ Generic ^^^
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
: backing_size(backing_size_), virtual_size(virtual_size_) {
try {
// Try to allocate a fastmem arena.
// The implementation will fail with std::bad_alloc on errors.
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
AlignUp(virtual_size, PageAlignment) +
3 * HugePageSize);
backing_base = impl->backing_base;
virtual_base = impl->virtual_base;
if (virtual_base) {
virtual_base += 2 * HugePageSize - 1;
virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
virtual_base_offset = virtual_base - impl->virtual_base;
}
} catch (const std::bad_alloc&) {
LOG_CRITICAL(HW_Memory,
"Fastmem unavailable, falling back to VirtualBuffer for memory allocation");
fallback_buffer = std::make_unique<Common::VirtualBuffer<u8>>(backing_size);
backing_base = fallback_buffer->data();
virtual_base = nullptr;
}
}
HostMemory::~HostMemory() = default;
HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size);
ASSERT(host_offset + length <= backing_size);
if (length == 0 || !virtual_base || !impl) {
return;
}
impl->Map(virtual_offset + virtual_base_offset, host_offset, length);
}
void HostMemory::Unmap(size_t virtual_offset, size_t length) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) {
return;
}
impl->Unmap(virtual_offset + virtual_base_offset, length);
}
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) {
return;
}
impl->Protect(virtual_offset + virtual_base_offset, length, read, write);
}
} // namespace Common
+70
View File
@@ -0,0 +1,70 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "common/virtual_buffer.h"
namespace Common {
/**
* A low level linear memory buffer, which supports multiple mappings
* Its purpose is to rebuild a given sparse memory layout, including mirrors.
*/
class HostMemory {
public:
explicit HostMemory(size_t backing_size_, size_t virtual_size_);
~HostMemory();
/**
* Copy constructors. They shall return a copy of the buffer without the mappings.
* TODO: Implement them with COW if needed.
*/
HostMemory(const HostMemory& other) = delete;
HostMemory& operator=(const HostMemory& other) = delete;
/**
* Move constructors. They will move the buffer and the mappings to the new object.
*/
HostMemory(HostMemory&& other) noexcept;
HostMemory& operator=(HostMemory&& other) noexcept;
void Map(size_t virtual_offset, size_t host_offset, size_t length);
void Unmap(size_t virtual_offset, size_t length);
void Protect(size_t virtual_offset, size_t length, bool read, bool write);
[[nodiscard]] u8* BackingBasePointer() noexcept {
return backing_base;
}
[[nodiscard]] const u8* BackingBasePointer() const noexcept {
return backing_base;
}
[[nodiscard]] u8* VirtualBasePointer() noexcept {
return virtual_base;
}
[[nodiscard]] const u8* VirtualBasePointer() const noexcept {
return virtual_base;
}
private:
size_t backing_size{};
size_t virtual_size{};
// Low level handler for the platform dependent memory routines
class Impl;
std::unique_ptr<Impl> impl;
u8* backing_base{};
u8* virtual_base{};
size_t virtual_base_offset{};
// Fallback if fastmem is not supported on this platform
std::unique_ptr<Common::VirtualBuffer<u8>> fallback_buffer;
};
} // namespace Common
+31
View File
@@ -0,0 +1,31 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Common::Literals {
constexpr u64 operator""_KiB(unsigned long long int x) {
return 1024ULL * x;
}
constexpr u64 operator""_MiB(unsigned long long int x) {
return 1024_KiB * x;
}
constexpr u64 operator""_GiB(unsigned long long int x) {
return 1024_MiB * x;
}
constexpr u64 operator""_TiB(unsigned long long int x) {
return 1024_GiB * x;
}
constexpr u64 operator""_PiB(unsigned long long int x) {
return 1024_TiB * x;
}
} // namespace Common::Literals
+22 -138
View File
@@ -17,7 +17,10 @@
#endif
#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/literals.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
@@ -97,8 +100,8 @@ private:
write_logs(entry);
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
// where a system is repeatedly spamming logs even on close.
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
int logs_written = 0;
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
@@ -140,10 +143,14 @@ private:
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
};
ConsoleBackend::~ConsoleBackend() = default;
void ConsoleBackend::Write(const Entry& entry) {
PrintMessage(entry);
}
ColorConsoleBackend::~ColorConsoleBackend() = default;
void ColorConsoleBackend::Write(const Entry& entry) {
PrintColoredMessage(entry);
}
@@ -154,19 +161,23 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
void(FS::RemoveFile(old_filename));
FS::RemoveFile(old_filename);
void(FS::RenameFile(filename, old_filename));
file = FS::IOFile(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
file =
std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
}
FileBackend::~FileBackend() = default;
void FileBackend::Write(const Entry& entry) {
using namespace Common::Literals;
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
// know)
constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
if (!file.IsOpen()) {
if (!file->IsOpen()) {
return;
}
@@ -176,147 +187,20 @@ void FileBackend::Write(const Entry& entry) {
return;
}
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
void(file.Flush());
file->Flush();
}
}
DebuggerBackend::~DebuggerBackend() = default;
void DebuggerBackend::Write(const Entry& entry) {
#ifdef _WIN32
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
#endif
}
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \
CLS(Log) \
CLS(Common) \
SUB(Common, Filesystem) \
SUB(Common, Memory) \
CLS(Core) \
SUB(Core, ARM) \
SUB(Core, Timing) \
CLS(Config) \
CLS(Debug) \
SUB(Debug, Emulated) \
SUB(Debug, GPU) \
SUB(Debug, Breakpoint) \
SUB(Debug, GDBStub) \
CLS(Kernel) \
SUB(Kernel, SVC) \
CLS(Service) \
SUB(Service, ACC) \
SUB(Service, Audio) \
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BGTC) \
SUB(Service, BTDRV) \
SUB(Service, BTM) \
SUB(Service, Capture) \
SUB(Service, ERPT) \
SUB(Service, ETicket) \
SUB(Service, EUPLD) \
SUB(Service, Fatal) \
SUB(Service, FGM) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, IRS) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \
SUB(Service, LM) \
SUB(Service, Migration) \
SUB(Service, Mii) \
SUB(Service, MM) \
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, OLSC) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
SUB(Service, PM) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, PSM) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, TCAP) \
SUB(Service, Time) \
SUB(Service, USB) \
SUB(Service, VI) \
SUB(Service, WLAN) \
CLS(HW) \
SUB(HW, Memory) \
SUB(HW, LCD) \
SUB(HW, GPU) \
SUB(HW, AES) \
CLS(IPC) \
CLS(Frontend) \
CLS(Render) \
SUB(Render, Software) \
SUB(Render, OpenGL) \
SUB(Render, Vulkan) \
CLS(Audio) \
SUB(Audio, DSP) \
SUB(Audio, Sink) \
CLS(Input) \
CLS(Network) \
CLS(Loader) \
CLS(CheatEngine) \
CLS(Crypto) \
CLS(WebService)
// GetClassName is a macro defined by Windows.h, grrr...
const char* GetLogClassName(Class log_class) {
switch (log_class) {
#define CLS(x) \
case Class::x: \
return #x;
#define SUB(x, y) \
case Class::x##_##y: \
return #x "." #y;
ALL_LOG_CLASSES()
#undef CLS
#undef SUB
case Class::Count:
break;
}
return "Invalid";
}
const char* GetLevelName(Level log_level) {
#define LVL(x) \
case Level::x: \
return #x
switch (log_level) {
LVL(Trace);
LVL(Debug);
LVL(Info);
LVL(Warning);
LVL(Error);
LVL(Critical);
case Level::Count:
break;
}
#undef LVL
return "Invalid";
}
void SetGlobalFilter(const Filter& filter) {
Impl::Instance().SetGlobalFilter(filter);
}
+14 -29
View File
@@ -1,36 +1,24 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <chrono>
#include <filesystem>
#include <memory>
#include <string>
#include <string_view>
#include "common/fs/file.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
namespace Common::FS {
class IOFile;
}
namespace Common::Log {
class Filter;
/**
* A log entry. Log entries are store in a structured format to permit more varied output
* formatting on different frontends, as well as facilitating filtering and aggregation.
*/
struct Entry {
std::chrono::microseconds timestamp;
Class log_class{};
Level log_level{};
const char* filename = nullptr;
unsigned int line_num = 0;
std::string function;
std::string message;
bool final_entry = false;
};
/**
* Interface for logging backends. As loggers can be created and removed at runtime, this can be
* used by a frontend for adding a custom logging backend as needed
@@ -38,6 +26,7 @@ struct Entry {
class Backend {
public:
virtual ~Backend() = default;
virtual void SetFilter(const Filter& new_filter) {
filter = new_filter;
}
@@ -53,6 +42,8 @@ private:
*/
class ConsoleBackend : public Backend {
public:
~ConsoleBackend() override;
static const char* Name() {
return "console";
}
@@ -67,6 +58,8 @@ public:
*/
class ColorConsoleBackend : public Backend {
public:
~ColorConsoleBackend() override;
static const char* Name() {
return "color_console";
}
@@ -83,6 +76,7 @@ public:
class FileBackend : public Backend {
public:
explicit FileBackend(const std::filesystem::path& filename);
~FileBackend() override;
static const char* Name() {
return "file";
@@ -95,7 +89,7 @@ public:
void Write(const Entry& entry) override;
private:
FS::IOFile file;
std::unique_ptr<FS::IOFile> file;
std::size_t bytes_written = 0;
};
@@ -104,6 +98,8 @@ private:
*/
class DebuggerBackend : public Backend {
public:
~DebuggerBackend() override;
static const char* Name() {
return "debugger";
}
@@ -119,17 +115,6 @@ void RemoveBackend(std::string_view backend_name);
Backend* GetBackend(std::string_view backend_name);
/**
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
* instead of underscores as in the enumeration.
*/
const char* GetLogClassName(Class log_class);
/**
* Returns the name of the passed log level as a C-string.
*/
const char* GetLevelName(Level log_level);
/**
* The global filter will prevent any messages from even being processed if they are filtered. Each
* backend can have a filter, but if the level is lower than the global filter, the backend will
+130 -2
View File
@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <algorithm>
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/string_util.h"
@@ -22,7 +21,7 @@ Level GetLevelByName(const It begin, const It end) {
template <typename It>
Class GetClassByName(const It begin, const It end) {
for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
for (u8 i = 0; i < static_cast<u8>(Class::Count); ++i) {
const char* level_name = GetLogClassName(static_cast<Class>(i));
if (Common::ComparePartialString(begin, end, level_name)) {
return static_cast<Class>(i);
@@ -62,6 +61,135 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
}
} // Anonymous namespace
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \
CLS(Log) \
CLS(Common) \
SUB(Common, Filesystem) \
SUB(Common, Memory) \
CLS(Core) \
SUB(Core, ARM) \
SUB(Core, Timing) \
CLS(Config) \
CLS(Debug) \
SUB(Debug, Emulated) \
SUB(Debug, GPU) \
SUB(Debug, Breakpoint) \
SUB(Debug, GDBStub) \
CLS(Kernel) \
SUB(Kernel, SVC) \
CLS(Service) \
SUB(Service, ACC) \
SUB(Service, Audio) \
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BGTC) \
SUB(Service, BTDRV) \
SUB(Service, BTM) \
SUB(Service, Capture) \
SUB(Service, ERPT) \
SUB(Service, ETicket) \
SUB(Service, EUPLD) \
SUB(Service, Fatal) \
SUB(Service, FGM) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, IRS) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \
SUB(Service, LM) \
SUB(Service, Migration) \
SUB(Service, Mii) \
SUB(Service, MM) \
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, OLSC) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
SUB(Service, PM) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, PSM) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, TCAP) \
SUB(Service, Time) \
SUB(Service, USB) \
SUB(Service, VI) \
SUB(Service, WLAN) \
CLS(HW) \
SUB(HW, Memory) \
SUB(HW, LCD) \
SUB(HW, GPU) \
SUB(HW, AES) \
CLS(IPC) \
CLS(Frontend) \
CLS(Render) \
SUB(Render, Software) \
SUB(Render, OpenGL) \
SUB(Render, Vulkan) \
CLS(Audio) \
SUB(Audio, DSP) \
SUB(Audio, Sink) \
CLS(Input) \
CLS(Network) \
CLS(Loader) \
CLS(CheatEngine) \
CLS(Crypto) \
CLS(WebService)
// GetClassName is a macro defined by Windows.h, grrr...
const char* GetLogClassName(Class log_class) {
switch (log_class) {
#define CLS(x) \
case Class::x: \
return #x;
#define SUB(x, y) \
case Class::x##_##y: \
return #x "." #y;
ALL_LOG_CLASSES()
#undef CLS
#undef SUB
case Class::Count:
break;
}
return "Invalid";
}
const char* GetLevelName(Level log_level) {
#define LVL(x) \
case Level::x: \
return #x
switch (log_level) {
LVL(Trace);
LVL(Debug);
LVL(Info);
LVL(Warning);
LVL(Error);
LVL(Critical);
case Level::Count:
break;
}
#undef LVL
return "Invalid";
}
Filter::Filter(Level default_level) {
ResetAll(default_level);
}
+12
View File
@@ -5,12 +5,24 @@
#pragma once
#include <array>
#include <chrono>
#include <cstddef>
#include <string_view>
#include "common/logging/log.h"
namespace Common::Log {
/**
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
* instead of underscores as in the enumeration.
*/
const char* GetLogClassName(Class log_class);
/**
* Returns the name of the passed log level as a C-string.
*/
const char* GetLevelName(Level log_level);
/**
* Implements a log message filter which allows different log classes to have different minimum
* severity levels. The filter can be changed at runtime and can be parsed from a string to allow
+1 -119
View File
@@ -5,7 +5,7 @@
#pragma once
#include <fmt/format.h>
#include "common/common_types.h"
#include "common/logging/types.h"
namespace Common::Log {
@@ -18,124 +18,6 @@ constexpr const char* TrimSourcePath(std::string_view source) {
return source.data() + idx;
}
/// Specifies the severity or level of detail of the log message.
enum class Level : u8 {
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
///< pollute logs.
Debug, ///< Less detailed debugging information.
Info, ///< Status information from important points during execution.
Warning, ///< Minor or potential problems found during execution of a task.
Error, ///< Major problems found during execution of a task that prevent it from being
///< completed.
Critical, ///< Major problems during execution that threaten the stability of the entire
///< application.
Count ///< Total number of logging levels
};
typedef u8 ClassType;
/**
* Specifies the sub-system that generated the log message.
*
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in
* backend.cpp.
*/
enum class Class : ClassType {
Log, ///< Messages about the log system itself
Common, ///< Library routines
Common_Filesystem, ///< Filesystem interface library
Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core
Core_ARM, ///< ARM CPU core
Core_Timing, ///< CoreTiming functions
Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools
Debug_Emulated, ///< Debug messages from the emulated programs
Debug_GPU, ///< GPU debugging tools
Debug_Breakpoint, ///< Logging breakpoints and watchpoints
Debug_GDBStub, ///< GDB Stub
Kernel, ///< The HLE implementation of the CTR kernel
Kernel_SVC, ///< Kernel system calls
Service, ///< HLE implementation of system services. Each major service
///< should have its own subclass.
Service_ACC, ///< The ACC (Accounts) service
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_ARP, ///< The ARP service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BGTC, ///< The BGTC (Background Task Controller) service
Service_BPC, ///< The BPC service
Service_BTDRV, ///< The Bluetooth driver service
Service_BTM, ///< The BTM service
Service_Capture, ///< The capture service
Service_ERPT, ///< The error reporting service
Service_ETicket, ///< The ETicket service
Service_EUPLD, ///< The error upload service
Service_Fatal, ///< The Fatal service
Service_FGM, ///< The FGM service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_IRS, ///< The IRS service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service
Service_LM, ///< The LM (Logger) service
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
Service_MM, ///< The MM (Multimedia) service
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_OLSC, ///< The OLSC service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
Service_PM, ///< The PM service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_PSM, ///< The PSM service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_TCAP, ///< The TCAP service.
Service_Time, ///< The time service
Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service
Service_WLAN, ///< The WLAN (Wireless local area network) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
HW_LCD, ///< LCD register emulation
HW_GPU, ///< GPU control emulation
HW_AES, ///< AES engine emulation
IPC, ///< IPC interface
Frontend, ///< Emulator UI
Render, ///< Emulator video output and hardware acceleration
Render_Software, ///< Software renderer backend
Render_OpenGL, ///< OpenGL backend
Render_Vulkan, ///< Vulkan backend
Audio, ///< Audio emulation
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
CheatEngine, ///< Memory manipulation and engine VM functions
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
Network, ///< Network emulation
WebService, ///< Interface to yuzu Web Services
Count ///< Total number of logging classes
};
/// Logs a message to the global logger, using fmt
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
+1 -1
View File
@@ -11,7 +11,7 @@
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
+144
View File
@@ -0,0 +1,144 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <chrono>
#include "common/common_types.h"
namespace Common::Log {
/// Specifies the severity or level of detail of the log message.
enum class Level : u8 {
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
///< pollute logs.
Debug, ///< Less detailed debugging information.
Info, ///< Status information from important points during execution.
Warning, ///< Minor or potential problems found during execution of a task.
Error, ///< Major problems found during execution of a task that prevent it from being
///< completed.
Critical, ///< Major problems during execution that threaten the stability of the entire
///< application.
Count ///< Total number of logging levels
};
/**
* Specifies the sub-system that generated the log message.
*
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in
* filter.cpp.
*/
enum class Class : u8 {
Log, ///< Messages about the log system itself
Common, ///< Library routines
Common_Filesystem, ///< Filesystem interface library
Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core
Core_ARM, ///< ARM CPU core
Core_Timing, ///< CoreTiming functions
Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools
Debug_Emulated, ///< Debug messages from the emulated programs
Debug_GPU, ///< GPU debugging tools
Debug_Breakpoint, ///< Logging breakpoints and watchpoints
Debug_GDBStub, ///< GDB Stub
Kernel, ///< The HLE implementation of the CTR kernel
Kernel_SVC, ///< Kernel system calls
Service, ///< HLE implementation of system services. Each major service
///< should have its own subclass.
Service_ACC, ///< The ACC (Accounts) service
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_ARP, ///< The ARP service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BGTC, ///< The BGTC (Background Task Controller) service
Service_BPC, ///< The BPC service
Service_BTDRV, ///< The Bluetooth driver service
Service_BTM, ///< The BTM service
Service_Capture, ///< The capture service
Service_ERPT, ///< The error reporting service
Service_ETicket, ///< The ETicket service
Service_EUPLD, ///< The error upload service
Service_Fatal, ///< The Fatal service
Service_FGM, ///< The FGM service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_IRS, ///< The IRS service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service
Service_LM, ///< The LM (Logger) service
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
Service_MM, ///< The MM (Multimedia) service
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_OLSC, ///< The OLSC service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
Service_PM, ///< The PM service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_PSM, ///< The PSM service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_TCAP, ///< The TCAP service.
Service_Time, ///< The time service
Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service
Service_WLAN, ///< The WLAN (Wireless local area network) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
HW_LCD, ///< LCD register emulation
HW_GPU, ///< GPU control emulation
HW_AES, ///< AES engine emulation
IPC, ///< IPC interface
Frontend, ///< Emulator UI
Render, ///< Emulator video output and hardware acceleration
Render_Software, ///< Software renderer backend
Render_OpenGL, ///< OpenGL backend
Render_Vulkan, ///< Vulkan backend
Audio, ///< Audio emulation
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
CheatEngine, ///< Memory manipulation and engine VM functions
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
Network, ///< Network emulation
WebService, ///< Interface to yuzu Web Services
Count ///< Total number of logging classes
};
/**
* A log entry. Log entries are store in a structured format to permit more varied output
* formatting on different frontends, as well as facilitating filtering and aggregation.
*/
struct Entry {
std::chrono::microseconds timestamp;
Class log_class{};
Level log_level{};
const char* filename = nullptr;
unsigned int line_num = 0;
std::string function;
std::string message;
bool final_entry = false;
};
} // namespace Common::Log
+2
View File
@@ -111,6 +111,8 @@ struct PageTable {
VirtualBuffer<u64> backing_addr;
size_t current_address_space_width_in_bits;
u8* fastmem_arena;
};
} // namespace Common
+13
View File
@@ -55,9 +55,11 @@ void LogSettings() {
log_setting("Renderer_UseAsynchronousGpuEmulation",
values.use_asynchronous_gpu_emulation.GetValue());
log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id);
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
@@ -90,6 +92,13 @@ bool IsGPULevelHigh() {
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
}
bool IsFastmemEnabled() {
if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) {
return values.cpuopt_fastmem;
}
return true;
}
float Volume() {
if (values.audio_muted) {
return 0.0f;
@@ -114,7 +123,9 @@ void RestoreGlobalState(bool is_powered_on) {
values.cpu_accuracy.SetGlobal(true);
values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
// Renderer
values.renderer_backend.SetGlobal(true);
@@ -127,10 +138,12 @@ void RestoreGlobalState(bool is_powered_on) {
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
values.use_nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.use_assembly_shaders.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.use_caches_gc.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
+9
View File
@@ -125,10 +125,13 @@ struct Values {
bool cpuopt_const_prop;
bool cpuopt_misc_ir;
bool cpuopt_reduce_misalign_checks;
bool cpuopt_fastmem;
Setting<bool> cpuopt_unsafe_unfuse_fma;
Setting<bool> cpuopt_unsafe_reduce_fp_error;
Setting<bool> cpuopt_unsafe_ignore_standard_fpcr;
Setting<bool> cpuopt_unsafe_inaccurate_nan;
Setting<bool> cpuopt_unsafe_fastmem_check;
// Renderer
Setting<RendererBackend> renderer_backend;
@@ -145,10 +148,13 @@ struct Values {
Setting<GPUAccuracy> gpu_accuracy;
Setting<bool> use_asynchronous_gpu_emulation;
Setting<bool> use_nvdec_emulation;
Setting<bool> accelerate_astc;
Setting<bool> use_vsync;
Setting<bool> disable_fps_limit;
Setting<bool> use_assembly_shaders;
Setting<bool> use_asynchronous_shaders;
Setting<bool> use_fast_gpu_time;
Setting<bool> use_caches_gc;
Setting<float> bg_red;
Setting<float> bg_green;
@@ -216,6 +222,7 @@ struct Values {
std::string program_args;
bool dump_exefs;
bool dump_nso;
bool enable_fs_access_log;
bool reporting_services;
bool quest_flag;
bool disable_macro_jit;
@@ -249,6 +256,8 @@ void SetConfiguringGlobal(bool is_global);
bool IsGPULevelExtreme();
bool IsGPULevelHigh();
bool IsFastmemEnabled();
float Volume();
std::string GetTimeZoneString();
+3
View File
@@ -139,6 +139,7 @@ add_library(core STATIC
frontend/input.h
hardware_interrupt_manager.cpp
hardware_interrupt_manager.h
hle/api_version.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -550,6 +551,8 @@ add_library(core STATIC
hle/service/spl/module.h
hle/service/spl/spl.cpp
hle/service/spl/spl.h
hle/service/spl/spl_results.h
hle/service/spl/spl_types.h
hle/service/ssl/ssl.cpp
hle/service/ssl/ssl.h
hle/service/time/clock_types.h
+12 -2
View File
@@ -8,6 +8,7 @@
#include <dynarmic/interface/A32/config.h>
#include <dynarmic/interface/A32/context.h>
#include "common/assert.h"
#include "common/literals.h"
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
@@ -22,6 +23,8 @@
namespace Core {
using namespace Common::Literals;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -128,6 +131,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
page_table->pointers.data());
config.fastmem_pointer = page_table->fastmem_arena;
}
config.absolute_offset_page_table = true;
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
@@ -142,8 +146,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
config.wall_clock_cntpct = uses_wall_clock;
// Code cache size
config.code_cache_size = 512 * 1024 * 1024;
config.far_code_offset = 256 * 1024 * 1024;
config.code_cache_size = 512_MiB;
config.far_code_offset = 400_MiB;
// Safe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
@@ -171,6 +175,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_reduce_misalign_checks) {
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
}
}
// Unsafe optimizations
@@ -182,6 +189,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
+14 -2
View File
@@ -7,6 +7,7 @@
#include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/A64/config.h>
#include "common/assert.h"
#include "common/literals.h"
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
@@ -24,6 +25,7 @@
namespace Core {
using Vector = Dynarmic::A64::Vector;
using namespace Common::Literals;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
@@ -160,6 +162,10 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
config.fastmem_pointer = page_table->fastmem_arena;
config.fastmem_address_space_bits = address_space_bits;
config.silently_mirror_fastmem = false;
}
// Multi-process state
@@ -180,8 +186,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.wall_clock_cntpct = uses_wall_clock;
// Code cache size
config.code_cache_size = 512 * 1024 * 1024;
config.far_code_offset = 256 * 1024 * 1024;
config.code_cache_size = 512_MiB;
config.far_code_offset = 400_MiB;
// Safe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
@@ -209,6 +215,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_reduce_misalign_checks) {
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
}
}
// Unsafe optimizations
@@ -223,6 +232,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_fastmem_check.GetValue()) {
config.fastmem_address_space_bits = 64;
}
}
return std::make_shared<Dynarmic::A64::Jit>(config);
+3 -2
View File
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <array>
#include <atomic>
#include <memory>
#include <utility>
@@ -377,7 +378,7 @@ struct System::Impl {
std::unique_ptr<Core::DeviceMemory> device_memory;
Core::Memory::Memory memory;
CpuManager cpu_manager;
bool is_powered_on = false;
std::atomic_bool is_powered_on{};
bool exit_lock = false;
Reporter reporter;
@@ -463,7 +464,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
}
bool System::IsPoweredOn() const {
return impl->is_powered_on;
return impl->is_powered_on.load(std::memory_order::relaxed);
}
void System::PrepareReschedule() {
+1 -1
View File
@@ -835,7 +835,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
"key_area_key_ocean_{:02X}",
"key_area_key_system_{:02X}",
};
WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
WriteKeyToFile(category, fmt::format(fmt::runtime(kak_names.at(field2)), field1), key);
} else if (id == S128KeyType::Master) {
WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
} else if (id == S128KeyType::Package1) {
+1 -1
View File
@@ -6,7 +6,7 @@
namespace Core {
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {}
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {}
DeviceMemory::~DeviceMemory() = default;
} // namespace Core
+10 -7
View File
@@ -5,7 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "common/virtual_buffer.h"
#include "common/host_memory.h"
namespace Core {
@@ -21,27 +21,30 @@ enum : u64 {
};
}; // namespace DramMemoryMap
class DeviceMemory : NonCopyable {
class DeviceMemory {
public:
explicit DeviceMemory();
~DeviceMemory();
DeviceMemory& operator=(const DeviceMemory&) = delete;
DeviceMemory(const DeviceMemory&) = delete;
template <typename T>
PAddr GetPhysicalAddr(const T* ptr) const {
return (reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(buffer.data())) +
return (reinterpret_cast<uintptr_t>(ptr) -
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
DramMemoryMap::Base;
}
u8* GetPointer(PAddr addr) {
return buffer.data() + (addr - DramMemoryMap::Base);
return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base);
}
const u8* GetPointer(PAddr addr) const {
return buffer.data() + (addr - DramMemoryMap::Base);
return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base);
}
private:
Common::VirtualBuffer<u8> buffer;
Common::HostMemory buffer;
};
} // namespace Core
+8 -5
View File
@@ -58,14 +58,17 @@ static bool FollowsNcaIdFormat(std::string_view name) {
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
bool within_two_digit, bool cnmt_suffix) {
if (!within_two_digit)
return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
Common::HexToString(nca_id, second_hex_upper));
if (!within_two_digit) {
const auto format_str = fmt::runtime(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca");
return fmt::format(format_str, Common::HexToString(nca_id, second_hex_upper));
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
Common::HexToString(nca_id, second_hex_upper));
const auto format_str =
fmt::runtime(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca");
return fmt::format(format_str, hash[0], Common::HexToString(nca_id, second_hex_upper));
}
static std::string GetCNMTName(TitleType type, u64 title_id) {
@@ -4,47 +4,29 @@
#include "core/file_sys/system_archive/system_version.h"
#include "core/file_sys/vfs_vector.h"
#include "core/hle/api_version.h"
namespace FileSys::SystemArchive {
namespace SystemVersionData {
// This section should reflect the best system version to describe yuzu's HLE api.
// TODO(DarkLordZach): Update when HLE gets better.
constexpr u8 VERSION_MAJOR = 11;
constexpr u8 VERSION_MINOR = 0;
constexpr u8 VERSION_MICRO = 1;
constexpr u8 REVISION_MAJOR = 1;
constexpr u8 REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
constexpr char DISPLAY_VERSION[] = "11.0.1";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
} // namespace SystemVersionData
std::string GetLongDisplayVersion() {
return SystemVersionData::DISPLAY_TITLE;
return HLE::ApiVersion::DISPLAY_TITLE;
}
VirtualDir SystemVersion() {
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
file->WriteObject(SystemVersionData::VERSION_MAJOR, 0);
file->WriteObject(SystemVersionData::VERSION_MINOR, 1);
file->WriteObject(SystemVersionData::VERSION_MICRO, 2);
file->WriteObject(SystemVersionData::REVISION_MAJOR, 4);
file->WriteObject(SystemVersionData::REVISION_MINOR, 5);
file->WriteArray(SystemVersionData::PLATFORM_STRING,
std::min<u64>(sizeof(SystemVersionData::PLATFORM_STRING), 0x20ULL), 0x8);
file->WriteArray(SystemVersionData::VERSION_HASH,
std::min<u64>(sizeof(SystemVersionData::VERSION_HASH), 0x40ULL), 0x28);
file->WriteArray(SystemVersionData::DISPLAY_VERSION,
std::min<u64>(sizeof(SystemVersionData::DISPLAY_VERSION), 0x18ULL), 0x68);
file->WriteArray(SystemVersionData::DISPLAY_TITLE,
std::min<u64>(sizeof(SystemVersionData::DISPLAY_TITLE), 0x80ULL), 0x80);
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MICRO, 2);
file->WriteObject(HLE::ApiVersion::SDK_REVISION_MAJOR, 4);
file->WriteObject(HLE::ApiVersion::SDK_REVISION_MINOR, 5);
file->WriteArray(HLE::ApiVersion::PLATFORM_STRING,
std::min<u64>(sizeof(HLE::ApiVersion::PLATFORM_STRING), 0x20ULL), 0x8);
file->WriteArray(HLE::ApiVersion::VERSION_HASH,
std::min<u64>(sizeof(HLE::ApiVersion::VERSION_HASH), 0x40ULL), 0x28);
file->WriteArray(HLE::ApiVersion::DISPLAY_VERSION,
std::min<u64>(sizeof(HLE::ApiVersion::DISPLAY_VERSION), 0x18ULL), 0x68);
file->WriteArray(HLE::ApiVersion::DISPLAY_TITLE,
std::min<u64>(sizeof(HLE::ApiVersion::DISPLAY_TITLE), 0x80ULL), 0x80);
return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file},
std::vector<VirtualDir>{}, "data");
}
-1
View File
@@ -6,7 +6,6 @@
#include <numeric>
#include <string>
#include "common/fs/path_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
-1
View File
@@ -14,7 +14,6 @@
#endif
#include "common/fs/path_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_libzip.h"
#include "core/file_sys/vfs_vector.h"
+1 -6
View File
@@ -24,17 +24,12 @@ constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) {
case Mode::Read:
return FS::FileAccessMode::Read;
case Mode::Write:
return FS::FileAccessMode::Write;
case Mode::ReadWrite:
return FS::FileAccessMode::ReadWrite;
case Mode::Append:
return FS::FileAccessMode::Append;
case Mode::ReadAppend:
return FS::FileAccessMode::ReadAppend;
case Mode::WriteAppend:
return FS::FileAccessMode::Append;
case Mode::All:
return FS::FileAccessMode::ReadAppend;
return FS::FileAccessMode::ReadWrite;
default:
return {};
}
+1
View File
@@ -4,6 +4,7 @@
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <tuple>
+40
View File
@@ -0,0 +1,40 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
// This file contains yuzu's HLE API version constants.
namespace HLE::ApiVersion {
// Horizon OS version constants.
constexpr u8 HOS_VERSION_MAJOR = 11;
constexpr u8 HOS_VERSION_MINOR = 0;
constexpr u8 HOS_VERSION_MICRO = 1;
// NintendoSDK version constants.
constexpr u8 SDK_REVISION_MAJOR = 1;
constexpr u8 SDK_REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
constexpr char DISPLAY_VERSION[] = "11.0.1";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
// Atmosphere version constants.
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4;
constexpr u32 GetTargetFirmware() {
return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
u32{HOS_VERSION_MICRO} << 8 | 0U;
}
} // namespace HLE::ApiVersion
@@ -4,7 +4,8 @@
#include <random>
#include "common/common_sizes.h"
#include "common/literals.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
#include "core/hle/kernel/k_trace.h"
@@ -25,6 +26,8 @@ constexpr const std::size_t RequiredNonSecureSystemMemorySize =
namespace {
using namespace Common::Literals;
u32 GetMemoryModeForInit() {
return 0x01;
}
@@ -57,11 +60,11 @@ size_t KSystemControl::Init::GetIntendedMemorySize() {
switch (GetMemorySizeForInit()) {
case Smc::MemorySize_4GB:
default: // All invalid modes should go to 4GB.
return Common::Size_4_GB;
return 4_GiB;
case Smc::MemorySize_6GB:
return Common::Size_6_GB;
return 6_GiB;
case Smc::MemorySize_8GB:
return Common::Size_8_GB;
return 8_GiB;
}
}
@@ -79,17 +82,17 @@ std::size_t KSystemControl::Init::GetApplicationPoolSize() {
switch (GetMemoryArrangeForInit()) {
case Smc::MemoryArrangement_4GB:
default:
return Common::Size_3285_MB;
return 3285_MiB;
case Smc::MemoryArrangement_4GBForAppletDev:
return Common::Size_2048_MB;
return 2048_MiB;
case Smc::MemoryArrangement_4GBForSystemDev:
return Common::Size_3285_MB;
return 3285_MiB;
case Smc::MemoryArrangement_6GB:
return Common::Size_4916_MB;
return 4916_MiB;
case Smc::MemoryArrangement_6GBForAppletDev:
return Common::Size_3285_MB;
return 3285_MiB;
case Smc::MemoryArrangement_8GB:
return Common::Size_4916_MB;
return 4916_MiB;
}
}();
@@ -103,22 +106,22 @@ size_t KSystemControl::Init::GetAppletPoolSize() {
switch (GetMemoryArrangeForInit()) {
case Smc::MemoryArrangement_4GB:
default:
return Common::Size_507_MB;
return 507_MiB;
case Smc::MemoryArrangement_4GBForAppletDev:
return Common::Size_1554_MB;
return 1554_MiB;
case Smc::MemoryArrangement_4GBForSystemDev:
return Common::Size_448_MB;
return 448_MiB;
case Smc::MemoryArrangement_6GB:
return Common::Size_562_MB;
return 562_MiB;
case Smc::MemoryArrangement_6GBForAppletDev:
return Common::Size_2193_MB;
return 2193_MiB;
case Smc::MemoryArrangement_8GB:
return Common::Size_2193_MB;
return 2193_MiB;
}
}();
// Return (possibly) adjusted size.
constexpr size_t ExtraSystemMemoryForAtmosphere = Common::Size_33_MB;
constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MiB;
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
}
+19 -16
View File
@@ -5,34 +5,37 @@
#include <array>
#include "common/assert.h"
#include "common/common_sizes.h"
#include "common/literals.h"
#include "core/hle/kernel/k_address_space_info.h"
namespace Kernel {
namespace {
using namespace Common::Literals;
constexpr u64 Size_Invalid = UINT64_MAX;
// clang-format off
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 32, .address = Common::Size_2_MB , .size = Common::Size_1_GB - Common::Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 32, .address = Common::Size_1_GB , .size = Common::Size_4_GB - Common::Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Common::Size_128_MB , .size = Common::Size_2_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 36, .address = Common::Size_2_GB , .size = Common::Size_64_GB - Common::Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Common::Size_128_MB , .size = Common::Size_512_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, },
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall },
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, },
{ .bit_width = 32, .address = 2_MiB , .size = 1_GiB - 2_MiB , .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 32, .address = 1_GiB , .size = 4_GiB - 1_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = 128_MiB , .size = 2_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::MapSmall },
{ .bit_width = 39, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_Invalid, .size = 2_GiB , .type = KAddressSpaceInfo::Type::Stack, },
}};
// clang-format on
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
return index < AddressSpaceInfos.size() &&
AddressSpaceInfos[index].address != Common::Size_Invalid;
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Size_Invalid;
}
using IndexArray =
@@ -18,41 +18,58 @@ class KernelCore;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel_)
: thread_queue(kernel_), kernel(kernel_) {}
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
void Wait(KLightLock* lock, s64 timeout = -1) {
WaitImpl(lock, timeout);
lock->Lock();
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
WaitImpl(lock, timeout, allow_terminating_thread);
}
void Broadcast() {
KScopedSchedulerLock lk{kernel};
while (thread_queue.WakeupFrontThread() != nullptr) {
// We want to signal all threads, and so should continue waking up until there's nothing
// to wake.
// Signal all threads.
for (auto& thread : wait_list) {
thread.SetState(ThreadState::Runnable);
}
}
private:
void WaitImpl(KLightLock* lock, s64 timeout) {
void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
KThread* owner = GetCurrentThreadPointer(kernel);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
lock->Unlock();
KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
if (!thread_queue.SleepThread(owner)) {
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Set the thread as waiting.
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(GetCurrentThread(kernel));
}
// Remove the thread from the wait list.
{
KScopedSchedulerLock sl{kernel};
wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
}
// Cancel the task that the sleep setup.
kernel.TimeManager().UnscheduleTimeEvent(owner);
// Re-acquire the lock.
lock->Lock();
}
KThreadQueue thread_queue;
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
} // namespace Kernel
+5 -14
View File
@@ -59,11 +59,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
owner_thread->AddWaiter(cur_thread);
// Set thread states.
if (cur_thread->GetState() == ThreadState::Runnable) {
cur_thread->SetState(ThreadState::Waiting);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
cur_thread->SetState(ThreadState::Waiting);
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
@@ -73,10 +69,9 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
// We're no longer waiting on the lock owner.
{
KScopedSchedulerLock sl{kernel};
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread) {
if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
}
@@ -95,17 +90,13 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner) {
if (next_owner != nullptr) {
next_tag = reinterpret_cast<uintptr_t>(next_owner);
if (num_waiters > 1) {
next_tag |= 0x1;
}
if (next_owner->GetState() == ThreadState::Waiting) {
next_owner->SetState(ThreadState::Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
next_owner->SetState(ThreadState::Runnable);
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/alignment.h"
#include "common/literals.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_system_control.h"
@@ -12,8 +13,10 @@ namespace Kernel {
namespace {
using namespace Common::Literals;
constexpr size_t CarveoutAlignment = 0x20000;
constexpr size_t CarveoutSizeMax = (512ULL * 1024 * 1024) - CarveoutAlignment;
constexpr size_t CarveoutSizeMax = (512_MiB) - CarveoutAlignment;
bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) {
// Above firmware 2.0.0, the PMC is not mappable.
+14 -13
View File
@@ -7,8 +7,7 @@
#include <utility>
#include "common/alignment.h"
#include "common/common_sizes.h"
#include "common/common_types.h"
#include "common/literals.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_region.h"
#include "core/hle/kernel/k_memory_region_type.h"
@@ -16,20 +15,22 @@
namespace Kernel {
constexpr std::size_t L1BlockSize = Common::Size_1_GB;
constexpr std::size_t L2BlockSize = Common::Size_2_MB;
using namespace Common::Literals;
constexpr std::size_t L1BlockSize = 1_GiB;
constexpr std::size_t L2BlockSize = 2_MiB;
constexpr std::size_t GetMaximumOverheadSize(std::size_t size) {
return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize;
}
constexpr std::size_t MainMemorySize = Common::Size_4_GB;
constexpr std::size_t MainMemorySizeMax = Common::Size_8_GB;
constexpr std::size_t MainMemorySize = 4_GiB;
constexpr std::size_t MainMemorySizeMax = 8_GiB;
constexpr std::size_t ReservedEarlyDramSize = 0x60000;
constexpr std::size_t ReservedEarlyDramSize = 384_KiB;
constexpr std::size_t DramPhysicalAddress = 0x80000000;
constexpr std::size_t KernelAslrAlignment = Common::Size_2_MB;
constexpr std::size_t KernelAslrAlignment = 2_MiB;
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
@@ -40,7 +41,7 @@ constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceE
constexpr std::size_t KernelVirtualAddressSpaceSize =
KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase;
constexpr std::size_t KernelVirtualAddressCodeSize = 0x62000;
constexpr std::size_t KernelVirtualAddressCodeSize = 392_KiB;
constexpr std::size_t KernelVirtualAddressCodeEnd =
KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize;
@@ -53,14 +54,14 @@ constexpr std::size_t KernelPhysicalAddressSpaceSize =
constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize;
constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax);
constexpr std::size_t KernelInitialPageHeapSize = Common::Size_128_KB;
constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
constexpr std::size_t KernelSlabHeapDataSize = Common::Size_5_MB;
constexpr std::size_t KernelSlabHeapGapsSize = Common::Size_2_MB - Common::Size_64_KB;
constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB;
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000ULL;
constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB;
constexpr std::size_t KernelResourceSize =
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
+4 -1
View File
@@ -4,6 +4,7 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/literals.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_address_space_info.h"
@@ -23,6 +24,8 @@ namespace Kernel {
namespace {
using namespace Common::Literals;
constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
switch (as_type) {
case FileSys::ProgramAddressSpaceType::Is32Bit:
@@ -89,7 +92,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
}
// Set code regions and determine remaining
constexpr std::size_t RegionAlignment{2 * 1024 * 1024};
constexpr std::size_t RegionAlignment{2_MiB};
VAddr process_code_start{};
VAddr process_code_end{};
std::size_t stack_region_size{};
+7 -9
View File
@@ -201,17 +201,15 @@ bool KProcess::ReleaseUserException(KThread* thread) {
// Remove waiter thread.
s32 num_waiters{};
KThread* next = thread->RemoveWaiterByKey(
std::addressof(num_waiters),
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
if (next != nullptr) {
if (next->GetState() == ThreadState::Waiting) {
next->SetState(ThreadState::Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
if (KThread* next = thread->RemoveWaiterByKey(
std::addressof(num_waiters),
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
next != nullptr) {
next->SetState(ThreadState::Runnable);
}
KScheduler::SetSchedulerUpdateNeeded(kernel);
return true;
} else {
return false;
+2 -1
View File
@@ -79,6 +79,7 @@ ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
R_UNLESS(current_values[index] <= value, ResultInvalidState);
limit_values[index] = value;
peak_values[index] = current_values[index];
return ResultSuccess;
}
@@ -117,7 +118,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
if (current_hints[index] + value <= limit_values[index] &&
(timeout < 0 || core_timing->GetGlobalTimeNs().count() < timeout)) {
waiter_count++;
cond_var.Wait(&lock, timeout);
cond_var.Wait(&lock, timeout, false);
waiter_count--;
} else {
break;
+5 -1
View File
@@ -4,9 +4,13 @@
#pragma once
#include "common/common_funcs.h"
namespace Kernel {
using namespace Common::Literals;
constexpr bool IsKTraceEnabled = false;
constexpr std::size_t KTraceBufferSize = IsKTraceEnabled ? 16 * 1024 * 1024 : 0;
constexpr std::size_t KTraceBufferSize = IsKTraceEnabled ? 16_MiB : 0;
} // namespace Kernel
+8 -9
View File
@@ -12,7 +12,6 @@
#include <utility>
#include "common/assert.h"
#include "common/common_sizes.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/thread.h"
@@ -180,7 +179,7 @@ struct KernelCore::Impl {
system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size);
// Reserve secure applet memory, introduced in firmware 5.0.0
constexpr u64 secure_applet_memory_size{Common::Size_4_MB};
constexpr u64 secure_applet_memory_size{4_MiB};
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
secure_applet_memory_size));
@@ -320,8 +319,8 @@ struct KernelCore::Impl {
const VAddr code_end_virt_addr = KernelVirtualAddressCodeEnd;
// Setup the containing kernel region.
constexpr size_t KernelRegionSize = Common::Size_1_GB;
constexpr size_t KernelRegionAlign = Common::Size_1_GB;
constexpr size_t KernelRegionSize = 1_GiB;
constexpr size_t KernelRegionAlign = 1_GiB;
constexpr VAddr kernel_region_start =
Common::AlignDown(code_start_virt_addr, KernelRegionAlign);
size_t kernel_region_size = KernelRegionSize;
@@ -368,7 +367,7 @@ struct KernelCore::Impl {
// Decide on the actual size for the misc region.
constexpr size_t MiscRegionAlign = KernelAslrAlignment;
constexpr size_t MiscRegionMinimumSize = Common::Size_32_MB;
constexpr size_t MiscRegionMinimumSize = 32_MiB;
const size_t misc_region_size = Common::AlignUp(
std::max(misc_region_needed_size, MiscRegionMinimumSize), MiscRegionAlign);
ASSERT(misc_region_size > 0);
@@ -381,7 +380,7 @@ struct KernelCore::Impl {
misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
// Setup the stack region.
constexpr size_t StackRegionSize = Common::Size_14_MB;
constexpr size_t StackRegionSize = 14_MiB;
constexpr size_t StackRegionAlign = KernelAslrAlignment;
const VAddr stack_region_start =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
@@ -414,7 +413,7 @@ struct KernelCore::Impl {
slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab));
// Setup the temp region.
constexpr size_t TempRegionSize = Common::Size_128_MB;
constexpr size_t TempRegionSize = 128_MiB;
constexpr size_t TempRegionAlign = KernelAslrAlignment;
const VAddr temp_region_start =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
@@ -470,7 +469,7 @@ struct KernelCore::Impl {
// Determine size available for kernel page table heaps, requiring > 8 MB.
const PAddr resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr;
ASSERT(page_table_heap_size / Common::Size_4_MB > 2);
ASSERT(page_table_heap_size / 4_MiB > 2);
// Insert a physical region for the kernel page table heap region
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
@@ -495,7 +494,7 @@ struct KernelCore::Impl {
ASSERT(linear_extents.GetEndAddress() != 0);
// Setup the linear mapping region.
constexpr size_t LinearRegionAlign = Common::Size_1_GB;
constexpr size_t LinearRegionAlign = 1_GiB;
const PAddr aligned_linear_phys_start =
Common::AlignDown(linear_extents.GetAddress(), LinearRegionAlign);
const size_t linear_region_size =
+9 -1
View File
@@ -58,7 +58,7 @@ public:
{7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
{12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
{13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
@@ -186,6 +186,14 @@ private:
rb.Push(static_cast<u32>(stream->GetQueueSize()));
}
void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(stream->GetPlayedSampleCount());
}
void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+2 -3
View File
@@ -19,7 +19,6 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/hex_util.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
@@ -314,7 +313,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
void(Common::FS::RemoveFile(zip_path));
Common::FS::RemoveFile(zip_path);
}
HandleDownloadDisplayResult(applet_manager, res);
@@ -446,7 +445,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
void(Common::FS::RemoveFile(bin_file_path));
Common::FS::RemoveFile(bin_file_path);
}
HandleDownloadDisplayResult(applet_manager, res);
+10 -5
View File
@@ -13,6 +13,7 @@
#include "common/common_types.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/directory.h"
@@ -785,6 +786,10 @@ FSP_SRV::FSP_SRV(Core::System& system_)
};
// clang-format on
RegisterHandlers(functions);
if (Settings::values.enable_fs_access_log) {
access_log_mode = AccessLogMode::SdCard;
}
}
FSP_SRV::~FSP_SRV() = default;
@@ -1041,9 +1046,9 @@ void FSP_SRV::DisableAutoSaveDataCreation(Kernel::HLERequestContext& ctx) {
void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
log_mode = rp.PopEnum<LogMode>();
access_log_mode = rp.PopEnum<AccessLogMode>();
LOG_DEBUG(Service_FS, "called, log_mode={:08X}", log_mode);
LOG_DEBUG(Service_FS, "called, access_log_mode={}", access_log_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1054,7 +1059,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(log_mode);
rb.PushEnum(access_log_mode);
}
void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
@@ -1062,9 +1067,9 @@ void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
auto log = Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(raw.data()), raw.size());
LOG_DEBUG(Service_FS, "called, log='{}'", log);
LOG_DEBUG(Service_FS, "called");
reporter.SaveFilesystemAccessReport(log_mode, std::move(log));
reporter.SaveFSAccessLog(log);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
+5 -7
View File
@@ -24,11 +24,10 @@ enum class AccessLogVersion : u32 {
Latest = V7_0_0,
};
enum class LogMode : u32 {
Off,
enum class AccessLogMode : u32 {
None,
Log,
RedirectToSdCard,
LogToSdCard = Log | RedirectToSdCard,
SdCard,
};
class FSP_SRV final : public ServiceFramework<FSP_SRV> {
@@ -59,13 +58,12 @@ private:
FileSystemController& fsc;
const FileSys::ContentProvider& content_provider;
const Core::Reporter& reporter;
FileSys::VirtualFile romfs;
u64 current_process_id = 0;
u32 access_log_program_index = 0;
LogMode log_mode = LogMode::LogToSdCard;
const Core::Reporter& reporter;
AccessLogMode access_log_mode = AccessLogMode::None;
};
} // namespace Service::FileSystem
@@ -314,6 +314,8 @@ void Controller_NPad::OnInit() {
void Controller_NPad::OnLoadInputDevices() {
const auto& players = Settings::values.players.GetValue();
std::lock_guard lock{mutex};
for (std::size_t i = 0; i < players.size(); ++i) {
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
@@ -348,6 +350,8 @@ void Controller_NPad::OnRelease() {
}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
std::lock_guard lock{mutex};
const auto controller_idx = NPadIdToIndex(npad_id);
const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
@@ -6,6 +6,8 @@
#include <array>
#include <atomic>
#include <mutex>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/quaternion.h"
@@ -563,6 +565,8 @@ private:
using MotionArray = std::array<
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
10>;
std::mutex mutex;
ButtonArray buttons;
StickArray sticks;
VibrationArray vibrations;
+22 -1
View File
@@ -236,7 +236,7 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
{80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
{81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
{83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
{83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -710,6 +710,27 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
.IsSixAxisSensorAtRest());
}
void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(false);
}
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
+1
View File
@@ -100,6 +100,7 @@ private:
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
+20 -27
View File
@@ -51,6 +51,24 @@ struct hash<Service::LM::LogPacketHeaderEntry> {
} // namespace std
namespace Service::LM {
namespace {
std::string_view NameOf(LogSeverity severity) {
switch (severity) {
case LogSeverity::Trace:
return "TRACE";
case LogSeverity::Info:
return "INFO";
case LogSeverity::Warning:
return "WARNING";
case LogSeverity::Error:
return "ERROR";
case LogSeverity::Fatal:
return "FATAL";
default:
return "UNKNOWN";
}
}
} // Anonymous namespace
enum class LogDestination : u32 {
TargetManager = 1 << 0,
@@ -262,33 +280,8 @@ private:
if (text_log) {
output_log += fmt::format("Log Text: {}\n", *text_log);
}
switch (entry.severity) {
case LogSeverity::Trace:
LOG_DEBUG(Service_LM, "LogManager TRACE ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Info:
LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Warning:
LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
DestinationToString(destination), output_log);
break;
case LogSeverity::Error:
LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Fatal:
LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
output_log);
break;
default:
LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
DestinationToString(destination), output_log);
break;
}
LOG_DEBUG(Service_LM, "LogManager {} ({}):\n{}", NameOf(entry.severity),
DestinationToString(destination), output_log);
}
static std::string DestinationToString(LogDestination destination) {
+2
View File
@@ -4,6 +4,8 @@
#pragma once
#include <array>
#include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/uuid.h"
@@ -307,6 +307,9 @@ void NVFlinger::Compose() {
}
s64 NVFlinger::GetNextTicks() const {
if (Settings::values.disable_fps_limit.GetValue()) {
return 0;
}
constexpr s64 max_hertz = 120LL;
return (1000000000 * (1LL << swap_interval)) / max_hertz;
}
+3 -3
View File
@@ -149,10 +149,10 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
std::string function_name = info == nullptr ? fmt::format("{}", ctx.GetCommand()) : info->name;
fmt::memory_buffer buf;
fmt::format_to(buf, "function '{}': port='{}' cmd_buf={{[0]=0x{:X}", function_name,
service_name, cmd_buf[0]);
fmt::format_to(std::back_inserter(buf), "function '{}': port='{}' cmd_buf={{[0]=0x{:X}",
function_name, service_name, cmd_buf[0]);
for (int i = 1; i <= 8; ++i) {
fmt::format_to(buf, ", [{}]=0x{:X}", i, cmd_buf[i]);
fmt::format_to(std::back_inserter(buf), ", [{}]=0x{:X}", i, cmd_buf[i]);
}
buf.push_back('}');
+4 -1
View File
@@ -41,7 +41,10 @@ class ServiceManager;
}
/// Default number of maximum connections to a server session.
static constexpr u32 ServerSessionCountMax = 0x10000;
static constexpr u32 ServerSessionCountMax = 0x40;
static_assert(ServerSessionCountMax == 0x40,
"ServerSessionCountMax isn't 0x40 somehow, this assert is a reminder that this will "
"break lots of things");
/**
* This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it
+9 -17
View File
@@ -151,27 +151,19 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
std::string name(PopServiceName(rp));
// Find the named port.
auto result = service_manager.GetServicePort(name);
if (result.Failed()) {
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.Code().raw);
return result.Code();
auto port_result = service_manager.GetServicePort(name);
if (port_result.Failed()) {
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
return port_result.Code();
}
auto* port = result.Unwrap();
// Reserve a new session from the process resource limit.
Kernel::KScopedResourceReservation session_reservation(
kernel.CurrentProcess()->GetResourceLimit(), Kernel::LimitableResource::Sessions);
R_UNLESS(session_reservation.Succeeded(), Kernel::ResultLimitReached);
auto& port = port_result.Unwrap()->GetClientPort();
// Create a new session.
Kernel::KClientSession* session{};
port->GetClientPort().CreateSession(std::addressof(session));
// Commit the session reservation.
session_reservation.Commit();
// Enqueue the session with the named port.
port->EnqueueSession(&session->GetParent()->GetServerSession());
if (const auto result = port.CreateSession(std::addressof(session)); result.IsError()) {
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
return result;
}
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Service::SPL {
CSRNG::CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "csrng") {
static const FunctionInfo functions[] = {
{0, &CSRNG::GetRandomBytes, "GetRandomBytes"},
{0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
};
RegisterHandlers(functions);
}
+123 -1
View File
@@ -10,6 +10,7 @@
#include <vector>
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/api_version.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/spl/csrng.h"
#include "core/hle/service/spl/module.h"
@@ -24,7 +25,46 @@ Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> modu
Module::Interface::~Interface() = default;
void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetConfig(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto config_item = rp.PopEnum<ConfigItem>();
// This should call svcCallSecureMonitor with the appropriate args.
// Since we do not have it implemented yet, we will use this for now.
const auto smc_result = GetConfigImpl(config_item);
const auto result_code = smc_result.Code();
if (smc_result.Failed()) {
LOG_ERROR(Service_SPL, "called, config_item={}, result_code={}", config_item,
result_code.raw);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result_code);
}
LOG_DEBUG(Service_SPL, "called, config_item={}, result_code={}, smc_result={}", config_item,
result_code.raw, *smc_result);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result_code);
rb.Push(*smc_result);
}
void Module::Interface::ModularExponentiate(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED_MSG("ModularExponentiate is not implemented!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSecureMonitorNotImplemented);
}
void Module::Interface::SetConfig(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED_MSG("SetConfig is not implemented!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSecureMonitorNotImplemented);
}
void Module::Interface::GenerateRandomBytes(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SPL, "called");
const std::size_t size = ctx.GetWriteBufferSize();
@@ -39,6 +79,88 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void Module::Interface::IsDevelopment(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED_MSG("IsDevelopment is not implemented!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSecureMonitorNotImplemented);
}
void Module::Interface::SetBootReason(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED_MSG("SetBootReason is not implemented!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSecureMonitorNotImplemented);
}
void Module::Interface::GetBootReason(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED_MSG("GetBootReason is not implemented!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSecureMonitorNotImplemented);
}
ResultVal<u64> Module::Interface::GetConfigImpl(ConfigItem config_item) const {
switch (config_item) {
case ConfigItem::DisableProgramVerification:
case ConfigItem::DramId:
case ConfigItem::SecurityEngineInterruptNumber:
case ConfigItem::FuseVersion:
case ConfigItem::HardwareType:
case ConfigItem::HardwareState:
case ConfigItem::IsRecoveryBoot:
case ConfigItem::DeviceId:
case ConfigItem::BootReason:
case ConfigItem::MemoryMode:
case ConfigItem::IsDevelopmentFunctionEnabled:
case ConfigItem::KernelConfiguration:
case ConfigItem::IsChargerHiZModeEnabled:
case ConfigItem::QuestState:
case ConfigItem::RegulatorType:
case ConfigItem::DeviceUniqueKeyGeneration:
case ConfigItem::Package2Hash:
return ResultSecureMonitorNotImplemented;
case ConfigItem::ExosphereApiVersion:
// Get information about the current exosphere version.
return MakeResult((u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) |
(u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) |
(u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) |
(static_cast<u64>(HLE::ApiVersion::GetTargetFirmware())));
case ConfigItem::ExosphereNeedsReboot:
// We are executing, so we aren't in the process of rebooting.
return MakeResult(u64{0});
case ConfigItem::ExosphereNeedsShutdown:
// We are executing, so we aren't in the process of shutting down.
return MakeResult(u64{0});
case ConfigItem::ExosphereGitCommitHash:
// Get information about the current exosphere git commit hash.
return MakeResult(u64{0});
case ConfigItem::ExosphereHasRcmBugPatch:
// Get information about whether this unit has the RCM bug patched.
return MakeResult(u64{0});
case ConfigItem::ExosphereBlankProdInfo:
// Get whether this unit should simulate a "blanked" PRODINFO.
return MakeResult(u64{0});
case ConfigItem::ExosphereAllowCalWrites:
// Get whether this unit should allow writing to the calibration partition.
return MakeResult(u64{0});
case ConfigItem::ExosphereEmummcType:
// Get what kind of emummc this unit has active.
return MakeResult(u64{0});
case ConfigItem::ExospherePayloadAddress:
// Gets the physical address of the reboot payload buffer, if one exists.
return ResultSecureMonitorNotInitialized;
case ConfigItem::ExosphereLogConfiguration:
// Get the log configuration.
return MakeResult(u64{0});
case ConfigItem::ExosphereForceEnableUsb30:
// Get whether usb 3.0 should be force-enabled.
return MakeResult(u64{0});
default:
return ResultSecureMonitorInvalidArgument;
}
}
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
auto module = std::make_shared<Module>();
std::make_shared<CSRNG>(system, module)->InstallAsService(service_manager);
+12 -1
View File
@@ -6,6 +6,8 @@
#include <random>
#include "core/hle/service/service.h"
#include "core/hle/service/spl/spl_results.h"
#include "core/hle/service/spl/spl_types.h"
namespace Core {
class System;
@@ -21,12 +23,21 @@ public:
const char* name);
~Interface() override;
void GetRandomBytes(Kernel::HLERequestContext& ctx);
// General
void GetConfig(Kernel::HLERequestContext& ctx);
void ModularExponentiate(Kernel::HLERequestContext& ctx);
void SetConfig(Kernel::HLERequestContext& ctx);
void GenerateRandomBytes(Kernel::HLERequestContext& ctx);
void IsDevelopment(Kernel::HLERequestContext& ctx);
void SetBootReason(Kernel::HLERequestContext& ctx);
void GetBootReason(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
private:
ResultVal<u64> GetConfigImpl(ConfigItem config_item) const;
std::mt19937 rng;
};
};
+42 -42
View File
@@ -10,13 +10,13 @@ SPL::SPL(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "spl:") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "ModularExponentiate"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
{11, nullptr, "IsDevelopment"},
{24, nullptr, "SetBootReason"},
{25, nullptr, "GetBootReason"},
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
{24, &SPL::SetBootReason, "SetBootReason"},
{25, &SPL::GetBootReason, "GetBootReason"},
};
// clang-format on
@@ -27,22 +27,22 @@ SPL_MIG::SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "spl:mig") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "ModularExponentiate"},
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GenerateRandomBytes"},
{11, nullptr, "IsDevelopment"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
{14, nullptr, "DecryptAesKey"},
{15, nullptr, "CryptAesCtr"},
{16, nullptr, "ComputeCmac"},
{21, nullptr, "AllocateAesKeyslot"},
{22, nullptr, "DeallocateAesKeySlot"},
{23, nullptr, "GetAesKeyslotAvailableEvent"},
{24, nullptr, "SetBootReason"},
{25, nullptr, "GetBootReason"},
{24, &SPL::SetBootReason, "SetBootReason"},
{25, &SPL::GetBootReason, "GetBootReason"},
};
// clang-format on
@@ -53,16 +53,16 @@ SPL_FS::SPL_FS(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "spl:fs") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "ModularExponentiate"},
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GenerateRandomBytes"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{9, nullptr, "ImportLotusKey"},
{10, nullptr, "DecryptLotusMessage"},
{11, nullptr, "IsDevelopment"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
{12, nullptr, "GenerateSpecificAesKey"},
{14, nullptr, "DecryptAesKey"},
{15, nullptr, "CryptAesCtr"},
@@ -71,8 +71,8 @@ SPL_FS::SPL_FS(Core::System& system_, std::shared_ptr<Module> module_)
{21, nullptr, "AllocateAesKeyslot"},
{22, nullptr, "DeallocateAesKeySlot"},
{23, nullptr, "GetAesKeyslotAvailableEvent"},
{24, nullptr, "SetBootReason"},
{25, nullptr, "GetBootReason"},
{24, &SPL::SetBootReason, "SetBootReason"},
{25, &SPL::GetBootReason, "GetBootReason"},
{31, nullptr, "GetPackage2Hash"},
};
// clang-format on
@@ -84,14 +84,14 @@ SPL_SSL::SPL_SSL(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "spl:ssl") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "ModularExponentiate"},
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
{11, nullptr, "IsDevelopment"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
{13, nullptr, "DecryptDeviceUniqueData"},
{14, nullptr, "DecryptAesKey"},
{15, nullptr, "CryptAesCtr"},
@@ -99,8 +99,8 @@ SPL_SSL::SPL_SSL(Core::System& system_, std::shared_ptr<Module> module_)
{21, nullptr, "AllocateAesKeyslot"},
{22, nullptr, "DeallocateAesKeySlot"},
{23, nullptr, "GetAesKeyslotAvailableEvent"},
{24, nullptr, "SetBootReason"},
{25, nullptr, "GetBootReason"},
{24, &SPL::SetBootReason, "SetBootReason"},
{25, &SPL::GetBootReason, "GetBootReason"},
{26, nullptr, "DecryptAndStoreSslClientCertKey"},
{27, nullptr, "ModularExponentiateWithSslClientCertKey"},
};
@@ -113,14 +113,14 @@ SPL_ES::SPL_ES(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "spl:es") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "ModularExponentiate"},
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GenerateRandomBytes"},
{11, nullptr, "IsDevelopment"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
{13, nullptr, "DecryptDeviceUniqueData"},
{14, nullptr, "DecryptAesKey"},
{15, nullptr, "CryptAesCtr"},
@@ -131,8 +131,8 @@ SPL_ES::SPL_ES(Core::System& system_, std::shared_ptr<Module> module_)
{21, nullptr, "AllocateAesKeyslot"},
{22, nullptr, "DeallocateAesKeySlot"},
{23, nullptr, "GetAesKeyslotAvailableEvent"},
{24, nullptr, "SetBootReason"},
{25, nullptr, "GetBootReason"},
{24, &SPL::SetBootReason, "SetBootReason"},
{25, &SPL::GetBootReason, "GetBootReason"},
{28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
{29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
{31, nullptr, "PrepareEsArchiveKey"},
@@ -147,14 +147,14 @@ SPL_MANU::SPL_MANU(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "spl:manu") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "ModularExponentiate"},
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
{11, nullptr, "IsDevelopment"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
{13, nullptr, "DecryptDeviceUniqueData"},
{14, nullptr, "DecryptAesKey"},
{15, nullptr, "CryptAesCtr"},
@@ -162,8 +162,8 @@ SPL_MANU::SPL_MANU(Core::System& system_, std::shared_ptr<Module> module_)
{21, nullptr, "AllocateAesKeyslot"},
{22, nullptr, "DeallocateAesKeySlot"},
{23, nullptr, "GetAesKeyslotAvailableEvent"},
{24, nullptr, "SetBootReason"},
{25, nullptr, "GetBootReason"},
{24, &SPL::SetBootReason, "SetBootReason"},
{25, &SPL::GetBootReason, "GetBootReason"},
{30, nullptr, "ReencryptDeviceUniqueData"},
};
// clang-format on
+31
View File
@@ -0,0 +1,31 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/result.h"
namespace Service::SPL {
// Description 0 - 99
constexpr ResultCode ResultSecureMonitorError{ErrorModule::SPL, 0};
constexpr ResultCode ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1};
constexpr ResultCode ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2};
constexpr ResultCode ResultSecureMonitorBusy{ErrorModule::SPL, 3};
constexpr ResultCode ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4};
constexpr ResultCode ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5};
constexpr ResultCode ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6};
constexpr ResultCode ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7};
constexpr ResultCode ResultInvalidSize{ErrorModule::SPL, 100};
constexpr ResultCode ResultUnknownSecureMonitorError{ErrorModule::SPL, 101};
constexpr ResultCode ResultDecryptionFailed{ErrorModule::SPL, 102};
constexpr ResultCode ResultOutOfKeySlots{ErrorModule::SPL, 104};
constexpr ResultCode ResultInvalidKeySlot{ErrorModule::SPL, 105};
constexpr ResultCode ResultBootReasonAlreadySet{ErrorModule::SPL, 106};
constexpr ResultCode ResultBootReasonNotSet{ErrorModule::SPL, 107};
constexpr ResultCode ResultInvalidArgument{ErrorModule::SPL, 108};
} // namespace Service::SPL
+232
View File
@@ -0,0 +1,232 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <span>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Service::SPL {
constexpr size_t AES_128_KEY_SIZE = 0x10;
namespace Smc {
enum class FunctionId : u32 {
SetConfig = 0xC3000401,
GetConfig = 0xC3000002,
GetResult = 0xC3000003,
GetResultData = 0xC3000404,
ModularExponentiate = 0xC3000E05,
GenerateRandomBytes = 0xC3000006,
GenerateAesKek = 0xC3000007,
LoadAesKey = 0xC3000008,
ComputeAes = 0xC3000009,
GenerateSpecificAesKey = 0xC300000A,
ComputeCmac = 0xC300040B,
ReencryptDeviceUniqueData = 0xC300D60C,
DecryptDeviceUniqueData = 0xC300100D,
ModularExponentiateWithStorageKey = 0xC300060F,
PrepareEsDeviceUniqueKey = 0xC3000610,
LoadPreparedAesKey = 0xC3000011,
PrepareCommonEsTitleKey = 0xC3000012,
// Deprecated functions.
LoadEsDeviceKey = 0xC300100C,
DecryptAndStoreGcKey = 0xC300100E,
// Atmosphere functions.
AtmosphereIramCopy = 0xF0000201,
AtmosphereReadWriteRegister = 0xF0000002,
AtmosphereGetEmummcConfig = 0xF0000404,
};
enum class CipherMode {
CbcEncrypt = 0,
CbcDecrypt = 1,
Ctr = 2,
};
enum class DeviceUniqueDataMode {
DecryptDeviceUniqueData = 0,
DecryptAndStoreGcKey = 1,
DecryptAndStoreEsDeviceKey = 2,
DecryptAndStoreSslKey = 3,
DecryptAndStoreDrmDeviceCertKey = 4,
};
enum class ModularExponentiateWithStorageKeyMode {
Gc = 0,
Ssl = 1,
DrmDeviceCert = 2,
};
enum class EsCommonKeyType {
TitleKey = 0,
ArchiveKey = 1,
};
struct AsyncOperationKey {
u64 value;
};
} // namespace Smc
enum class HardwareType {
Icosa = 0,
Copper = 1,
Hoag = 2,
Iowa = 3,
Calcio = 4,
Aula = 5,
};
enum class SocType {
Erista = 0,
Mariko = 1,
};
enum class HardwareState {
Development = 0,
Production = 1,
};
enum class MemoryArrangement {
Standard = 0,
StandardForAppletDev = 1,
StandardForSystemDev = 2,
Expanded = 3,
ExpandedForAppletDev = 4,
// Note: Dynamic is not official.
// Atmosphere uses it to maintain compatibility with firmwares prior to 6.0.0,
// which removed the explicit retrieval of memory arrangement from PM.
Dynamic = 5,
Count,
};
enum class BootReason {
Unknown = 0,
AcOk = 1,
OnKey = 2,
RtcAlarm1 = 3,
RtcAlarm2 = 4,
};
struct BootReasonValue {
union {
u32 value{};
BitField<0, 8, u32> power_intr;
BitField<8, 8, u32> rtc_intr;
BitField<16, 8, u32> nv_erc;
BitField<24, 8, u32> boot_reason;
};
};
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");
struct AesKey {
std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
std::span<u8> AsBytes() {
return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
}
std::span<const u8> AsBytes() const {
return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
}
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "AesKey definition!");
struct IvCtr {
std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
std::span<u8> AsBytes() {
return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
}
std::span<const u8> AsBytes() const {
return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
}
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "IvCtr definition!");
struct Cmac {
std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
std::span<u8> AsBytes() {
return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
}
std::span<const u8> AsBytes() const {
return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
}
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "Cmac definition!");
struct AccessKey {
std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
std::span<u8> AsBytes() {
return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
}
std::span<const u8> AsBytes() const {
return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
}
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "AccessKey definition!");
struct KeySource {
std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};
std::span<u8> AsBytes() {
return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
}
std::span<const u8> AsBytes() const {
return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
}
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "KeySource definition!");
enum class ConfigItem : u32 {
// Standard config items.
DisableProgramVerification = 1,
DramId = 2,
SecurityEngineInterruptNumber = 3,
FuseVersion = 4,
HardwareType = 5,
HardwareState = 6,
IsRecoveryBoot = 7,
DeviceId = 8,
BootReason = 9,
MemoryMode = 10,
IsDevelopmentFunctionEnabled = 11,
KernelConfiguration = 12,
IsChargerHiZModeEnabled = 13,
QuestState = 14,
RegulatorType = 15,
DeviceUniqueKeyGeneration = 16,
Package2Hash = 17,
// Extension config items for exosphere.
ExosphereApiVersion = 65000,
ExosphereNeedsReboot = 65001,
ExosphereNeedsShutdown = 65002,
ExosphereGitCommitHash = 65003,
ExosphereHasRcmBugPatch = 65004,
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
};
} // namespace Service::SPL
@@ -125,7 +125,7 @@ ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& locati
return ERROR_TIME_NOT_FOUND;
}
vfs_file = zoneinfo_dir->GetFile(location_name);
vfs_file = zoneinfo_dir->GetFileRelative(location_name);
if (!vfs_file) {
LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
time_zone_binary_titleid, location_name);
+18
View File
@@ -12,6 +12,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
@@ -32,6 +33,7 @@ struct Memory::Impl {
void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
current_page_table = &process.PageTable().PageTableImpl();
current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth();
@@ -41,13 +43,23 @@ struct Memory::Impl {
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End,
"Out of bounds target: {:016X}", target);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size);
}
}
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Unmap(base, size);
}
}
bool IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const {
@@ -466,6 +478,12 @@ struct Memory::Impl {
if (vaddr == 0) {
return;
}
if (Settings::IsFastmemEnabled()) {
const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
}
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU
// address space, marking the region as un/cached. The region is marked un/cached at a
// granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
+20 -16
View File
@@ -195,7 +195,9 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Core::Memory::Memo
namespace Core {
Reporter::Reporter(System& system_) : system(system_) {}
Reporter::Reporter(System& system_) : system(system_) {
ClearFSAccessLog();
}
Reporter::~Reporter() = default;
@@ -362,22 +364,12 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
}
void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
std::string log_message) const {
if (!IsReportingEnabled())
return;
void Reporter::SaveFSAccessLog(std::string_view log_message) const {
const auto access_log_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "FsAccessLog.txt";
const auto timestamp = GetTimestamp();
const auto title_id = system.CurrentProcess()->GetTitleID();
json out;
out["yuzu_version"] = GetYuzuVersionData();
out["report_common"] = GetReportCommonData(title_id, ResultSuccess, timestamp);
out["log_mode"] = fmt::format("{:08X}", static_cast<u32>(log_mode));
out["log_message"] = std::move(log_message);
SaveToFile(std::move(out), GetPath("filesystem_access_report", title_id, timestamp));
void(Common::FS::AppendStringToFile(access_log_path, Common::FS::FileType::TextFile,
log_message));
}
void Reporter::SaveUserReport() const {
@@ -392,6 +384,18 @@ void Reporter::SaveUserReport() const {
GetPath("user_report", title_id, timestamp));
}
void Reporter::ClearFSAccessLog() const {
const auto access_log_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "FsAccessLog.txt";
Common::FS::IOFile access_log_file{access_log_path, Common::FS::FileAccessMode::Write,
Common::FS::FileType::TextFile};
if (!access_log_file.IsOpen()) {
LOG_ERROR(Common_Filesystem, "Failed to clear the filesystem access log.");
}
}
bool Reporter::IsReportingEnabled() const {
return Settings::values.reporting_services;
}
+3 -6
View File
@@ -16,10 +16,6 @@ namespace Kernel {
class HLERequestContext;
} // namespace Kernel
namespace Service::FileSystem {
enum class LogMode : u32;
}
namespace Service::LM {
struct LogMessage;
} // namespace Service::LM
@@ -69,14 +65,15 @@ public:
std::optional<std::string> custom_text_main = {},
std::optional<std::string> custom_text_detail = {}) const;
void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
std::string log_message) const;
void SaveFSAccessLog(std::string_view log_message) const;
// Can be used anywhere to generate a backtrace and general info report at any point during
// execution. Not intended to be used for anything other than debugging or testing.
void SaveUserReport() const;
private:
void ClearFSAccessLog() const;
bool IsReportingEnabled() const;
System& system;
+1
View File
@@ -230,6 +230,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
Settings::values.use_asynchronous_gpu_emulation.GetValue());
AddField(field_type, "Renderer_UseNvdecEmulation",
Settings::values.use_nvdec_emulation.GetValue());
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseAssemblyShaders",
Settings::values.use_assembly_shaders.GetValue());
+1 -2
View File
@@ -71,8 +71,7 @@ if (ENABLE_SDL2)
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
endif()
target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
target_link_libraries(input_common PRIVATE usb)
create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
+7 -9
View File
@@ -2,25 +2,23 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <stop_token>
#include <thread>
#include "common/settings.h"
#include "input_common/mouse/mouse_input.h"
namespace MouseInput {
Mouse::Mouse() {
update_thread = std::thread(&Mouse::UpdateThread, this);
update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
}
Mouse::~Mouse() {
update_thread_running = false;
if (update_thread.joinable()) {
update_thread.join();
}
}
Mouse::~Mouse() = default;
void Mouse::UpdateThread() {
void Mouse::UpdateThread(std::stop_token stop_token) {
constexpr int update_time = 10;
while (update_thread_running) {
while (!stop_token.stop_requested()) {
for (MouseInfo& info : mouse_info) {
const Common::Vec3f angular_direction{
-info.tilt_direction.y,
+3 -3
View File
@@ -6,6 +6,7 @@
#include <array>
#include <mutex>
#include <stop_token>
#include <thread>
#include "common/common_types.h"
@@ -85,7 +86,7 @@ public:
[[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
private:
void UpdateThread();
void UpdateThread(std::stop_token stop_token);
void UpdateYuzuSettings();
void StopPanning();
@@ -105,12 +106,11 @@ private:
u16 buttons{};
u16 toggle_buttons{};
u16 lock_buttons{};
std::thread update_thread;
std::jthread update_thread;
MouseButton last_button{MouseButton::Undefined};
std::array<MouseInfo, 7> mouse_info;
Common::SPSCQueue<MouseStatus> mouse_queue;
bool configuring{false};
bool update_thread_running{true};
int mouse_panning_timout{};
};
} // namespace MouseInput
+2
View File
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include <mutex>
#include <utility>
+1
View File
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/settings.h"
#include "core/frontend/framebuffer_layout.h"
#include "input_common/touch_from_button.h"
+1
View File
@@ -2,6 +2,7 @@ add_executable(tests
common/bit_field.cpp
common/cityhash.cpp
common/fibers.cpp
common/host_memory.cpp
common/param_package.cpp
common/ring_buffer.cpp
core/core_timing.cpp
+185
View File
@@ -0,0 +1,185 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <catch2/catch.hpp>
#include "common/host_memory.h"
#include "common/literals.h"
using Common::HostMemory;
using namespace Common::Literals;
static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
static constexpr size_t BACKING_SIZE = 4_GiB;
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
}
TEST_CASE("HostMemory: Simple map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x5000, 0x8000, 0x1000);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
data[0] = 50;
REQUIRE(data[0] == 50);
}
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x5000, 0x3000, 0x2000);
mem.Map(0x8000, 0x4000, 0x1000);
volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
mirror_b[0] = 76;
REQUIRE(mirror_a[0x1000] == 76);
}
TEST_CASE("HostMemory: Simple unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x5000, 0x3000, 0x2000);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
data[75] = 50;
REQUIRE(data[75] == 50);
mem.Unmap(0x5000, 0x2000);
}
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x5000, 0x3000, 0x2000);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
data[0] = 50;
REQUIRE(data[0] == 50);
mem.Unmap(0x5000, 0x2000);
mem.Map(0x5000, 0x3000, 0x2000);
REQUIRE(data[0] == 50);
mem.Map(0x7000, 0x2000, 0x5000);
REQUIRE(data[0x3000] == 50);
}
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x0000, 0, 0x20000);
mem.Unmap(0x0000, 0x4000);
mem.Map(0x1000, 0, 0x2000);
mem.Map(0x3000, 0, 0x1000);
mem.Map(0, 0, 0x1000);
}
TEST_CASE("HostMemory: Full unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x8000, 0, 0x4000);
mem.Unmap(0x8000, 0x4000);
mem.Map(0x6000, 0, 0x16000);
}
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x0000, 0, 0x4000);
mem.Unmap(0x2000, 0x4000);
mem.Map(0x2000, 0x80000, 0x4000);
}
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x8000, 0, 0x4000);
mem.Unmap(0x6000, 0x4000);
mem.Map(0x8000, 0, 0x2000);
}
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x0000, 0, 0x4000);
mem.Map(0x4000, 0, 0x1b000);
mem.Unmap(0x3000, 0x1c000);
mem.Map(0x3000, 0, 0x20000);
}
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x0000, 0, 0x4000);
mem.Map(0x4000, 0, 0x4000);
mem.Unmap(0x2000, 0x4000);
mem.Map(0x2000, 0, 0x4000);
}
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x4000, 0, 0x4000);
mem.Map(0x8000, 0, 0x4000);
mem.Unmap(0x4000, 0x4000);
mem.Map(0, 0, 0x4000);
mem.Map(0x4000, 0, 0x4000);
}
TEST_CASE("HostMemory: Unmap to right", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x4000, 0, 0x4000);
mem.Map(0x8000, 0, 0x4000);
mem.Unmap(0x8000, 0x4000);
mem.Map(0x8000, 0, 0x4000);
}
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x4000, 0x10000, 0x4000);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x1000] = 17;
mem.Unmap(0x6000, 0x2000);
REQUIRE(ptr[0x1000] == 17);
}
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x4000, 0x10000, 0x4000);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x3000] = 19;
ptr[0x3fff] = 12;
mem.Unmap(0x4000, 0x2000);
REQUIRE(ptr[0x3000] == 19);
REQUIRE(ptr[0x3fff] == 12);
}
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x4000, 0x10000, 0x4000);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x0000] = 19;
ptr[0x3fff] = 12;
mem.Unmap(0x1000, 0x2000);
REQUIRE(ptr[0x0000] == 19);
REQUIRE(ptr[0x3fff] == 12);
}
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x4000, 0x10000, 0x2000);
mem.Map(0x6000, 0x20000, 0x2000);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x0000] = 19;
ptr[0x3fff] = 12;
mem.Unmap(0x5000, 0x2000);
REQUIRE(ptr[0x0000] == 19);
REQUIRE(ptr[0x3fff] == 12);
}
+1
View File
@@ -237,6 +237,7 @@ add_library(video_core STATIC
texture_cache/util.cpp
texture_cache/util.h
textures/astc.h
textures/astc.cpp
textures/decoders.cpp
textures/decoders.h
textures/texture.cpp
+11
View File
@@ -256,6 +256,16 @@ public:
stream_score += score;
}
/// Sets the new frame tick
void SetFrameTick(u64 new_frame_tick) noexcept {
frame_tick = new_frame_tick;
}
/// Returns the new frame tick
[[nodiscard]] u64 FrameTick() const noexcept {
return frame_tick;
}
/// Returns the likeliness of this being a stream buffer
[[nodiscard]] int StreamScore() const noexcept {
return stream_score;
@@ -586,6 +596,7 @@ private:
RasterizerInterface* rasterizer = nullptr;
VAddr cpu_addr = 0;
Words words;
u64 frame_tick = 0;
BufferFlagBits flags{};
int stream_score = 0;
};
+120 -46
View File
@@ -18,6 +18,7 @@
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/literals.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
@@ -47,8 +48,11 @@ constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
constexpr u32 NUM_STORAGE_BUFFERS = 16;
constexpr u32 NUM_STAGES = 5;
using namespace Common::Literals;
template <typename P>
class BufferCache {
// Page size for caching purposes.
// This is unrelated to the CPU page size and it can be changed as it seems optimal.
static constexpr u32 PAGE_BITS = 16;
@@ -65,6 +69,9 @@ class BufferCache {
static constexpr BufferId NULL_BUFFER_ID{0};
static constexpr u64 EXPECTED_MEMORY = 512_MiB;
static constexpr u64 CRITICAL_MEMORY = 1_GiB;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using Runtime = typename P::Runtime;
@@ -92,7 +99,7 @@ class BufferCache {
};
public:
static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096;
static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4_KiB;
explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
Tegra::Engines::Maxwell3D& maxwell3d_,
@@ -102,6 +109,8 @@ public:
void TickFrame();
void RunGarbageCollector();
void WriteMemory(VAddr cpu_addr, u64 size);
void CachedWriteMemory(VAddr cpu_addr, u64 size);
@@ -243,6 +252,8 @@ private:
template <bool insert>
void ChangeRegister(BufferId buffer_id);
void TouchBuffer(Buffer& buffer) const noexcept;
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
@@ -255,6 +266,10 @@ private:
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
void DownloadBufferMemory(Buffer& buffer_id);
void DownloadBufferMemory(Buffer& buffer_id, VAddr cpu_addr, u64 size);
void DeleteBuffer(BufferId buffer_id);
void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
@@ -319,6 +334,10 @@ private:
size_t immediate_buffer_capacity = 0;
std::unique_ptr<u8[]> immediate_buffer_alloc;
typename SlotVector<Buffer>::Iterator deletion_iterator;
u64 frame_tick = 0;
u64 total_used_memory = 0;
std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table;
};
@@ -332,6 +351,28 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_}, runtime{runtime_} {
// Ensure the first slot is used for the null buffer
void(slot_buffers.insert(runtime, NullBufferParams{}));
deletion_iterator = slot_buffers.end();
}
template <class P>
void BufferCache<P>::RunGarbageCollector() {
const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY;
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32;
for (; num_iterations > 0; --num_iterations) {
if (deletion_iterator == slot_buffers.end()) {
deletion_iterator = slot_buffers.begin();
}
++deletion_iterator;
if (deletion_iterator == slot_buffers.end()) {
break;
}
const auto [buffer_id, buffer] = *deletion_iterator;
if (buffer->FrameTick() + ticks_to_destroy < frame_tick) {
DownloadBufferMemory(*buffer);
DeleteBuffer(buffer_id);
}
}
}
template <class P>
@@ -349,6 +390,10 @@ void BufferCache<P>::TickFrame() {
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
if (Settings::values.use_caches_gc.GetValue() && total_used_memory >= EXPECTED_MEMORY) {
RunGarbageCollector();
}
++frame_tick;
delayed_destruction_ring.Tick();
}
@@ -371,50 +416,8 @@ void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
template <class P>
void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) {
boost::container::small_vector<BufferCopy, 1> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
copies.push_back(BufferCopy{
.src_offset = range_offset,
.dst_offset = total_size_bytes,
.size = range_size,
});
total_size_bytes += range_size;
largest_copy = std::max(largest_copy, range_size);
});
if (total_size_bytes == 0) {
return;
}
MICROPROFILE_SCOPE(GPU_DownloadMemory);
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
const u8* const mapped_memory = download_staging.mapped_span.data();
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
for (BufferCopy& copy : copies) {
// Modify copies to have the staging offset in mind
copy.dst_offset += download_staging.offset;
}
runtime.CopyBuffer(download_staging.buffer, buffer, copies_span);
runtime.Finish();
for (const BufferCopy& copy : copies) {
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* copy_mapped_memory = mapped_memory + dst_offset;
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, copy_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const BufferCopy& copy : copies) {
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, immediate_buffer.data(), copy.size);
}
}
});
ForEachBufferInRange(cpu_addr, size,
[&](BufferId, Buffer& buffer) { DownloadBufferMemory(buffer); });
}
template <class P>
@@ -640,6 +643,7 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
template <class P>
void BufferCache<P>::BindHostIndexBuffer() {
Buffer& buffer = slot_buffers[index_buffer.buffer_id];
TouchBuffer(buffer);
const u32 offset = buffer.Offset(index_buffer.cpu_addr);
const u32 size = index_buffer.size;
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
@@ -658,6 +662,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
const Binding& binding = vertex_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer);
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
if (!flags[Dirty::VertexBuffer0 + index]) {
continue;
@@ -693,6 +698,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
const VAddr cpu_addr = binding.cpu_addr;
const u32 size = binding.size;
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer);
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
size <= uniform_buffer_skip_cache_size &&
!buffer.IsRegionGpuModified(cpu_addr, size);
@@ -744,6 +750,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) {
const Binding& binding = storage_buffers[stage][index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -766,6 +773,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
const Binding& binding = transform_feedback_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -784,6 +792,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
ForEachEnabledBit(enabled_compute_uniform_buffers, [&](u32 index) {
const Binding& binding = compute_uniform_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -803,6 +812,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) {
const Binding& binding = compute_storage_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -1101,6 +1111,7 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
TouchBuffer(slot_buffers[new_buffer_id]);
for (const BufferId overlap_id : overlap.ids) {
JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
}
@@ -1122,8 +1133,14 @@ template <class P>
template <bool insert>
void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
const Buffer& buffer = slot_buffers[buffer_id];
const auto size = buffer.SizeBytes();
if (insert) {
total_used_memory += Common::AlignUp(size, 1024);
} else {
total_used_memory -= Common::AlignUp(size, 1024);
}
const VAddr cpu_addr_begin = buffer.CpuAddr();
const VAddr cpu_addr_end = cpu_addr_begin + buffer.SizeBytes();
const VAddr cpu_addr_end = cpu_addr_begin + size;
const u64 page_begin = cpu_addr_begin / PAGE_SIZE;
const u64 page_end = Common::DivCeil(cpu_addr_end, PAGE_SIZE);
for (u64 page = page_begin; page != page_end; ++page) {
@@ -1135,6 +1152,11 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
}
}
template <class P>
void BufferCache<P>::TouchBuffer(Buffer& buffer) const noexcept {
buffer.SetFrameTick(frame_tick);
}
template <class P>
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
if (buffer.CpuAddr() == 0) {
@@ -1211,6 +1233,57 @@ void BufferCache<P>::MappedUploadMemory(Buffer& buffer, u64 total_size_bytes,
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
}
template <class P>
void BufferCache<P>::DownloadBufferMemory(Buffer& buffer) {
DownloadBufferMemory(buffer, buffer.CpuAddr(), buffer.SizeBytes());
}
template <class P>
void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 size) {
boost::container::small_vector<BufferCopy, 1> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
copies.push_back(BufferCopy{
.src_offset = range_offset,
.dst_offset = total_size_bytes,
.size = range_size,
});
total_size_bytes += range_size;
largest_copy = std::max(largest_copy, range_size);
});
if (total_size_bytes == 0) {
return;
}
MICROPROFILE_SCOPE(GPU_DownloadMemory);
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
const u8* const mapped_memory = download_staging.mapped_span.data();
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
for (BufferCopy& copy : copies) {
// Modify copies to have the staging offset in mind
copy.dst_offset += download_staging.offset;
}
runtime.CopyBuffer(download_staging.buffer, buffer, copies_span);
runtime.Finish();
for (const BufferCopy& copy : copies) {
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* copy_mapped_memory = mapped_memory + dst_offset;
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, copy_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const BufferCopy& copy : copies) {
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, immediate_buffer.data(), copy.size);
}
}
}
template <class P>
void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
const auto scalar_replace = [buffer_id](Binding& binding) {
@@ -1236,6 +1309,7 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
Unregister(buffer_id);
delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
slot_buffers.erase(buffer_id);
NotifyBufferDeletion();
}

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